Path: blob/main/dev-docs/feature-format-matrix/create_table.py
3557 views
import yaml1import json2import pathlib345class Trie:6def __init__(self):7self.children = {}8self.values = []9self.entry = ""1011def insert(self, path, value):12node = self13for c in path:14if c not in node.children:15node.children[c] = Trie()16node = node.children[c]17node.values.append(value)18node.entry = value["entry"]1920def json(self):21if not self.children:22return self.values23return {k: v.json() for k, v in self.children.items()}2425def tabulator_leaf(self):26result = {}27for v in self.values:28result[v["format"]] = v["table_cell"]29return result3031def tabulator(self):32if not self.children:33return []34result = []35for k, v in self.children.items():36children = v.tabulator()37feature = k38if v.entry != "":39link = (40"<a href='%s' target='_blank'><i class='fa-solid fa-link' aria-label='link'></i></a>"41% v.entry42)43feature = "%s %s" % (link, k)44d = {"sort_key": k, "feature": feature, **v.tabulator_leaf()}45if children:46d["_children"] = children47result.append(d)48result.sort(key=lambda x: x["sort_key"])49return result5051def depth(self):52if not self.children:53return 054return max([v.depth() for v in self.children.values()]) + 15556def size(self):57if not self.children:58return 159return sum([v.size() for v in self.children.values()])6061def walk(self, visitor, path=None):62if path is None:63path = []64visitor(self, path)65for k, v in self.children.items():66v.walk(visitor, path + [k])676869def extract_metadata_from_file(file):70with open(file, "r") as f:71lines = f.readlines()72start = None73end = None74for i, line in enumerate(lines):75if line.strip() == "---":76if start is None:77start = i78else:79end = i80metadata = yaml.load(81"".join(lines[start + 1 : end]), Loader=yaml.SafeLoader82)83return metadata84raise ValueError("No metadata found in file %s" % file)858687def table_cell(entry, _feature, _format_name, format_config):88if type(format_config) == str:89format_config = {}90result = []91quality = format_config.get("quality", "unknown")92if quality is not None:93if type(quality) == str:94quality = quality.lower()95qualities = {96-1: "🚫",970: "⚠",981: "✓",992: "✓✓",100"unknown": "❓",101"na": "NA",102}103colors = {104-1: "bad",1050: "ok",1061: "good",1072: "good",108"unknown": "unknown",109"na": "na",110}111color = colors[quality]112quality_icon = qualities.get(quality, "❓")113result.append(f"<span class='{color}'>{quality_icon}</span>")114comment = format_config.get("comment", None)115if comment is not None:116# This is going to be an accessibility problem117result.append(f"<span title='{comment}'>💬</span>")118return "".join(result)119120121def compute_trie(detailed=False):122trie = Trie()123pattern = "qmd-files/**/*.qmd" if detailed else "qmd-files/**/document.qmd"124for entry in pathlib.Path(".").glob(pattern):125if detailed:126feature = entry.parts[1:]127else:128feature = entry.parts[1:-1]129front_matter = extract_metadata_from_file(entry)130try:131format = front_matter["format"]132except KeyError:133raise Exception("No format found in %s" % entry)134for format_name, format_config in format.items():135trie.insert(136feature,137{138"feature": "/".join(feature),139"format": format_name,140"entry": entry,141"format_config": format_config,142"table_cell": table_cell(143entry, feature, format_name, format_config144),145},146)147return trie148149150def render_features_formats_data(trie=None):151if trie is None:152trie = compute_trie()153entries = trie.tabulator()154return (155"```{=html}\n<script type='text/javascript'>\nvar tableData = %s;\n</script>\n```\n"156% json.dumps(entries, indent=2)157)158159160def compute_quality_summary(trie=None):161if trie is None:162trie = compute_trie()163quality_summary = {"unknown": 0, -1: 0, 0: 0, 1: 0, 2: 0, "na": 0}164n_rows = 0165166def visit(node, _path):167nonlocal n_rows168if not node.children or len(node.values):169n_rows = n_rows + 1170for v in node.values:171config = v["format_config"]172if type(config) == str:173config = {}174quality = config.get("quality", "unknown")175if type(quality) == str:176quality = quality.lower()177if quality_summary.get(quality) is None:178raise ValueError("Invalid quality value %s" % quality)179quality_summary[quality] += 1180181trie.walk(visit)182return {"n_rows": n_rows, "quality": quality_summary}183184185