view src/kaigo/Perori/perori.go @ 50:638e1ad05cae

small changes.
author pyon@macmini
date Fri, 24 Apr 2020 22:22:12 +0900
parents b50f2a581cf8
children
line wrap: on
line source

package main

import (
	"flag"
	"compress/gzip"
	"encoding/csv"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sort"
	"time"

	"golang.org/x/text/encoding/japanese"
	"golang.org/x/text/transform"
)

var debug_log bool

// Constants
const version =  "0.3b"
const default_dbfile =  "ikenshoirai.db"
const default_csvfile = "ikenshoirai.csv"

const tpl = `
<!DOCTYPE html> <html>
	<head> 
	<style type="text/css">
		body { font-size: 9pt; margin-left: 0px;}
		h2 { font-size: 11pt; margin-bottom: 1px; background-color: #ccccff; padding-left: 5px; }
		h3 { font-size: 11pt; margin-bottom: 1px; background-color: #f0a8a8; padding-left: 10px; }
		table, th, td { border: 0.3px #c0c0c0 solid; border-collapse:collapse; }
		table { margin-bottom: 5px; margin-left: 15px; }
		th { background-color: #ccffcc; }
		hr { page-break-before: always; }
	</style>
	<title> - </title>
	</head>
	<body>

	<h2> List <small>( Date = {{.Ymd}} / N = {{.NHhs}} / Dr = {{.NDr}} )</small> </h2>
	{{range .Doctors}}
		<h3>{{.Name}}<small> ..... {{.Hp}}:{{.Senmon}}</small></h3>
		{{range .Clients}}
		<table>
			<tr>
				<td width=140 style="background-color: #98f0f0; padding-left: 10px;">{{.Name}}</td>
				<td width=120 align=center>{{.Kubun}} {{.Ymd}}</td>
				<td width=480 style="padding-left: 10px;">

				{{if .Prev.Ymd}}
					{{if eq .DrId .Prev.DrId}}
						{{str2cp932 "★ 継続 -"}} {{.Prev.Ymd}}
					{{else}}
						{{.Prev.Dr}} {{.Prev.Ymd}}
					{{end}}
				{{else}}
					New !
				{{end}}

				</td>
				<td width=80 align=center>{{.Hhsno}}</td>
			</tr>
			<tr>
				<td colspan=4 style="font-family: serif; font-size: 8pt; padding-left: 30px;">{{.Biko}}</td>
			</tr>

		</table>
		{{end}}
	{{end}}

	<hr />
	{{$hpno := 0}}
	<h2> N by Hp </h2>
	<table> 
	<tr> <th> no </th> <th> hp </th> <th width=60> n </th> </tr>
	{{range $hp, $n := .Hp}}
		<tr>
			{{$hpno = add1 $hpno}}
			<td align=right style="padding-right: 5px;"> {{$hpno}} </td>
			<td style="padding-left: 5px;"> {{$hp}} </td> 
		    <td align=right style="padding-right: 5px;"> {{$n}} </td> 
		 </tr>
	{{end}}
	<tr> <td></td> <td align=right> sum &gt &gt &gt</td> <td align=right style="padding-right: 5px;"> <b> {{.HpSum}} </b> </td> </tr>
	</table>
	</body>
</html>`


// Define Types
type PrevSinsei struct {
	Biko string
	DrId string
	Dr string
	IraiYmd string
	Ymd string
	Kubun string
}

type Sinsei struct {
	Hhsno string
	Name string
	Biko string
	DrId string
	Dr string
	DrKana string
	Hp string
	IraiYmd string
	Ymd string
	Kubun string
	Senmon string
	Prev PrevSinsei
}

func (s *Sinsei) SetPrev(prev PrevSinsei) {
	s.Prev = prev
}

func (s Sinsei) String() string {
	return strings.Join([]string{s.Hhsno, s.Name, s.Ymd, s.Kubun, s.Dr, s.Hp, s.Senmon, s.IraiYmd, s.Biko}, ",")
}

