comparison 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
comparison
equal deleted inserted replaced
40:c6df3bae683e 41:34a474fb83c3
1 package main
2
3 import (
4 "flag"
5 "compress/gzip"
6 "encoding/csv"
7 "fmt"
8 "html/template"
9 "io/ioutil"
10 "log"
11 "os"
12 "strings"
13 "sort"
14 "time"
15
16 "golang.org/x/text/encoding/japanese"
17 "golang.org/x/text/transform"
18 )
19
20 var debug_log bool
21
22 // Constants
23 const version = "0.2"
24 const default_dbfile = "ikenshoirai.db"
25 const default_csvfile = "ikenshoirai.csv"
26
27 const tpl = `
28 <!DOCTYPE html> <html>
29 <head>
30 <style type="text/css">
31 body { font-size: 9pt; margin-left: 0px;}
32 h2 { font-size: 11pt; margin-bottom: 1px; background-color: #ccccff; padding-left: 5px; }
33 h3 { font-size: 11pt; margin-bottom: 1px; background-color: #f0a8a8; padding-left: 10px; }
34 table, th, td { border: 0.3px #c0c0c0 solid; border-collapse:collapse; }
35 table { margin-bottom: 5px; margin-left: 15px; }
36 th { background-color: #ccffcc; }
37 hr { page-break-before: always; }
38 </style>
39 <title> - </title>
40 </head>
41 <body>
42
43 <h2> List <small>( Date = {{.Ymd}} / N = {{.NHhs}} / Dr = {{.NDr}} )</small> </h2>
44 {{range .Doctors}}
45 <h3>{{.Name}}<small> ..... {{.Hp}}:{{.Senmon}}</small></h3>
46 {{range .Clients}}
47 <table>
48 <tr>
49 <td width=140 style="background-color: #98f0f0; padding-left: 10px;">{{.Name}}</td>
50 <td width=120 align=center>{{.Kubun}} {{.Ymd}}</td>
51 <td width=480 style="padding-left: 10px;">
52
53 {{if .Prev.Ymd}}
54 {{if eq .DrId .Prev.DrId}}
55 {{str2cp932 "★ 継続 -"}} {{.Prev.Ymd}}
56 {{else}}
57 {{.Prev.Dr}} {{.Prev.Ymd}}
58 {{end}}
59 {{else}}
60 New !
61 {{end}}
62
63 </td>
64 <td width=80 align=center>{{.Hhsno}}</td>
65 </tr>
66 <tr>
67 <td colspan=4 style="font-family: serif; font-size: 8pt; padding-left: 10px;">{{.Biko}}</td>
68 </tr>
69
70 </table>
71 {{end}}
72 {{end}}
73
74 <hr />
75 {{$hpno := 0}}
76 <h2> N by Hp </h2>
77 <table>
78 <tr> <th> no </th> <th> hp </th> <th width=60> n </th> </tr>
79 {{range $hp, $n := .Hp}}
80 <tr>
81 {{$hpno = add1 $hpno}}
82 <td align=right style="padding-right: 5px;"> {{$hpno}} </td>
83 <td style="padding-left: 5px;"> {{$hp}} </td>
84 <td align=right style="padding-right: 5px;"> {{$n}} </td>
85 </tr>
86 {{end}}
87 <tr> <td></td> <td align=right> sum &gt &gt &gt</td> <td align=right style="padding-right: 5px;"> <b> {{.HpSum}} </b> </td> </tr>
88 </table>
89 </body>
90 </html>`
91
92
93 // Define Types
94 type PrevSinsei struct {
95 Biko string
96 DrId string
97 Dr string
98 IraiYmd string
99 Ymd string
100 Kubun string
101 }
102
103 type Sinsei struct {
104 Hhsno string
105 Name string
106 Biko string
107 DrId string
108 Dr string
109 DrKana string
110 Hp string
111 IraiYmd string
112 Ymd string
113 Kubun string
114 Senmon string
115 Prev PrevSinsei
116 }
117
118 func (s *Sinsei) SetPrev(prev PrevSinsei) {
119 s.Prev = prev
120 }
121
122 func (s Sinsei) String() string {
123 return strings.Join([]string{s.Hhsno, s.Name, s.Ymd, s.Kubun, s.Dr, s.Hp, s.Senmon, s.IraiYmd, s.Biko}, ",")
124 }
125
126 func (s *Sinsei) Humanize() {
127 var buf string
128
129 switch s.Kubun {
130 case "01":
131 buf = "新規"
132 case "02":
133 buf = "更新"
134 case "10":
135 buf = "支介"
136 case "05":
137 buf = "区変"
138 case "03":
139 buf = "転入"
140 case "09":
141 buf = "証交"
142 }
143 s.Kubun, _, _ = transform.String(japanese.ShiftJIS.NewEncoder(), buf)
144
145 s.Ymd = strings.Join([]string{s.Ymd[4:6], s.Ymd[6:8]}, ".")
146 }
147
148 type Doctor struct {
149 Id string
150 Name string
151 Kana string
152 Hp string
153 Senmon string
154 Clients []Sinsei
155 }
156
157 func (d *Doctor) AddClient(sinsei Sinsei) {
158 d.Clients = append(d.Clients, sinsei)
159 }
160
161 func (d Doctor) String() string {
162 return d.Name
163 }
164
165 // Main
166 func main() {
167 var csvfile, dbfile, date string
168
169 today := time.Now().Format("20060102")
170
171 flag.StringVar(&csvfile, "c", default_csvfile, "csv file")
172 flag.StringVar(&dbfile, "b", default_dbfile, "db file")
173 flag.StringVar(&date, "r", today, "Ikensho Irai YMD")
174 flag.BoolVar(&debug_log, "d", false, "print debug-log (stderr)")
175 flag.Parse()
176
177 csvdata, hhshash, err := getdata_fromCSV(csvfile, date)
178 if err != nil {
179 log.Fatal(err)
180 }
181 print_debug_log(fmt.Sprintf("csvdata: n=%d", len(csvdata))) //
182 print_debug_log(fmt.Sprintf("hhshash: n=%d", len(hhshash))) //
183
184 dbdata, err := getdata_fromDB(dbfile, hhshash)
185 if err != nil {
186 log.Fatal(err)
187 }
188 print_debug_log(fmt.Sprintf("dbdata: n=%d", len(dbdata))) //
189
190 dbdata = append(dbdata, csvdata...)
191 print_debug_log(fmt.Sprintf("dbdata: n=%d", len(dbdata))) //
192
193 sort.Slice(dbdata, func(i, j int) bool {
194 if dbdata[i].Hhsno != dbdata[j].Hhsno {
195 return dbdata[i].Hhsno < dbdata[j].Hhsno
196 }
197 if dbdata[i].Ymd != dbdata[j].Ymd {
198 return dbdata[i].Ymd > dbdata[j].Ymd
199 }
200 return false
201 })
202
203 var recentdata []Sinsei
204 prevhash := make(map[string]PrevSinsei)
205 hhscnt := make(map[string]int)
206 for _, ss := range dbdata {
207 ss.Humanize()
208 if n := hhscnt[ss.Hhsno]; n == 1 {
209 prevhash[ss.Hhsno] = PrevSinsei{
210 Biko: ss.Biko,
211 DrId: ss.DrId,
212 Dr: ss.Dr + "(" + ss.Hp + ":" + ss.Senmon + ")",
213 IraiYmd: ss.IraiYmd,
214 Ymd: ss.Ymd,
215 Kubun: ss.Kubun,
216 }
217 } else {
218 recentdata = append(recentdata, ss)
219 hhscnt[ss.Hhsno]++;
220 }
221 }
222 print_debug_log(fmt.Sprintf("recentdata: n=%d", len(recentdata))) //
223
224 doctorhash := make(map[string]Doctor)
225 hpcnt := make(map[string]int)
226 var hpcntsum int
227 for _, ss := range recentdata {
228 ss.SetPrev(prevhash[ss.Hhsno])
229 if d, ok := doctorhash[ss.DrId]; !ok {
230 doctorhash[ss.DrId] = Doctor{
231 Id: ss.DrId,
232 Name: ss.Dr,
233 Kana: ss.DrKana,
234 Hp: ss.Hp,
235 Senmon: ss.Senmon,
236 Clients: []Sinsei{ss},
237 }
238 } else {
239 d.AddClient(ss)
240 doctorhash[ss.DrId] = d
241 }
242 hpcnt[ss.Hp]++
243 hpcntsum++
244 }
245
246 var doctors []Doctor
247 for _, dr := range doctorhash {
248 doctors = append(doctors, dr)
249 }
250 sort.Slice(doctors, func(i, j int) bool {
251 return doctors[i].Kana < doctors[j].Kana
252 })
253
254 irai := struct {
255 Ymd string
256 NHhs int
257 NSinsei int
258 NDr int
259 Doctors []Doctor
260 Hp map[string]int
261 HpSum int
262 }{
263 Ymd: strings.Join([]string{date[0:4], date[4:6], date[6:8]}, "."),
264 NHhs: len(hhshash),
265 NSinsei: len(dbdata),
266 NDr: len(doctors),
267 Doctors: doctors,
268 Hp: hpcnt,
269 HpSum: hpcntsum,
270 }
271
272 funcmap := template.FuncMap{
273 "shorten": shorten,
274 "str2cp932": str2cp932,
275 "add1": func(a int) int { return a + 1 },
276 }
277
278 t, err := template.New("webpage").Funcs(funcmap).Parse(tpl)
279 if err != nil {
280 log.Fatal(err)
281 }
282
283 err = t.Execute(os.Stdout, irai)
284 if err != nil {
285 log.Fatal(err)
286 }
287 }
288
289 // Utility functions
290 func csv2sinsei(record []string) Sinsei {
291 return Sinsei{
292 Hhsno: strings.TrimSpace(record[0]),
293 Name: strings.TrimSpace(record[1]),
294 Biko: strings.TrimSpace(record[2]),
295 DrId: strings.TrimSpace(record[3]),
296 Dr: strings.TrimSpace(record[4]),
297 DrKana: strings.TrimSpace(record[5]),
298 Hp: strings.TrimSpace(record[6]),
299 IraiYmd: strings.TrimSpace(record[7]),
300 Ymd: strings.TrimSpace(record[8]),
301 Kubun: strings.TrimSpace(record[9]),
302 Senmon: strings.TrimSpace(record[10]),
303 }
304 }
305
306 func getdata_fromCSV(file, date string) (sinsei []Sinsei, hhshash map[string]bool, err error) {
307 hhshash = make(map[string]bool)
308
309 data, err := ioutil.ReadFile(file)
310 if err != nil {
311 return sinsei, hhshash, err
312 }
313
314 r := csv.NewReader(strings.NewReader(string(data)))
315 records, err := r.ReadAll()
316 if err != nil {
317 return sinsei, hhshash, err
318 }
319
320 for _, record := range records {
321 ss := csv2sinsei(record)
322 if ss.IraiYmd != date {
323 continue
324 }
325 hhshash[ss.Hhsno] = true
326 sinsei = append(sinsei, ss)
327 }
328
329 return sinsei, hhshash, nil
330 }
331
332 func getdata_fromDB(file string, hhshash map[string]bool) (sinsei []Sinsei, err error) {
333 f, err := os.Open(file)
334 if err != nil {
335 return sinsei, err
336 }
337 defer f.Close()
338
339 zr, err := gzip.NewReader(f)
340 if err != nil {
341 return sinsei, err
342 }
343
344 data, err := ioutil.ReadAll(zr)
345 if err != nil {
346 return sinsei, err
347 }
348
349 if err := zr.Close(); err != nil {
350 return sinsei, err
351 }
352
353 r := csv.NewReader(strings.NewReader(string(data)))
354 records, err := r.ReadAll()
355 if err != nil {
356 return sinsei, err
357 }
358
359 for _, record := range records {
360 hno := strings.TrimSpace(record[0])
361 if _, ok := hhshash[hno]; ok {
362 sinsei = append(sinsei, csv2sinsei(record))
363 }
364 }
365
366 return sinsei, nil
367 }
368
369 func shorten(msg string, length int) string {
370 if len(msg) > length {
371 msg = msg[0:length] + "..."
372 }
373 return msg
374 }
375
376 func str2cp932(s string) string {
377 s, _, _ = transform.String(japanese.ShiftJIS.NewEncoder(), s)
378 return s
379 }
380
381 func print_debug_log(msg string) {
382 if debug_log {
383 fmt.Fprintf(os.Stderr, "%s\n", msg)
384 }
385 }
386