-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpatch_overrides.py
More file actions
134 lines (118 loc) · 5.67 KB
/
Copy pathpatch_overrides.py
File metadata and controls
134 lines (118 loc) · 5.67 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
#!/usr/bin/env python3
"""Let a sidecar file correct subtopic and topic without editing bank files.
tools/section_gaps.py and tools/section_triage.py found blocks whose
origin.section was captured wrong, and in seven of them that wrong heading also
produced a wrong topic tag. Both need fixing, and the obvious way - editing
content/banks/mineru/*.json - is the wrong way: those files are regenerated by
import_mineru_bank.py, so the next re-import silently discards every
correction.
So corrections live in content/subtopic_overrides.json instead, keyed by source
block rather than by question id. Block keys survive a re-import; question ids
do not necessarily, and a per-question file would need re-checking every time
the extraction changed. One entry covers a whole block, which is the unit the
defect actually occurs in.
The override runs last in _hydrate, after the section backfill, so it wins over
both the heading and anything in the bank file. Entries carry "apply": false
until a human flips them, meaning a generated file does nothing until reviewed.
Two edits:
1. content.overrides() load and index the sidecar, cached like every other
content read and cleared by invalidate()
2. content._hydrate apply a matching entry's subtopic and topic
An override may move a question to a different subject, because some of these
belong to one: the depth-first-search questions sitting under
discrete-mathematics/graph-theory are algorithms/graph-traversal questions.
item["subject"] is already whatever the question says rather than whatever file
it sits in, so this needs no further change.
Run patch_subtopic.py first - this patch anchors on its output.
CRLF-safe and idempotent. Run from the repo root: python patch_overrides.py
"""
import io
import os
import sys
EDITS = [
# ---- 1. loader -----------------------------------------------------
('core/content.py',
'def normalise_topic(conn, subject_slug, topic_slug, name=""):',
'def overrides(reload=False):\n'
' """Block key -> correction, from content/subtopic_overrides.json.\n'
'\n'
' Keyed "<volume>|<chapter>.<block>" so a correction outlives the\n'
' re-import that would renumber or rename individual questions. Entries\n'
' with apply false are ignored, so a freshly generated file is inert\n'
' until somebody has read it.\n'
' """\n'
' if reload or "overrides" not in _cache:\n'
' data = _read_json(\n'
' os.path.join(CONTENT_DIR, "subtopic_overrides.json"), {}\n'
' ) or {}\n'
' out = {}\n'
' for key, spec in (data.get("overrides") or {}).items():\n'
' if not isinstance(spec, dict) or not spec.get("apply"):\n'
' continue\n'
' entry = {}\n'
' if "subtopic" in spec:\n'
' entry["subtopic"] = _norm_key(spec.get("subtopic") or "")\n'
' topic = (spec.get("topic") or "").strip()\n'
' if "/" in topic:\n'
' subject, tslug = topic.split("/", 1)\n'
' entry["subject"] = subject\n'
' entry["topic"] = tslug\n'
' if entry:\n'
' out[key] = entry\n'
' _cache["overrides"] = out\n'
' return _cache["overrides"]\n'
'\n'
'\n'
'def _block_key(origin):\n'
' """"<volume>|<chapter>.<block>" for a question\'s origin, or ""."""\n'
' if not isinstance(origin, dict):\n'
' return ""\n'
' parts = (origin.get("ref") or "").split(".")\n'
' if len(parts) < 3 or not parts[1].isdigit():\n'
' return ""\n'
' return "%s|%s.%d" % (origin.get("volume") or "", parts[0], int(parts[1]))\n'
'\n'
'\n'
'def normalise_topic(conn, subject_slug, topic_slug, name=""):'),
# ---- 2. apply in _hydrate ------------------------------------------
('core/content.py',
' item["subtopic"] = _norm_key(origin["section"])\n'
' item.setdefault("subtopic", "")',
' item["subtopic"] = _norm_key(origin["section"])\n'
' item.setdefault("subtopic", "")\n'
' # Last word on both fields. A blank subtopic here is deliberate and\n'
' # not a failure: where the heading is wrong but nobody knows the right\n'
' # one, empty costs granularity while wrong misroutes every search that\n'
' # would otherwise have found the question.\n'
' fix = overrides().get(_block_key(q.get("origin")))\n'
' if fix:\n'
' item.update(fix)'),
]
def main():
changed = skipped = 0
for path, old, new in EDITS:
if not os.path.isfile(path):
sys.exit("missing %s - run this from the repo root" % path)
s = io.open(path, encoding="utf-8", newline="").read()
crlf = "\r\n" in s
o = old.replace("\n", "\r\n") if crlf else old
n = new.replace("\n", "\r\n") if crlf else new
if n in s:
print(" skip %-26s already applied" % path)
skipped += 1
continue
if s.count(o) != 1:
sys.exit(
" ERROR %s: anchor found %d times, expected 1.%s"
% (path, s.count(o),
" Run patch_subtopic.py first." if "subtopic" in old else "")
)
io.open(path, "w", encoding="utf-8", newline="").write(s.replace(o, n, 1))
print(" patched %-26s ok" % path)
changed += 1
print("\n %d edit(s) applied, %d already present" % (changed, skipped))
if changed:
print(" Next: python tools/propose_overrides.py")
return 0
if __name__ == "__main__":
sys.exit(main())