Mercurial > mercurial > hgweb_kaigo.hg.cgi
comparison Perori/perori.go @ 0:aaaa401818a1 draft
first commit.
author | pyon <pyon@macmini> |
---|---|
date | Mon, 24 May 2021 21:32:58 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:aaaa401818a1 |
---|---|
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.3b" | |
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: 30px;">{{.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 > > ></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[2:4], 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 if dbdata[i].IraiYmd != dbdata[j].IraiYmd { | |
201 return dbdata[i].IraiYmd > dbdata[j].IraiYmd | |
202 } | |
203 return false | |
204 }) | |
205 | |
206 var dbdata2 []Sinsei // delete same Ymd (for changing Dr.) | |
207 var lasthhsno, lastymd string | |
208 for _, ss := range dbdata { | |
209 if ss.Hhsno == lasthhsno && lastymd == ss.Ymd { | |
210 continue | |
211 } | |
212 dbdata2 = append(dbdata2, ss) | |
213 lasthhsno = ss.Hhsno | |
214 lastymd = ss.Ymd | |
215 } | |
216 | |
217 var lastdata []Sinsei | |
218 prevhash := make(map[string]PrevSinsei) | |
219 hhscnt := make(map[string]int) | |
220 for _, ss := range dbdata2 { | |
221 ss.Humanize() | |
222 switch hhscnt[ss.Hhsno] { | |
223 case 0: | |
224 lastdata = append(lastdata, ss) | |
225 case 1: | |
226 prevhash[ss.Hhsno] = PrevSinsei{ | |
227 Biko: ss.Biko, | |
228 DrId: ss.DrId, | |
229 Dr: ss.Dr + "(" + ss.Hp + ":" + ss.Senmon + ")", | |
230 IraiYmd: ss.IraiYmd, | |
231 Ymd: ss.Ymd, | |
232 Kubun: ss.Kubun, | |
233 } | |
234 } | |
235 hhscnt[ss.Hhsno]++; | |
236 } | |
237 print_debug_log(fmt.Sprintf("lastdata: n=%d", len(lastdata))) // | |
238 | |
239 doctorhash := make(map[string]Doctor) | |
240 hpcnt := make(map[string]int) | |
241 var hpcntsum int | |
242 for _, ss := range lastdata { | |
243 ss.SetPrev(prevhash[ss.Hhsno]) | |
244 if d, ok := doctorhash[ss.DrId]; !ok { | |
245 doctorhash[ss.DrId] = Doctor{ | |
246 Id: ss.DrId, | |
247 Name: ss.Dr, | |
248 Kana: ss.DrKana, | |
249 Hp: ss.Hp, | |
250 Senmon: ss.Senmon, | |
251 Clients: []Sinsei{ss}, | |
252 } | |
253 } else { | |
254 d.AddClient(ss) | |
255 doctorhash[ss.DrId] = d | |
256 } | |
257 hpcnt[ss.Hp]++ | |
258 hpcntsum++ | |
259 } | |
260 | |
261 var doctors []Doctor | |
262 for _, dr := range doctorhash { | |
263 doctors = append(doctors, dr) | |
264 } | |
265 sort.Slice(doctors, func(i, j int) bool { | |
266 if doctors[i].Kana != doctors[j].Kana { | |
267 return doctors[i].Kana < doctors[j].Kana | |
268 } | |
269 if doctors[i].Id != doctors[j].Id { | |
270 return doctors[i].Id < doctors[j].Id | |
271 } | |
272 return false | |
273 }) | |
274 | |
275 irai := struct { | |
276 Ymd string | |
277 NHhs int | |
278 //NSinsei int | |
279 NDr int | |
280 Doctors []Doctor | |
281 Hp map[string]int | |
282 HpSum int | |
283 }{ | |
284 Ymd: strings.Join([]string{date[0:4], date[4:6], date[6:8]}, "."), | |
285 NHhs: len(hhshash), | |
286 //NSinsei: len(dbdata), | |
287 NDr: len(doctors), | |
288 Doctors: doctors, | |
289 Hp: hpcnt, | |
290 HpSum: hpcntsum, | |
291 } | |
292 | |
293 funcmap := template.FuncMap{ | |
294 "shorten": shorten, | |
295 "str2cp932": str2cp932, | |
296 "add1": func(a int) int { return a + 1 }, | |
297 } | |
298 | |
299 t, err := template.New("webpage").Funcs(funcmap).Parse(tpl) | |
300 if err != nil { | |
301 log.Fatal(err) | |
302 } | |
303 | |
304 err = t.Execute(os.Stdout, irai) | |
305 if err != nil { | |
306 log.Fatal(err) | |
307 } | |
308 } | |
309 | |
310 // Utility functions | |
311 func csv2sinsei(record []string) Sinsei { | |
312 return Sinsei{ | |
313 Hhsno: strings.TrimSpace(record[0]), | |
314 Name: strings.TrimSpace(record[1]), | |
315 Biko: strings.TrimSpace(record[2]), | |
316 DrId: strings.TrimSpace(record[3]), | |
317 Dr: strings.TrimSpace(record[4]), | |
318 DrKana: strings.TrimSpace(record[5]), | |
319 Hp: strings.TrimSpace(record[6]), | |
320 IraiYmd: strings.TrimSpace(record[7]), | |
321 Ymd: strings.TrimSpace(record[8]), | |
322 Kubun: strings.TrimSpace(record[9]), | |
323 Senmon: strings.TrimSpace(record[10]), | |
324 } | |
325 } | |
326 | |
327 func getdata_fromCSV(file, date string) (sinsei []Sinsei, hhshash map[string]bool, err error) { | |
328 hhshash = make(map[string]bool) | |
329 | |
330 data, err := ioutil.ReadFile(file) | |
331 if err != nil { | |
332 return sinsei, hhshash, err | |
333 } | |
334 | |
335 r := csv.NewReader(strings.NewReader(string(data))) | |
336 records, err := r.ReadAll() | |
337 if err != nil { | |
338 return sinsei, hhshash, err | |
339 } | |
340 | |
341 for _, record := range records { | |
342 ss := csv2sinsei(record) | |
343 if ss.IraiYmd == date { | |
344 hhshash[ss.Hhsno] = true | |
345 } | |
346 } | |
347 | |
348 for _, record := range records { | |
349 ss := csv2sinsei(record) | |
350 if _, ok := hhshash[ss.Hhsno]; ok { | |
351 sinsei = append(sinsei, ss) | |
352 } | |
353 } | |
354 | |
355 return sinsei, hhshash, nil | |
356 } | |
357 | |
358 func getdata_fromDB(file string, hhshash map[string]bool) (sinsei []Sinsei, err error) { | |
359 f, err := os.Open(file) | |
360 if err != nil { | |
361 return sinsei, err | |
362 } | |
363 defer f.Close() | |
364 | |
365 zr, err := gzip.NewReader(f) | |
366 if err != nil { | |
367 return sinsei, err | |
368 } | |
369 | |
370 data, err := ioutil.ReadAll(zr) | |
371 if err != nil { | |
372 return sinsei, err | |
373 } | |
374 | |
375 if err := zr.Close(); err != nil { | |
376 return sinsei, err | |
377 } | |
378 | |
379 r := csv.NewReader(strings.NewReader(string(data))) | |
380 records, err := r.ReadAll() | |
381 if err != nil { | |
382 return sinsei, err | |
383 } | |
384 | |
385 for _, record := range records { | |
386 hno := strings.TrimSpace(record[0]) | |
387 if _, ok := hhshash[hno]; ok { | |
388 sinsei = append(sinsei, csv2sinsei(record)) | |
389 } | |
390 } | |
391 | |
392 return sinsei, nil | |
393 } | |
394 | |
395 func shorten(msg string, length int) string { | |
396 if len(msg) > length { | |
397 msg = msg[0:length] + "..." | |
398 } | |
399 return msg | |
400 } | |
401 | |
402 func str2cp932(s string) string { | |
403 s, _, _ = transform.String(japanese.ShiftJIS.NewEncoder(), s) | |
404 return s | |
405 } | |
406 | |
407 func print_debug_log(msg string) { | |
408 if debug_log { | |
409 fmt.Fprintf(os.Stderr, "%s\n", msg) | |
410 } | |
411 } | |
412 |