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
|
#!/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()
|