func (s *Sinsei) Humanize() {
	var buf string

	switch s.Kubun {
	case "01":
		buf = "新規"
	case "02":
		buf = "更新"
	case "10":
		buf = "支介"
	case "05":
		buf = "区変"
	case "03":
		buf = "転入"
	case "09":
		buf = "証交"
	}
	s.Kubun, _, _ = transform.String(japanese.ShiftJIS.NewEncoder(), buf)

	s.Ymd = strings.Join([]string{s.Ymd[2:4], s.Ymd[4:6], s.Ymd[6:8]}, ".")
}

type Doctor struct {
	Id string
	Name string
	Kana string
	Hp	string
	Senmon string
	Clients []Sinsei
}

func (d *Doctor) AddClient(sinsei Sinsei) {
	d.Clients = append(d.Clients, sinsei)
}

func (d Doctor) String() string {
	return d.Name
}

// Main
func main() {
	var csvfile, dbfile, date string

	today := time.Now().Format("20060102")

	flag.StringVar(&csvfile, "c", default_csvfile, "csv file")
	flag.StringVar(&dbfile, "b", default_dbfile, "db file")
	flag.StringVar(&date, "r", today, "Ikensho Irai YMD")
	flag.BoolVar(&debug_log, "d", false, "print debug-log (stderr)")
	flag.Parse()

	csvdata, hhshash, err := getdata_fromCSV(csvfile, date)
	if err != nil {
		log.Fatal(err)
	}
	print_debug_log(fmt.Sprintf("csvdata: n=%d", len(csvdata)))	//
	print_debug_log(fmt.Sprintf("hhshash: n=%d", len(hhshash)))	//

	dbdata, err := getdata_fromDB(dbfile, hhshash)
	if err != nil {
		log.Fatal(err)
	}
	print_debug_log(fmt.Sprintf("dbdata: n=%d", len(dbdata)))	//

	dbdata = append(dbdata, csvdata...)
	print_debug_log(fmt.Sprintf("dbdata: n=%d", len(dbdata)))	//

	sort.Slice(dbdata, func(i, j int) bool {
		if dbdata[i].Hhsno != dbdata[j].Hhsno {
			return dbdata[i].Hhsno < dbdata[j].Hhsno
		}
		if dbdata[i].Ymd != dbdata[j].Ymd {
			return dbdata[i].Ymd > dbdata[j].Ymd
		}
		if dbdata[i].IraiYmd != dbdata[j].IraiYmd {
			return dbdata[i].IraiYmd > dbdata[j].IraiYmd
		}
		return false
	})

	var dbdata2 []Sinsei	// delete same Ymd (for changing Dr.)
	var lasthhsno, lastymd string
	for _, ss := range dbdata {
		if ss.Hhsno == lasthhsno && lastymd == ss.Ymd {
			continue
		}
		dbdata2 = append(dbdata2, ss)
		lasthhsno = ss.Hhsno
		lastymd = ss.Ymd
	}

	var lastdata []Sinsei
	prevhash := make(map[string]PrevSinsei)
	hhscnt := make(map[string]int)
	for _, ss := range dbdata2 {
		ss.Humanize()
		switch hhscnt[ss.Hhsno] {
		case 0:
			lastdata = append(lastdata, ss)
		case 1:
			prevhash[ss.Hhsno] = PrevSinsei{
				Biko: ss.Biko,
				DrId: ss.DrId,
				Dr: ss.Dr + "(" + ss.Hp + ":" + ss.Senmon + ")",
				IraiYmd: ss.IraiYmd,
				Ymd: ss.Ymd,
				Kubun: ss.Kubun,
			}
		}
		hhscnt[ss.Hhsno]++;
	}
	print_debug_log(fmt.Sprintf("lastdata: n=%d", len(lastdata)))	//

	doctorhash := make(map[string]Doctor)
	hpcnt := make(map[string]int)
	var hpcntsum int
	for _, ss := range lastdata {
		ss.SetPrev(prevhash[ss.Hhsno])
		if d, ok := doctorhash[ss.DrId]; !ok {
			doctorhash[ss.DrId] = Doctor{
				Id: ss.DrId,
				Name: ss.Dr,
				Kana: ss.DrKana,
				Hp: ss.Hp,
				Senmon: ss.Senmon,
				Clients: []Sinsei{ss},
			}
		} else {
			d.AddClient(ss)
			doctorhash[ss.DrId] = d
		}
		hpcnt[ss.Hp]++
		hpcntsum++
	}

	var doctors []Doctor
	for _, dr := range doctorhash {
		doctors = append(doctors, dr)
	}
	sort.Slice(doctors, func(i, j int) bool {
		if doctors[i].Kana != doctors[j].Kana {
			return doctors[i].Kana < doctors[j].Kana
		}
		if doctors[i].Id != doctors[j].Id {
			return doctors[i].Id < doctors[j].Id
		}
		return false
	})

	irai := struct {
		Ymd string
		NHhs int
		//NSinsei int
		NDr int
		Doctors []Doctor
		Hp map[string]int
		HpSum int
	}{
		Ymd: strings.Join([]string{date[0:4], date[4:6], date[6:8]}, "."),
		NHhs: len(hhshash),
		//NSinsei: len(dbdata),
		NDr: len(doctors),
		Doctors: doctors,
		Hp: hpcnt,
		HpSum: hpcntsum,
	}

	funcmap := template.FuncMap{
		"shorten": shorten,
		"str2cp932": str2cp932,
		"add1": func(a int) int { return a + 1 },
	}

	t, err := template.New("webpage").Funcs(funcmap).Parse(tpl)
	if err != nil {
		log.Fatal(err)
	}

	err = t.Execute(os.Stdout, irai)
	if err != nil {
		log.Fatal(err)
	}
}

