Mercurial > mercurial > hgweb_golang.cgi
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 > > ></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 |
