-
Notifications
You must be signed in to change notification settings - Fork 431
Expand file tree
/
Copy pathcreate_table.py
More file actions
183 lines (162 loc) · 5.45 KB
/
create_table.py
File metadata and controls
183 lines (162 loc) · 5.45 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import yaml
import json
import pathlib
class Trie:
def __init__(self):
self.children = {}
self.values = []
self.entry = ""
def insert(self, path, value):
node = self
for c in path:
if c not in node.children:
node.children[c] = Trie()
node = node.children[c]
node.values.append(value)
node.entry = value["entry"]
def json(self):
if not self.children:
return self.values
return {k: v.json() for k, v in self.children.items()}
def tabulator_leaf(self):
result = {}
for v in self.values:
result[v["format"]] = v["table_cell"]
return result
def tabulator(self):
if not self.children:
return []
result = []
for k, v in self.children.items():
children = v.tabulator()
feature = k
if v.entry != "":
link = (
"<a href='%s' target='_blank'><i class='fa-solid fa-link' aria-label='link'></i></a>"
% v.entry
)
feature = "%s %s" % (link, k)
d = {"sort_key": k, "feature": feature, **v.tabulator_leaf()}
if children:
d["_children"] = children
result.append(d)
result.sort(key=lambda x: x["sort_key"])
return result
def depth(self):
if not self.children:
return 0
return max([v.depth() for v in self.children.values()]) + 1
def size(self):
if not self.children:
return 1
return sum([v.size() for v in self.children.values()])
def walk(self, visitor, path=None):
if path is None:
path = []
visitor(self, path)
for k, v in self.children.items():
v.walk(visitor, path + [k])
def extract_metadata_from_file(file):
with open(file, "r") as f:
lines = f.readlines()
start = None
end = None
for i, line in enumerate(lines):
if line.strip() == "---":
if start is None:
start = i
else:
end = i
metadata = yaml.load(
"".join(lines[start + 1 : end]), Loader=yaml.SafeLoader
)
return metadata
raise ValueError("No metadata found in file %s" % file)
def table_cell(entry, _feature, _format_name, format_config):
if type(format_config) == str:
format_config = {}
result = []
quality = format_config.get("quality", "unknown")
if quality is not None:
if type(quality) == str:
quality = quality.lower()
qualities = {
-1: "🚫",
0: "⚠",
1: "✓",
2: "✓✓",
"unknown": "❓",
"na": "NA",
}
colors = {
-1: "bad",
0: "ok",
1: "good",
2: "good",
"unknown": "unknown",
"na": "na",
}
color = colors[quality]
quality_icon = qualities.get(quality, "❓")
result.append(f"<span class='{color}'>{quality_icon}</span>")
comment = format_config.get("comment", None)
if comment is not None:
# This is going to be an accessibility problem
result.append(f"<span title='{comment}'>💬</span>")
return "".join(result)
def compute_trie(detailed=False):
trie = Trie()
pattern = "qmd-files/**/*.qmd" if detailed else "qmd-files/**/document.qmd"
for entry in pathlib.Path(".").glob(pattern):
if detailed:
feature = entry.parts[1:]
else:
feature = entry.parts[1:-1]
front_matter = extract_metadata_from_file(entry)
try:
format = front_matter["format"]
except KeyError:
raise Exception("No format found in %s" % entry)
for format_name, format_config in format.items():
trie.insert(
feature,
{
"feature": "/".join(feature),
"format": format_name,
"entry": entry,
"format_config": format_config,
"table_cell": table_cell(
entry, feature, format_name, format_config
),
},
)
return trie
def render_features_formats_data(trie=None):
if trie is None:
trie = compute_trie()
entries = trie.tabulator()
return (
"```{=html}\n<script type='text/javascript'>\nvar tableData = %s;\n</script>\n```\n"
% json.dumps(entries, indent=2)
)
def compute_quality_summary(trie=None):
if trie is None:
trie = compute_trie()
quality_summary = {"unknown": 0, -1: 0, 0: 0, 1: 0, 2: 0, "na": 0}
n_rows = 0
def visit(node, _path):
nonlocal n_rows
if not node.children or len(node.values):
n_rows = n_rows + 1
for v in node.values:
config = v["format_config"]
if type(config) == str:
config = {}
quality = config.get("quality", "unknown")
if type(quality) == str:
quality = quality.lower()
if quality_summary.get(quality) is None:
raise ValueError("Invalid quality value %s" % quality)
quality_summary[quality] += 1
trie.walk(visit)
return {"n_rows": n_rows, "quality": quality_summary}