diff src/kaigo/Perori/perori.go @ 41:34a474fb83c3

add perori/porori/nk.
author pyon@macmini
date Wed, 04 Mar 2020 23:46:59 +0900
parents
children c58172a59534
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/kaigo/Perori/perori.go	Wed Mar 04 23:46:59 2020 +0900
@@ -0,0 +1,386 @@
+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.2"
+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: 10px;">{{.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[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
+		}
+		return false
+	})
+
+	var recentdata []Sinsei
+	prevhash := make(map[string]PrevSinsei)
+	hhscnt := make(map[string]int)
+	for _, ss := range dbdata {
+		ss.Humanize()
+		if n := hhscnt[ss.Hhsno]; n == 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,
+			}
+		} else {
+			recentdata = append(recentdata, ss)
+			hhscnt[ss.Hhsno]++;
+		}
+	}
+	print_debug_log(fmt.Sprintf("recentdata: n=%d", len(recentdata)))	//
+
+	doctorhash := make(map[string]Doctor)
+	hpcnt := make(map[string]int)
+	var hpcntsum int
+	for _, ss := range recentdata {
+		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 {
+		return doctors[i].Kana < doctors[j].Kana
+	})
+
+	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 {
+			continue
+		}
+		hhshash[ss.Hhsno] = true
+		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)
+	}
+}
+