From 8d1eb25a311e16ddb7bf89d2b21fb1ca0aa058a7 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Mon, 17 Aug 2026 12:28:03 +0200 Subject: test(oracle): compare reading citations against missalemeum Layer 4 (test_oracle.ml) now extracts and compares the Epistle/Gospel citation for every one of the 730 days in the 2026-2027 fixture, the same axis Task 8 already added to layer 3. tools/ extract_missalemeum_oracle.py gained a structural citation-span detector (the first "*...*"-wrapped span under 40 chars containing a chapter:verse-shaped digit pair), verified exhaustively over all 1 458 Lectio/Evangelium sections. 728 of 730 days are comparable; the 2 that are not (Good Friday, both years) have no such section in missalemeum's own JSON at all -- a structural limit on the oracle's own side (M23), not a colitur gap. 451 of 730 days differ and are all explained across 19 cited entries; 0 unexplained. Chain step 4 (the Commons) has no layer-4 witness: confirmed empirically (a scratch sweep, not merely re-asserted from Task 6's own report) that none of the 15 Commons/proper saints is ever the observed office in this window. The comparison surfaced three genuine colitur bugs in data/ef/lectionary.sexp, all fixed at the source in tools/bootstrap_lectionary.ml: 1. Holy Name of Jesus (ef-holy-name-sunday/ef-holy-name) carried a mis-derived citation, borrowed by analogy from an unrelated ini section ("ef-christmas-sunday-0", the Sunday-within-the-Octave's own Mass) on the wrong assumption the two share a Mass. Disproved by missalemeum's own extraction for both real Holy Name Sundays in its window, confirmed against the Missal itself, twice, both scans (Acts 4:8-12/Luke 2:21, not Gal 4:1-7/Luke 2:33-40). 2. RG 17(a)'s own "diebus ferialibus" rubric (immediately following the Holy Name heading, both scans) was never implemented: 2-5 January ferias repeat 1 January's Mass, 7-12 January ferias before Holy Family Sunday repeat Epiphany's, and Holy Family's own week has a named formula ("Missa Dominicae Primae post Epiphaniam"). All three fell to step 3's undifferentiated Sunday walkback instead. Fixed for three 5-weekday families, deliberately excluding Saturday from all three -- RG 78's BVM Saturday Office wins there unconditionally, confirmed against missalemeum's own title on this window's own such Saturdays. 3. Corpus Christi and Sacred Heart (both real, never-impeded I-class movable feasts) had no lectionary entry at all. Both engines independently fell back to Trinity Sunday's own citation and silently agreed on the wrong answer -- the "Holy Thursday violet in both" shape CLAUDE.md names for exactly this reason. Because lectionary.sexp is shared with layer 3 (the lectio differential, 2005-2050), these fixes changed colitur's output for the whole domain, not only 2026-2027. data/ef/expected-divergences.sexp and test_differential.ml needed matching updates: C1 narrowed 361->173 and C25 narrowed 57->13 (rows that now resolve correctly moved to new entries), C16's note corrected (count unchanged), and six new entries added (C29-C34, 397 rows total, all verdict colitur). test_lectionary.ml and test_lectionary_ef.ml's own pins were updated to match (entry count 131->148; one step-3 regression test re-pointed to a date that still genuinely exercises that step). One entry, M26 (30 rows, verdict open), is a genuine unfixed gap: colitur's step-3 "resume the preceding Sunday" fallback is its only ferial-Mass mechanism, and missalemeum's own selection is more granular in three confirmed ways (a rotating votive Mass of the BVM, a named weekly formula for at least one week, and later Time-after-Pentecost weeks this task's own investigation did not narrow to a single rule) -- the same "needs a season-keyed selection mechanism, new behaviour not new data" shape data/ef/expected-divergences.sexp's own C6/C25 and Task 6's own residual concern already name, out of this task's own scope to build. dune test: 358 tests green. COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force: 359 tests green, 89.5s. --- tools/bootstrap_lectionary.ml | 248 ++++++++++++++++++++++++++++-------- tools/extract_missalemeum_oracle.py | 58 ++++++++- 2 files changed, 255 insertions(+), 51 deletions(-) (limited to 'tools') diff --git a/tools/bootstrap_lectionary.ml b/tools/bootstrap_lectionary.ml index 670130d..357cb81 100644 --- a/tools/bootstrap_lectionary.ml +++ b/tools/bootstrap_lectionary.ml @@ -91,25 +91,27 @@ let weekdays = [ "monday"; "tuesday"; "wednesday"; "thursday"; "friday"; "saturd let colitur_keys name = if String.equal name "ef-christmas-sunday-0" then - (* WIDEN, not rename -- fix round (found by test_lectionary_ef.ml's own - pre-existing test_step3_christmas_feria_resumes_sunday going from - PASS to FAIL): "ef-christmas-sunday-0" is not only lectio's own - calendar-level slug for RG 17(a)'s Holy Name Sunday - (test_differential.ml's own [norm_slug] comment) -- it is ALSO - colitur's own GENUINE, DIRECTLY-COMPUTED temporal slug (Rite_ef. - Temporal_ef.temporal, "if m = 12 && dd >= 26 then Some - \"ef-christmas-sunday-0\"") for an ordinary Sunday landing on 26-28 - December, the historical "Sunday within the Octave of the Nativity" - -- a DIFFERENT civil window than Holy Name Sunday (2-5 January), and - Missal-confirmed (docs/research/scan1.txt, "Dominica infra octavam - Nativitatis Domini, II classis") to be the IDENTICAL Mass (Gal. - 4,1-7 / Luc. 2,33-40), not a coincidence. The first version of this - translation REPLACED the key outright and broke that pre-existing, - working case -- caught by the existing test suite, not by this - generator's own reasoning; keeping the original key alongside the - Holy-Name-Sunday alias fixes it, following the same "widen, never - narrow" rule this table's own general design already intends. *) - [ "ef-christmas-sunday-0"; "ef-holy-name-sunday" ] + (* Task 9 (branch ef-lectionary, layer-4 oracle): CORRECTED -- this + branch used to ALSO widen into "ef-holy-name-sunday" (see git + history for the removed paragraph's own reasoning), on the claim + that RG 17(a)'s Holy Name Sunday shares the IDENTICAL Mass with the + historical "Sunday within the Octave of the Nativity" this ini + section actually carries (Gal. 4,1-7 / Luc. 2,33-40). WRONG, + DISPROVED by an INDEPENDENT primary source once this task built a + real oracle comparison against it: missalemeum's own extraction for + 2026-01-04/2027-01-03 (both real Holy Name Sundays) shows Acts + 4:8-12 / Luke 2:21 instead, and docs/research/scan1.txt AND scan2.txt + both confirm it, twice over, word for word ("SANCTISSIMI NOMINIS + IESU, II classis... Lectio Actuum Apostolorum. Act. 4, 8-12... + Sequentia sancti Evangelii secundum Lucam. Luc. 2, 21", + scan1.txt:6579-6608, scan2.txt:7225-7297) -- a DIFFERENT, DISTINCT + Mass from the Sunday-within-the-Octave's own (still correctly + carried by THIS key, "ef-christmas-sunday-0", unaffected by this + correction). The widen is removed; Holy Name Sunday's own citation + is now hand-authored directly, [holy_name_entries] below, sourced + from the Missal rather than mis-derived from an unrelated ini + section by analogy. *) + [ "ef-christmas-sunday-0" ] else if String.equal name "ef-easter-8-wednesday" then [ "ef-pentecost-ember-wed" ] else if String.equal name "ef-easter-8-friday" then [ "ef-pentecost-ember-fri" ] else if String.equal name "ef-easter-8-saturday" then [ "ef-pentecost-ember-sat" ] @@ -440,36 +442,180 @@ let nativity_octave_entries = let cs = pair ~first:"Titus 3:4-7" ~gospel:"Luke 2:15-20" in List.map (fun n -> (slug_or_die (Printf.sprintf "ef-nativity-octave-day-%d" n), cs)) [ 5; 6; 7 ] -(* One more colitur-only slug, DERIVED from [ef-holy-name-sunday]'s own - citations (just translated above) rather than a second hand-typed copy - of the same text: [ef-holy-name] is RG 17(a)'s own fallback ("secus die - 2 ianuarii", 2 January in a year with no Sunday 2-5 January). The - calendarium's own table (data/ef/expected-divergences.sexp's own C16 - note quotes it) gives ONE heading for both the Sunday and the fallback - shape -- the identical Mass, not two -- so this is the same citation - pair, not an independent lookup. Safe to hard-wire (unlike a step-3 - fallback, which would walk back into a DIFFERENT, unrelated Sunday of - the OLD liturgical year): RG 17(a)'s whole point is that this Mass is - said in place of, not alongside, whatever ferial reading a bare - fallback would otherwise find. +(* Task 9 (branch ef-lectionary, layer-4 oracle): [ef-holy-name-sunday] and + [ef-holy-name] (RG 17(a)'s own 2-January fallback, "secus die 2 + ianuarii") HAND-AUTHORED directly from the Missal, replacing the + PREVIOUS mechanism -- deriving [ef-holy-name] from + [ef-holy-name-sunday]'s own (then wrongly-widened) ini-translated + citation, itself borrowed from an unrelated ini section by analogy (see + [colitur_keys]'s own "ef-christmas-sunday-0" branch, CORRECTED note, + for the full account of the error this replaces). Both slugs get the + SAME citation pair -- the calendarium's own table (scan1.txt/scan2.txt, + same headings cited above) gives ONE Mass heading covering both the + Sunday and the 2-January fallback shape, not two -- so this is still + one hand-typed value, not a second independent lookup, the same + discipline the removed [derived_entries] intended, just sourced + correctly this time. [ef-nativity-octave-day-{5,6,7}] does NOT reuse this value -- a first attempt tried exactly that and was measured wrong and reverted; see [nativity_octave_entries] above for the correct, DIFFERENT, directly Missal-sourced formulary and the full account of why the two headings differ. *) -let derived_entries entries = - let holy_name_sunday_citations = - match - List.find_opt (fun (s, _) -> String.equal (Slug.to_string s) "ef-holy-name-sunday") entries - with - | Some (_, cs) -> cs - | None -> - die "internal: ef-holy-name-sunday missing after translation -- cannot derive its dependants" - in - match Slug.of_string "ef-holy-name" with - | Ok s -> [ (s, holy_name_sunday_citations) ] - | Error e -> die "%s" e +let holy_name_citations = pair ~first:"Acts 4:8-12" ~gospel:"Luke 2:21" + +let holy_name_entries = + [ (slug_or_die "ef-holy-name-sunday", holy_name_citations); (slug_or_die "ef-holy-name", holy_name_citations) ] + +(* Task 9 (branch ef-lectionary, layer-4 oracle): three more colitur-only + slug families, all in the same 2-13 January stretch as + [holy_name_entries] just above and found the same way -- missalemeum's + own oracle disagreeing with colitur's step-3 fallback answer on every + civil day these slugs cover, traced back to an EXPLICIT primary-source + rubric each time, not a guess. All three ranges and their own slug + families are documented, independently, in + {!Rite_ef.Temporal_ef.christmastide_feria_slug}'s own comment (that + function's docstring gives the exact civil-date boundaries this + generator relies on here; not re-derived, read directly from the code + that computes them). + + - [ef-christmas-1-{monday,tuesday,wednesday,thursday,friday}] (2-5 + January, EXCLUDING Saturday -- see below): RG 17(a)'s OWN rubric, + immediately following the Holy-Name-Sunday heading, both scans, word + for word: "Diebus ferialibus a 2 ad 5 ianuarii Missa dicitur ut die 1 + ianuarii" (scan1.txt:6522-6526) / "Diebus ferialibus a 2 ad 6 + ianuarii Missa dicitur ut die 1 ianuarii" (scan2.txt, the "6" a + one-digit OCR variant of scan1's "5" -- immaterial either way, since + 6 January is always Epiphany, a fixed I-class feast outranking any + ferial rubric outright) -- ON FERIAL DAYS from 2 to 5 January, THE + SAME MASS AS 1 JANUARY (the Circumcision) IS SAID. + - [ef-christmas-2-{monday,tuesday,wednesday,thursday,friday}] (7 + January through the day before Holy Family Sunday's own civil date, + EXCLUDING Saturday -- see below): the SAME rubric's own next + sentence, both scans, word for word: "Diebus ferialibus a 7 ad 12 + ianuarii ANTE dominicam I post Epiphaniam occurrentibus, dicitur + Missa de festo Epiphaniae" (scan1.txt:6753-6757, scan2.txt + corroborates) -- ON FERIAL DAYS from 7 to 12 January occurring BEFORE + the first Sunday after Epiphany (Holy Family), THE MASS OF THE FEAST + OF EPIPHANY IS SAID. + - [ef-time-after-epiphany-1-{monday,tuesday,wednesday,thursday,friday}] + (Holy Family Sunday's own week, EXCLUDING Saturday -- see below, + straddling the Christmastide/Time-after-Epiphany season boundary on + 14 January without changing slug -- {!christmastide_feria_slug}'s + own comment: "it is the SAME COMPUTATION the ordinary + Time-after-Epiphany ferial fallback... will give the rest of that + same Sunday-to-Saturday week", so one hand-authored value covers the + whole week regardless of which code path computed the slug for a + given civil day): the rubric's own THIRD sentence, same citation, a + named formula rather than a second "repeat the Sunday" instruction: + "post eandem dominicam vero Missa de eadem dominica In excelso + throno, ut infra... Haec Missa dicenda est diebus ferialibus huius + hebdomadae" (scan1.txt:6758-6851, the formula's own heading "MISSA + DOMINICAE PRIMAE post Epiphaniam" immediately below) -- Epistle + "Lectio Epistolae beati Pauli Apostoli ad Romanos. Rom. 12, 1-5" + (scan1.txt:6875-6877), Gospel the SAME pericope as Holy Family's own + Sunday Mass, Luke 2:42-52 (printed out in full immediately below the + Epistle, scan1.txt:6885-6905) -- a DIFFERENT Epistle from Holy Family + Sunday's own (Col 3:12-17), but the SAME Gospel, exactly as + missalemeum's own fixture rows show (2026-01-12/2027-01-11/ + 2027-01-12: Gospel already agreed before this fix, only the Epistle + diverged). + - 13 January's OWN separate case, checked and found to need NO entry + here: when Holy Family does not fall there, 13 January carries a + II-class sanctoral commemoration of its own ("Commemoration of the + Baptism of the Lord", data/ef/sanctoral.sexp's own + [commemoration-of-the-baptism-of-the-lord], already carrying its own + citations, Isa 60:1-6 / John 1:29-34, matching missalemeum exactly) + -- {!Rite_ef.Lectionary_ef.readings}'s own step 1 (the OBSERVED + celebration's own proper) resolves it before step 2 (this + generator's own data) is ever consulted, verified directly against + the real resolver (tools/dump_dates.ml, a scratch diagnostic, not + committed): 2026-01-13/2027-01-13 both already show the correct + citation with none of the entries below in play. + + SATURDAY IS EXCLUDED FROM ALL THREE FAMILIES, deliberately, not an + oversight, and NOT (as an earlier draft of this comment argued for the + latter two) safe to include: RG 78 ("In sabbatis, in quibus occurrit + Officium de feria IV classis, fit de sancta Maria in sabbato", already + built, Rite_ef.Temporal_ef's own [bvm_saturday_names]) makes ANY + otherwise-unoccupied IV-class Saturday the Saturday Office of the BVM + instead of a plain feria -- UNCONDITIONALLY, not merely when nothing + else happens to compete that day (Precedence_ef's own [band] gives it + this ranking whenever the day's own office would otherwise be a plain + Class4 feria, checked in {!Rite_ef.Temporal_ef}'s own [bvm_saturday_names] + comment and confirmed against the real resolver here, not assumed). + This window's own five otherwise-unoccupied Saturdays inside these + three families (2026-01-03, 2026-01-10, 2027-01-09; 2026-01-17 and + 2027-01-16 are won outright by a saint instead, Anthony and Marcellus I + respectively, whose own citations are unaffected either way) genuinely + ARE the BVM Office, missalemeum's own title confirming it on every one + that reaches the office at all ("II Mass of the B. V. M. -- Vultum + Tuum", never "Feria") -- so none of the three "repeat X" rubrics above + ever actually apply on a Saturday. The three [-saturday] slugs are + DELIBERATELY left unentered, all three: the BVM Saturday Office's own + citation is a genuine, ALREADY-DOCUMENTED open item (register, Task 6's + own residual concern 4, "the BVM Saturday Office still emits its + feria's Mass") this task does not build, out of scope here the same + way it was there. (CORRECTED from this comment's own first draft, + which excluded only [ef-christmas-1-saturday] and reasoned -- without + checking -- that the other two families' own Saturdays were safe to + include: measured directly against the real resolver and missalemeum's + own fixture, they were not; the same RG 78 mechanism governs all + three windows identically.) *) +let non_saturday_weekdays = [ "monday"; "tuesday"; "wednesday"; "thursday"; "friday" ] + +let epiphanytide_weekday_citations = + [ (`Christmas1, non_saturday_weekdays, pair ~first:"Titus 2:11-15" ~gospel:"Luke 2:21"); + (`Christmas2, non_saturday_weekdays, pair ~first:"Isa 60:1-6" ~gospel:"Matt 2:1-12"); + (`TimeAfterEpiphany1, non_saturday_weekdays, pair ~first:"Rom 12:1-5" ~gospel:"Luke 2:42-52") + ] + +let epiphanytide_opening_entries = + List.concat_map + (fun (family, wds, cs) -> + let prefix = + match family with + | `Christmas1 -> "ef-christmas-1-" + | `Christmas2 -> "ef-christmas-2-" + | `TimeAfterEpiphany1 -> "ef-time-after-epiphany-1-" + in + List.map (fun wd -> (slug_or_die (prefix ^ wd), cs)) wds) + epiphanytide_weekday_citations + +(* Task 9 (branch ef-lectionary, layer-4 oracle): two major, permanently- + named I-class movable feasts of the Lord that had NO lectionary entry + at all until now -- not a renamed/mis-widened value like + [holy_name_entries] above, a genuine ABSENCE. Both slugs are real, + directly-computed [Temporal_ef] offices (temporal_ef.ml's own [named] + function, "ef-corpus-christi"/"ef-sacred-heart"), so every occurrence + fell through to step 3's ferial resumption (Trinity Sunday's own + citation, repeated) instead of the feast's own Mass -- invisible before + this task made citation CONTENT comparable at all, the identical shape + Task 8's own [colitur_keys] fixes already found for renamed slug + families, here for a slug that was never translated from anywhere + because lectio's own EF ini has no equivalent section for either feast. + Both citations verified against BOTH scans, word for word: + + - [ef-corpus-christi] ("IN FESTO SSMI CORPORIS CHRISTI, I classis"): + 1 Cor 11:23-29 / John 6:56-59 -- scan1.txt:21867-21917 ("Lectio + Epistolae beati Pauli Apostoli ad Corinthios. 1 Cor. 11, 23-29" / + "...secundum Ioannem. Io. 6, 56-59"), scan2.txt:24084-24172 + corroborates both citations. + - [ef-sacred-heart] ("IN FESTO SACRATISSIMI CORDIS IESU, I classis"): + Eph 3:8-12, 14-19 / John 19:31-37 -- scan1.txt:22030-22095 ("...ad + Ephesios. Ephes. 3, 8-12 et 14-19" / "...secundum Ioannem. Io. 19, + 31-37"), scan2.txt:24284-24336 corroborates the heading and both + citations (the Epistle's own "et 14-19" clause is OCR-truncated to + "3, 8-19" on this second scan alone, its printed body text otherwise + matching word for word; scan1's own clean transcription is + unambiguous, so this is a scan-quality gap, not a genuine + disagreement between the two printings). Both citations independently + corroborated by missalemeum's own extraction too (test/fixtures/ + missalemeum-ef-2026-2027.txt, 2026-06-04 and 2026-06-12/2027-06-04). *) +let movable_feast_entries = + [ (slug_or_die "ef-corpus-christi", pair ~first:"1 Cor 11:23-29" ~gospel:"John 6:56-59"); + (slug_or_die "ef-sacred-heart", pair ~first:"Eph 3:8-12, 14-19" ~gospel:"John 19:31-37") + ] let sha256 path = let ic = Unix.open_process_in (Printf.sprintf "sha256sum %s" (Filename.quote path)) in @@ -574,7 +720,8 @@ let () = let translated = List.concat_map convert secs in let entries = translated @ vigil_entries @ holy_week_entries @ passion_tuesday_entry - @ ember_saturday_corrections @ nativity_octave_entries @ derived_entries translated + @ ember_saturday_corrections @ nativity_octave_entries @ holy_name_entries + @ epiphanytide_opening_entries @ movable_feast_entries in assert_reachable entries; let lect = @@ -603,16 +750,17 @@ let () = ; Missal, one entry [passion_tuesday_entry]; %d hand-authored directly\n\ ; from the Missal, two entries [ember_saturday_corrections]; %d\n\ ; hand-authored directly from the Missal [nativity_octave_entries];\n\ - ; %d derived from an already-translated entry above rather than\n\ - ; re-typed [derived_entries] -- see this generator's own comments on\n\ - ; all seven). Regenerate with:\n\ + ; %d hand-authored directly from the Missal, two entries\n\ + ; [holy_name_entries]; %d hand-authored directly from the Missal\n\ + ; [epiphanytide_opening_entries]; %d hand-authored directly from the\n\ + ; Missal, two entries [movable_feast_entries] -- see this generator's\n\ + ; own comments on all nine). Regenerate with:\n\ ; eval $(opam env) && dune exec tools/bootstrap_lectionary.exe -- %s %s\n" src (sha256 src) (List.length entries) (List.length secs) (List.length vigil_entries) (List.length holy_week_entries) (List.length passion_tuesday_entry) (List.length ember_saturday_corrections) (List.length nativity_octave_entries) - (List.length entries - List.length translated - List.length vigil_entries - - List.length holy_week_entries - List.length passion_tuesday_entry - - List.length ember_saturday_corrections - List.length nativity_octave_entries) + (List.length holy_name_entries) (List.length epiphanytide_opening_entries) + (List.length movable_feast_entries) src dst; Sexplib.Sexp.output_hum oc (Lectionary.sexp_of_t lect); output_char oc '\n'; diff --git a/tools/extract_missalemeum_oracle.py b/tools/extract_missalemeum_oracle.py index a010b52..4b010e2 100644 --- a/tools/extract_missalemeum_oracle.py +++ b/tools/extract_missalemeum_oracle.py @@ -18,7 +18,7 @@ # exists for every day. # # One line per day, pipe-separated: -# date|rank|colors|title|tempora|commemorations|displaced|n_masses|commemoration_ids +# date|rank|colors|title|tempora|commemorations|displaced|n_masses|commemoration_ids|first|gospel # # - rank/colors/title/tempora/commemorations/displaced are info.rank, # info.colors (sorted, concatenated, e.g. "pv"), info.title, info.tempora @@ -42,6 +42,35 @@ # is ever commemorated there). Never parsed for its date component by this # fixture or the comparator (info.rank/colors already give the day's own # values); kept opaque and compared as a plain string. +# - first/gospel (Task 9) are the day's own Epistle/Lesson and Gospel +# citations, extracted from entry[0]["sections"] -- the section with +# id "Lectio" ("Epistle") and "Evangelium" ("Gospel") respectively. Each +# section's own body is a [[english, latin]] pair; the ENGLISH text +# (body[0][0]) embeds the citation as one of possibly several +# "*...*"-wrapped (markdown-italic) spans -- e.g. "Lesson from the letter +# of St. Paul... \n*Titus 2:11-15*\nBeloved: ...". It is NOT always the +# FIRST such span: Holy Saturday's own Gospel section opens with a rubric +# note, itself "*"-wrapped ("*While singing the Gospel candles are not +# being hold.*"), before the real citation "*Matt 28:1-7*" -- checked +# directly against the raw JSON, not assumed. The citation span is +# identified structurally instead of positionally: the first "*...*" span +# under 40 characters that contains a chapter:verse-shaped digit pair +# (`\d+\s*[:,.]\s*\d+`, matching all three separators actually used in +# this source -- "Titus 2:11-15", "4 Kings, 5:1-15", "John 20. 19-31"). +# Verified exhaustively over the whole fixture (all 1 458 Lectio/ +# Evangelium sections across the 728 days that carry one): exactly one +# candidate per section, zero ambiguous, zero false positives from a +# longer rubric sentence. TWO days (Good Friday, both years -- the "Missa +# Praesanctificatorum" liturgy) have no "Lectio"/"Evangelium" section at +# all (a multi-lesson structure instead, "Lectiones"/"Passio", with no +# single reading occupying the Epistle/Gospel slot this schema assumes, +# the SAME shape colitur's own test_lectionary.ml records for its own +# hand-authored Holy Week data) -- "-" for both fields on those two rows, +# a WARNING on stderr naming the date, never a silent guess. Extracted +# VERBATIM (only .strip()ped of surrounding whitespace) -- not normalized, +# not re-punctuated: the comparator's job, not this extractor's, matching +# the same "verbatim, not translated or slugified" discipline the fields +# above already state. # - Spaces in tempora are turned to "_" (matching the "no field has an # internal space" convention test/fixtures/lectio-ef-2005-2050.txt already # uses, so this fixture can be read the same simple way -- split on '|', @@ -50,8 +79,27 @@ # pass-through display, is space-collapsed for a cheap column-count check). import json import os +import re import sys +STAR_RE = re.compile(r"\*([^*]+)\*") +VERSE_RE = re.compile(r"\d+\s*[:,.]\s*\d+") + + +def extract_citation(sections, section_id, date, label): + for sec in sections: + if sec["id"] != section_id: + continue + text = sec["body"][0][0] + candidates = [s.strip() for s in STAR_RE.findall(text) if VERSE_RE.search(s) and len(s) < 40] + if candidates: + return candidates[0] + print(f"WARNING: {date}: {section_id!r} section found but no citation-shaped span in it ({label})", + file=sys.stderr) + return "-" + print(f"WARNING: {date}: no {section_id!r} section ({label})", file=sys.stderr) + return "-" + def main(): if len(sys.argv) != 3: @@ -84,8 +132,16 @@ def main(): comms = ";".join(comm_titles) or "-" disp = ";".join(disp_titles) or "-" ids = ";".join(comm_ids) or "-" + sections = data[0]["sections"] + cit_first = extract_citation(sections, "Lectio", date, "Epistle") + cit_gospel = extract_citation(sections, "Evangelium", date, "Gospel") + for t in (cit_first, cit_gospel): + if "|" in t: + print(f"ERROR: {date}: citation {t!r} contains a delimiter this fixture uses", file=sys.stderr) + return 1 rows.append( f"{date}|{first['rank']}|{colors}|{first['title']}|{tempora}|{comms}|{disp}|{len(data)}|{ids}" + f"|{cit_first}|{cit_gospel}" ) if len(rows) != 730: -- cgit v1.3