diff options
Diffstat (limited to 'tools/export_lectio_patch.py')
| -rw-r--r-- | tools/export_lectio_patch.py | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/tools/export_lectio_patch.py b/tools/export_lectio_patch.py new file mode 100644 index 0000000..de8a208 --- /dev/null +++ b/tools/export_lectio_patch.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +"""Emit the minimal set of field changes that bring lectio's EF data into +agreement with colitur, each annotated with the rubric that justifies it. + +DELIBERATELY A PATCH, NOT A REGENERATION. colitur carries 205 of the 327 +Polish names lectio ships, so regenerating lectio's ini from colitur would +silently drop 122 of them. It also uses a different slug vocabulary for +Passion/Holy week and a different Paschaltide week numbering. What colitur is +authoritative for is the adjudicated FIELDS -- rank/status, colour, and the +reading citations it has verified against the Missal -- and this emits only +those. + +Every line of output names the colitur allow-list entry (C-class) carrying the +citation, so a lectio reviewer can check the rubric rather than trust the tool. +""" +import re, sys, os + +COL = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +LEC = os.path.expanduser('~/git/projects/lectio/internal/caldata') + +def parse_ini(path): + out, cur = {}, None + for line in open(path, errors='replace'): + s = line.strip() + m = re.match(r'^\[([\w-]+)\]$', s) + if m: cur = m.group(1); out[cur] = {}; continue + if cur and '=' in s and not s.startswith((';', '#')): + k, v = s.split('=', 1); out[cur][k.strip()] = v.strip() + return out + +def colitur_sanctoral(): + t = open(f'{COL}/data/ef/sanctoral.sexp').read() + out = {} + for m in re.finditer(r'\(\(slug ([\w-]+)\)(.*?)\(layer', t, re.S): + slug, blk = m.group(1), m.group(2) + g = lambda k: (re.search(r'\(' + k + r' (\w+)\)', blk) or [None, None])[1] + out[slug] = {'rank': g('rank'), 'status': g('status'), + 'colour': g('colour'), 'subject': g('subject')} + return out + +def colitur_lectionary(): + t = open(f'{COL}/data/ef/lectionary.sexp').read() + out = {} + for m in re.finditer(r'\(([a-z0-9-]+)\s*\(((?:\s*\(\(part \w+\) \(reference "[^"]*"\)\))+)\s*\)\)', t): + p = dict(re.findall(r'\(part (\w+)\) \(reference "([^"]*)"\)', m.group(2))) + out[m.group(1)] = {'first': p.get('First'), 'gospel': p.get('Gospel')} + return out + +RANKMAP = {'Class1': 'class-1', 'Class2': 'class-2', 'Class3': 'class-3', 'Class4': 'class-4'} +# why each field change is being made, keyed by slug +WHY = { + 'ubaldus': ('C38', 'Missal calendarium: "S. Ubaldi Ep. et Conf., III classis" -- a feast, not a commemoration'), + 'didacus': ('C38', 'Missal calendarium: "S. Didaci Conf., III classis" -- a feast, not a commemoration'), + 'vigil-of-st-lawrence': ('C37', 'RG 128: violet for vigils of II and III class outside Paschaltide'), + 'vigil-of-the-assumption': ('C37', 'RG 128: violet for vigils of II and III class outside Paschaltide'), + 'ef-advent-ember-sat': ('C27', 'lectio carries St Thomas the Apostle\'s Mass under this key; the real Advent Ember Saturday Mass is 2 Thess 2:1-8 / Luke 3:1-6'), + 'ef-september-ember-sat': ('C28', 'lectio carries St Matthew\'s Mass under this key; the real September Ember Saturday Mass is Heb 9:2-12 / Luke 13:6-17'), +} + +def main(): + lec_cal = parse_ini(f'{LEC}/tridentine-calendar.ini') + lec_lec = parse_ini(f'{LEC}/tridentine-lectionary.ini') + col_cal, col_lec = colitur_sanctoral(), colitur_lectionary() + changes = [] + for slug in sorted(set(lec_cal) & set(col_cal)): + l, c = lec_cal[slug], col_cal[slug] + want_rank = 'commemoration' if c['status'] == 'Commemoration_only' else RANKMAP.get(c['rank']) + if l.get('rank') != want_rank: + changes.append(('tridentine-calendar.ini', slug, 'rank', l.get('rank'), want_rank)) + if c['colour'] and l.get('colour', '').lower() != c['colour'].lower(): + changes.append(('tridentine-calendar.ini', slug, 'colour', l.get('colour'), c['colour'].lower())) + for slug in sorted(set(lec_lec) & set(col_lec)): + for k in ('first', 'gospel'): + if col_lec[slug].get(k) and lec_lec[slug].get(k) != col_lec[slug][k]: + changes.append(('tridentine-lectionary.ini', slug, k, lec_lec[slug].get(k), col_lec[slug][k])) + print(f"# {len(changes)} field changes bringing lectio's EF data into agreement with colitur") + print(f"# Generated by colitur tools/export_lectio_patch.py -- review, do not apply blind.\n") + cur = None + for f, slug, key, old, new in changes: + if f != cur: print(f"\n## {f}"); cur = f + cid, why = WHY.get(slug, ('?', 'no citation recorded -- INVESTIGATE before applying')) + print(f" [{slug}]") + print(f" {key}: {old!r} -> {new!r}") + print(f" why ({cid}): {why}") + only_col = sorted(set(col_lec) - set(lec_lec)) + print(f"\n## lectionary entries colitur has and lectio lacks: {len(only_col)}") + print(" (additions, not corrections -- listed for review, not emitted as a patch)") + for s in only_col[:8]: print(f" {s}") + if len(only_col) > 8: print(f" ... and {len(only_col)-8} more") + +main() |
