Reduce time windows a bit
[sispare.git] / from_jisho.myr
blobbbf2120a46c16c5080ddfad82387e458ffd8e026
1 use std
3 use bio
4 use date
5 use escfmt
6 use json
7 use sys
9 use t
11 use "util"
13 const main = {args
14         var dir_arg : byte[:] = [][:]
15         var a_string : byte[:] = [][:]
16         var b_string : byte[:] = [][:]
17         var name : byte[:] = [][:]
18         var basedir : byte[:] = [][:]
19         var dir_checked = false
20         var cmd : std.optparsed = std.optparse(args, &[
21                 .minargs = 1,
22                 .maxargs = 1,
23                 .opts = [
24                         [ .opt = 'd', .arg = "dir", .desc = "base directory", .optional = true ],
25                 ][:],
26         ])
28         for opt : cmd.opts
29                 match opt
30                 | ('d', dir): dir_arg = dir
31                 | _: std.fatal("impossible {}\n", opt)
32                 ;;
33         ;;
35         match get_basedir(dir_arg)
36         | `std.Err e: std.fatal("{}\n", e)
37         | `std.Ok b: basedir = b
38         ;;
39         auto (basedir : t.doomed_str)
41         if cmd.args.len != 1
42                 std.fatal("exactly one argument required\n")
43         ;;
45         match from_jisho(cmd.args[0])
46         | `std.Ok (n, a, b): (name, a_string, b_string) = (n, a, b)
47         | `std.Err e: std.fatal("{}\n", e)
48         ;;
49         var card_name : byte[:] = std.fmt("jisho_word_{}", name)
50         auto (card_name : t.doomed_str)
51         var card_dir : byte[:] = std.pathjoin([basedir, "cards", card_name][:])
53         match std.diropen(card_dir)
54         | `std.Ok d2:
55                 std.dirclose(d2)
56                 std.fatal("card \"{}\" already exists\n", name)
57         | `std.Err e:
58         ;;
60         match std.mkpath(card_dir)
61         | std.Enone:
62         | e: std.fatal("mkdir(\"{}\"): {}\n", card_dir, e)
63         ;;
65         var sched_dir_path = std.pathjoin([basedir, "schedule"][:])
66         match std.mkpath(sched_dir_path)
67         | std.Enone:
68         | e: std.fatal("mkdir(\"{}\"): {}\n", sched_dir_path, e)
69         ;;
71         var a_path : byte[:] = std.pathjoin([card_dir, "side_A"][:])
72         var b_path : byte[:] = std.pathjoin([card_dir, "side_B"][:])
73         var level_path : byte[:] = std.pathjoin([card_dir, "level"][:])
74         var next_review : date.instant = date.subperiod(date.utcnow(), `date.Day 7)
75         var sched_path = std.pathjoin([sched_dir_path, std.fmt("{f=%Y-%m-%d}", next_review)][:])
77         match std.open(sched_path, std.Owrite | std.Ocreat | std.Oappend)
78         | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", sched_path, e)
79         | `std.Ok fd:
80                 std.fput(fd, "{}\n", card_name)
81                 std.close(fd)
82         ;;
84         match std.open(level_path, std.Owrite | std.Ocreat | std.Oappend)
85         | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", level_path, e)
86         | `std.Ok fd:
87                 std.fput(fd, "1\n")
88                 std.close(fd)
89         ;;
91         match std.open(a_path, std.Owrite | std.Ocreat | std.Oappend)
92         | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", a_path, e)
93         | `std.Ok fd:
94                 std.fput(fd, "{}\n", a_string)
95                 std.close(fd)
96         ;;
98         match std.open(b_path, std.Owrite | std.Ocreat | std.Oappend)
99         | `std.Err e: std.fput(std.Err, "std.open(\"{}\"): {}\n", b_path, e)
100         | `std.Ok fd:
101                 std.fput(fd, "{}\n", b_string)
102                 std.close(fd)
103         ;;
105         std.put("Added to {}\n", card_dir)
108 const from_jisho = { w : byte[:]
109         var url = std.fmt("https://jisho.org/api/v1/search/words?keyword={}", escfmt.url(w))
110         var human_url = std.fmt("https://jisho.org/word/{}", escfmt.url(w))
111         var res : byte[:] = [][:]
112         var root : json.elt#
113         var found_anything : bool = false
114         var name : byte[:] = [][:]
115         var side_b : std.strbuf# = std.mksb()
116         auto (url : t.doomed_str)
117         auto (human_url : t.doomed_str)
119         match std.spork(["curl", "-s", url][:])
120         | `std.Err e: -> `std.Err std.fmt("spork(): {}", e)
121         | `std.Ok (pid, infd, outfd):
122                 std.close(infd)
123                 match std.fslurp(outfd)
124                 | `std.Ok buf: res = buf
125                 | `std.Err e: -> `std.Err std.fmt("slurp(): {}", e)
126                 ;;
128                 match std.wait(pid)
129                 | `std.Waiterror: -> `std.Err "waitpid(curl): Waiterror"
130                 | `std.Wfailure: -> `std.Err "waitpid(curl): Wfailure"
131                 | `std.Wsignalled : -> `std.Err "waitpid(curl): Wsignalled"
132                 | `std.Wsuccess:
133                 ;;
134         ;;
136         match json.parse(res)
137         | `std.Err e: -> `std.Err std.fmt("json.parse()", res)
138         | `std.Ok r: root = r
139         ;;
141         match by_path(root, ["meta", "status"][:])
142         | `std.Some v:
143                 match v#
144                 | `json.Num json_code:
145                         if json_code != 200.0
146                                 -> `std.Err std.fmt("response code: {}", json_code)
147                         ;;
148                 | _: -> `std.Err std.fmt("response {}", v)
149                 ;;
150         | _: -> `std.Err "malformed json response (no status code)"
151         ;;
153         /* Now walk through the data and try to pick out parts that match */
154         var data : json.elt#[:] = [][:]
155         match by_path(root, ["data"][:])
156         | `std.Some &(`json.Arr v): data = v
157         | `std.Some v: -> `std.Err std.fmt("malformed json response (data was \"{}\")", v)
158         | `std.None: -> `std.Err "malformed json response (no data)"
159         ;;
161         std.sbfmt(side_b, "{}\n\n", human_url)
163         for var j = 0; j < data.len; ++j
164                 var srs = get_spelling_readings(data[j])
165                 for (spelling, reading) : srs
166                         if std.eq(spelling, w) || std.eq(reading, w)
167                                 append_word_desc(side_b, data[j], srs)
168                                 break
169                         ;;
170                 ;;
171         ;;
172         
174         -> `std.Ok (w, w, std.sbfin(side_b))  
177 const by_path : (root : json.elt#, seq : byte[:][:] -> std.option(json.elt#)) = {root : json.elt#, seq : byte[:][:]
178         while seq.len > 0
179                 match root#
180                 | `json.Obj a:
181                         var success = false
182                         for var j = 0; j < a.len; ++j
183                                 var key : byte[:] = a[j].0
184                                 var val : json.elt# = a[j].1
185                                 if std.eq(key, seq[0])
186                                         root = val
187                                         seq = seq[1:]
188                                         success = true
189                                         j = a.len
190                                 ;;
191                         ;;
193                         if !success
194                                 -> `std.None
195                         ;;
196                 | _:
197                         -> `std.None
198                 ;;
199         ;;
201         -> `std.Some root
204 /* Just take the first entry of "japanese" that has both a spelling and a reading */
205 const get_spelling_readings = {root : json.elt#
206         var ret : (byte[:], byte[:])[:] = [][:]
207         match by_path(root, ["japanese"][:])
208         | `std.Some &(`json.Arr a):
209                 for var j = 0; j < a.len; ++j
210                         match (by_path(a[j], ["word"][:]), by_path(a[j], ["reading"][:]))
211                         | (`std.Some &(`json.Str w), `std.Some &(`json.Str r)):
212                                 std.slpush(&ret, (w, r))
213                         | (`std.None, `std.Some &(`json.Str r)):
214                                 std.slpush(&ret, ("", r))
215                         | _:
216                         ;;
217                 ;;
218         | _:
219         ;;
221         -> ret
224 const append_word_desc = {sb : std.strbuf#, root : json.elt#, srs : (byte[:], byte[:])[:]
225         /*
226              本 (ほん)
228              Noun
230                1. book; volume; script
232              Prefix
234                2. this; present
236                3. main; head
238                4. real; regular
240              Suffix, Counter
242                5. counter for long cylindrical things; counter for films, TV shows, etc.; counter for goals, home runs, etc.; counter for telephone calls​
243          */
244         var n = 1
245         for (spelling, reading) : srs
246                 if spelling.len > 0
247                         std.sbfmt(sb, "{}  ({})\n", spelling, reading)
248                 else
249                         std.sbfmt(sb, "{}\n", reading)
250                 ;;
251         ;;
252         std.sbfmt(sb, "\n")
254         match by_path(root, ["senses"][:])
255         | `std.Some &(`json.Arr a):
256                 for var j = 0; j < a.len; ++j
257                         match by_path(a[j], ["parts_of_speech"][:])
258                         | `std.Some &(`json.Arr ap):
259                                 var first = true
260                                 for var k = 0; k < ap.len; ++k
261                                         match ap[k]
262                                         | &(`json.Str s):
263                                                 if !first
264                                                         std.sbfmt(sb, ", ")
265                                                 ;;
266                                                 std.sbfmt(sb, "{}", s)
267                                                 first = false
268                                         | _:
269                                         ;;
270                                 ;;
271                                 if !first
272                                         std.sbfmt(sb, "\n\n")
273                                 ;;
274                         | _:
275                         ;;
277                         match by_path(a[j], ["english_definitions"][:])
278                         | `std.Some &(`json.Arr ap):
279                                 var first = true
280                                 for var k = 0; k < ap.len; ++k
281                                         match ap[k]
282                                         | &(`json.Str s):
283                                                 if !first
284                                                         std.sbfmt(sb, ", ")
285                                                 else
286                                                         std.sbfmt(sb, " {w=3,p= }. ", n)
287                                                 ;;
288                                                 std.sbfmt(sb, "{}", s)
289                                                 first = false
290                                         | _:
291                                         ;;
292                                 ;;
294                                 if !first
295                                         std.sbfmt(sb, "\n\n")
296                                 ;;
298                                 n++
299                         | _:
300                         ;;
302                 ;;
303         | _:
304         ;;