1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
%w[json erb]
.each { require _1 }
def fputs(f, c)
puts "fputs #{f}"
File.write f, c
end
def erb(tmpl, ctx={}) =
ERB.new(File.read(tmpl), trim_mode: "-")
.result_with_hash(ctx)
module Component
def Component.parse(str)
script = []; style = []; html = []
cur = html
str.lines.each do |line|
line = line.chomp
if line == "<script>" and cur.object_id == html.object_id
cur = script
elsif line == "<style>" and cur.object_id == html.object_id
cur = style
elsif line =~ /^<\/(style|script)>$/ and cur.object_id != html.object_id
cur = html
else
cur.push line
end
end
{style: style * "\n",
html: html * "\n",
script: script * "\n"}
end
private
def Component.amalg(list, key) =
list.values.map { _1[key] }.filter { _1 != "" } * "\n"
public
def Component.parse_many(deps)
start = deps.map { [_1.split(".")[0], parse(read(_1, []))] }.to_h
start.merge(style: amalg(start, :style),
html: amalg(start, :html),
script: amalg(start, :script))
end
def Component.read(f, deps)
if not f =~ /\.erb/
File.read f
else
erb f, parse_many(deps)
end
end
end
DB = "protobowl-2019-10-15-ALL.json"
KEPT_KEYS = %w[category subcategory difficulty
year source tournament round num
question answer]
$data = nil
def get_data
return $data if $data
$data =
File.read(DB)
.split("\n")
.map! { JSON.parse _1 }
.filter! { _1["type"] == "qb" }
end
file DB do
sh "wget -N https://github.com/neotenic/database-dumps/raw/refs/heads/master/2019-10-15-ALL.json.xz"
sh "unxz 2019-10-15-ALL.json.xz"
mv "2019-10-15-ALL.json", DB
end
file "summary.json": [DB] do
data = get_data
difficulties = data.map { _1["difficulty"] }.uniq
categories = data.map { _1["category"] }.uniq
fputs "summary.json",
JSON.generate({difficulties:, categories:})
end.invoke # ensure it's generated now
$difficulties, $categories =
JSON.parse(File.read "summary.json")
.values_at("difficulties", "categories")
def dir_from(dif) = "qdb/#{dif.downcase}"
def name_from(dif, cat) = "#{dir_from dif}/#{cat.downcase.gsub(/\s/, "-")}.json"
$files = []
$difficulties.each do |dif|
dir = dir_from(dif)
directory dir
$categories.each do |cat|
f = name_from(dif, cat)
$files.push f
file f => [DB, dir] do
d = get_data
.filter { _1["difficulty"] == dif &&
_1["category"] == cat }
.map { _1.slice(*KEPT_KEYS) }
fputs f, JSON.generate(d)
end
end
end
file qdb: [DB, *$files]
file "deck.html": ["deck.erb.html", "summary.json"] do
fputs "deck.html", erb("deck.erb.html")
end
file "_.html": ["_.erb.html",
"utils.html", "storage.html",
"card.html", "reviewer.html",
"deck.html", "settings.html",
"ie.html"] do |t|
fputs "_.html", Component.read("_.erb.html", t.prereqs[1..-1])
end
task :clean do
["qdb", "summary.json", "_.html", "deck.html"].map { rm_r _1 }
end
task default: ["qdb", "_.html"]
task :publish do
sh "rsync -rutv --delete qdb _.html manifest.json root@ba.ln.ea.cx:/var/www/onpoint/"
end
|