// Utility functions
func csv2sinsei(record []string) Sinsei {
	return Sinsei{
		Hhsno:   strings.TrimSpace(record[0]),
		Name:    strings.TrimSpace(record[1]),
		Biko:    strings.TrimSpace(record[2]),
		DrId:    strings.TrimSpace(record[3]),
		Dr:      strings.TrimSpace(record[4]),
		DrKana:  strings.TrimSpace(record[5]),
		Hp:      strings.TrimSpace(record[6]),
		IraiYmd: strings.TrimSpace(record[7]),
		Ymd:     strings.TrimSpace(record[8]),
		Kubun:   strings.TrimSpace(record[9]),
		Senmon:  strings.TrimSpace(record[10]),
	}
}

func getdata_fromCSV(file, date string) (sinsei []Sinsei, hhshash map[string]bool, err error) {
	hhshash = make(map[string]bool)

	data, err := ioutil.ReadFile(file)
	if err != nil {
		return sinsei, hhshash, err
	}

	r := csv.NewReader(strings.NewReader(string(data)))
	records, err := r.ReadAll()
	if err != nil {
		return sinsei, hhshash, err
	}

	for _, record := range records {
		ss := csv2sinsei(record)
		if ss.IraiYmd == date {
			hhshash[ss.Hhsno] = true
		}
	}

	for _, record := range records {
		ss := csv2sinsei(record)
		if _, ok := hhshash[ss.Hhsno]; ok {
			sinsei = append(sinsei, ss)
		}
	}

	return sinsei, hhshash, nil
}

func getdata_fromDB(file string, hhshash map[string]bool) (sinsei []Sinsei, err error) {
	f, err := os.Open(file)
	if err != nil {
		return sinsei, err
	}
	defer f.Close()

	zr, err := gzip.NewReader(f)
	if err != nil {
		return sinsei, err
	}

	data, err := ioutil.ReadAll(zr)
	if err != nil {
		return sinsei, err
	}

	if err := zr.Close(); err != nil {
		return sinsei, err
	}

	r := csv.NewReader(strings.NewReader(string(data)))
	records, err := r.ReadAll()
	if err != nil {
		return sinsei, err
	}

	for _, record := range records {
		hno := strings.TrimSpace(record[0])
		if _, ok := hhshash[hno]; ok {
			sinsei = append(sinsei, csv2sinsei(record))
		}
	}

	return sinsei, nil
}

func shorten(msg string, length int) string {
	if len(msg) > length {
		msg = msg[0:length] + "..."
	}
	return msg
}

func str2cp932(s string) string {
	s, _, _ = transform.String(japanese.ShiftJIS.NewEncoder(), s)
	return s
}

func print_debug_log(msg string) {
	if debug_log {
		fmt.Fprintf(os.Stderr, "%s\n", msg)
	}
}