diff options
| -rw-r--r-- | NOTICE | 8 | ||||
| -rw-r--r-- | internal/caldata/caldata_test.go | 26 | ||||
| -rw-r--r-- | internal/caldata/roman-calendar.ini | 134 | ||||
| -rw-r--r-- | internal/config/config.go | 2 | ||||
| -rw-r--r-- | scripts/gen-of-sanctoral-readings.py | 136 |
5 files changed, 302 insertions, 4 deletions
@@ -27,9 +27,11 @@ internal/caldata/tridentine-lectionary.ini (Extraordinary Form) and internal/caldata/of-lectionary.ini (Ordinary Form) were compiled from missalemeum.com (Divinum Officium data, EF), niedziela.pl (OF Sundays), and catholic-resources.org (Fr. Felix Just, S.J. — the Ordinary Form weekday/ferial -lectionary of Ordinary Time, scripts/genlect-of-cr.py). Only the references were -taken; the reading TEXT is rendered from the public-domain corpora below — no -copyrighted translation is redistributed. +lectionary of Ordinary Time via scripts/genlect-of-cr.py, and the proper readings +of the OF sanctoral — feasts, solemnities and proper-reading memorials — +inlined into roman-calendar.ini via scripts/gen-of-sanctoral-readings.py). Only +the references were taken; the reading TEXT is rendered from the public-domain +corpora below — no copyrighted translation is redistributed. OF sanctoral calendar --------------------- diff --git a/internal/caldata/caldata_test.go b/internal/caldata/caldata_test.go index 5ffa57e..0d6616e 100644 --- a/internal/caldata/caldata_test.go +++ b/internal/caldata/caldata_test.go @@ -1,6 +1,7 @@ package caldata import ( + "strings" "testing" "github.com/lukaszkasprzak/lectio/internal/calendar" @@ -30,6 +31,31 @@ func TestUniversalLoads(t *testing.T) { } } +func TestUniversalSanctoralReadings(t *testing.T) { + l := Universal() + // A feast/solemnity or a PROPER-reading memorial carries its own inline Mass + // (imported from catholic-resources.org) rather than falling back to the ferial. + want := map[string][2]string{ // slug -> {reading.first substr, reading.gospel substr} + "barnabas-the-apostle": {"Acts 11", "Matthew 10:7-13"}, + "assumption-of-the-blessed-virgin-mary": {"Revelation 11", "Luke 1:39-56"}, + "our-lady-of-sorrows": {"Hebrews 5", "John 19:25-27"}, + "all-saints": {"Revelation 7", "Matthew 5:1-12"}, + } + for slug, w := range want { + rc, ok := l.Cels[slug] + if !ok { + t.Errorf("missing %q", slug) + continue + } + if got := rc.Fields["reading.first"]; !strings.Contains(got, w[0]) { + t.Errorf("%s: reading.first = %q, want containing %q", slug, got, w[0]) + } + if got := rc.Fields["reading.gospel"]; !strings.Contains(got, w[1]) { + t.Errorf("%s: reading.gospel = %q, want containing %q", slug, got, w[1]) + } + } +} + func TestTridentineLoads(t *testing.T) { l := Tridentine() if l.ID != "tridentine" { diff --git a/internal/caldata/roman-calendar.ini b/internal/caldata/roman-calendar.ini index 63fd703..ccffa64 100644 --- a/internal/caldata/roman-calendar.ini +++ b/internal/caldata/roman-calendar.ini @@ -35,6 +35,9 @@ class = lord colour = white name.en = The Most Holy Name of Jesus name.la = Ss.mi Nominis Iesu +reading.first = Philippians 2:1-11 +reading.psalm = Psalms 8:4-5,6-7,8-9 +reading.gospel = Luke 2:21-24 [raymond-of-penyafort-priest] date = 01-07 @@ -111,6 +114,9 @@ colour = white name.en = The Conversion of Saint Paul, apostle name.pl = Nawrócenia św. Pawła Apostoła name.la = In conversione S. Pauli, apostoli +reading.first = The Acts 22:3-16 +reading.psalm = Psalms 117:1bc,2 +reading.gospel = Mark 16:15-18 [saints-timothy-and-titus-bishops] date = 01-26 @@ -119,6 +125,9 @@ class = saint colour = white name.en = Saints Timothy and Titus, bishops name.la = Ss. Timothei et Titi, episcoporum +reading.first = 2 Timothy 1:1-8 +reading.psalm = Psalms 96:1-2a,2b-3,7-8a,10 +reading.gospel = Luke 10:1-9 [angela-merici-virgin] date = 01-27 @@ -153,6 +162,10 @@ class = lord colour = white name.en = Presentation of the Lord name.la = IN PRAESENTATIONE DOMINI +reading.first = Malachi 3:1-4 +reading.psalm = Psalms 24:7,8,9,10 +reading.second = Hebrews 2:14-18 +reading.gospel = Luke 2:22-40 [ansgar-bishop] date = 02-03 @@ -254,6 +267,9 @@ colour = white name.en = Chair of Saint Peter, apostle name.pl = katedry św. Piotra Apostoła name.la = Cathedrae S. Petri, apostoli +reading.first = 1 Peter 5:1-4 +reading.psalm = Psalms 23:1-3a,4,5,6 +reading.gospel = Matthew 16:13-19 [polycarp-bishop-and-martyr] date = 02-23 @@ -319,6 +335,10 @@ colour = white name.en = Saint Joseph Husband of the Blessed Virgin Mary name.pl = św. Józefa, Oblubieńca Najświętszej Maryi Panny name.la = S. IOSEPH, SPONSI B. M. V. +reading.first = 2 Samuel 7:4-5a,12-14a,16 +reading.psalm = Psalms 89:2-3,4-5,27+29 +reading.second = Romans 4:13,16-18,22 +reading.gospel = Matthew 1:16,18-21,24a [turibius-of-mogrovejo-bishop] date = 03-23 @@ -336,6 +356,10 @@ colour = white name.en = Annunciation of the Lord name.pl = Zwiastowania Pańskiego name.la = IN ANNUNTIATIONE DOMINI +reading.first = Isaiah 7:10-14;8:10 +reading.psalm = Psalms 40:7-8a,8b-9,10,11 +reading.second = Hebrews 10:4-10 +reading.gospel = Luke 1:26-38 [francis-of-paola-hermit] date = 04-02 @@ -424,6 +448,9 @@ class = saint colour = red name.en = Saint Mark the Evangelist name.la = S. Marci, evangelistae +reading.first = 1 Peter 5:5b-14 +reading.psalm = Psalms 89:2-3,6-7,16-17 +reading.gospel = Mark 16:15-20 [louis-grignon-de-montfort-priest] date = 04-28 @@ -465,6 +492,9 @@ class = saint colour = white name.en = Saint Joseph the Worker name.la = S. Ioseph Opificis +reading.first = Genesis 1:26-2:3 +reading.psalm = Psalms 90:2,3-4,12-13,14+16 +reading.gospel = Matthew 13:54-58 [athanasius-bishop-and-doctor] date = 05-02 @@ -483,6 +513,9 @@ colour = red name.en = Saints Philip and James, Apostles name.pl = Najświętszej Maryi Panny, Królowej Polski name.la = Ss. Philippi et Iacobi, apostolorum +reading.first = 1 Corinthians 15:1-8 +reading.psalm = Psalms 19:2-3,4-5 +reading.gospel = John 14:6-14 [saints-nereus-and-achilleus-martyrs] date = 05-12 @@ -516,6 +549,9 @@ colour = red name.en = Saint Matthias the Apostle name.pl = św. Macieja, apostoła name.la = S. Matthiae, apostoli +reading.first = The Acts 1:15-17,20-26 +reading.psalm = Psalms 113:1-2,3-4,5-6,7-8 +reading.gospel = John 15:9-17 [john-i-pope-and-martyr] date = 05-18 @@ -598,6 +634,9 @@ colour = white name.en = Visitation of the Blessed Virgin Mary name.pl = Nawiedzenia Najświętszej Maryi Panny name.la = In Visitatione B. Mariae Virginis +reading.first = Zephaniah 3:14-18a +reading.psalm = Isaiah 12:2-3,4bcd,5-6 +reading.gospel = Luke 1:39-56 [justin-martyr] date = 06-01 @@ -657,6 +696,9 @@ colour = red name.en = Saint Barnabas the Apostle name.pl = św. Barnaby, apostoła name.la = S. Barnabae, apostoli +reading.first = The Acts 11:21b-26;13:1-3 +reading.psalm = Psalms 98:1,2-3ab,3cd-4,5-6 +reading.gospel = Matthew 10:7-13 [anthony-of-padua-priest-and-doctor] date = 06-13 @@ -708,6 +750,10 @@ colour = white name.en = Birth of Saint John the Baptist name.pl = narodzenia św. Jana Chrzciciela name.la = IN NATIVITATE S. IOANNIS BAPTISTAE +reading.first = Isaiah 49:1-6 +reading.psalm = Psalms 139:1b-3,13-14ab,14c-15 +reading.second = The Acts 13:22-26 +reading.gospel = Luke 1:57-66,80 [cyril-of-alexandria-bishop-and-doctor] date = 06-27 @@ -732,6 +778,10 @@ class = saint colour = red name.en = Saints Peter and Paul, Apostles name.la = Ss. PETRI ET PAULI, APOSTOLORUM +reading.first = The Acts 12:1-11 +reading.psalm = Psalms 34:2-3,4-5,6-7,8-9 +reading.second = 2 Timothy 4:6-8,17-18 +reading.gospel = Matthew 16:13-19 [first-martyrs-of-the-church-of-rome] date = 06-30 @@ -749,6 +799,9 @@ colour = red name.en = Saint Thomas the Apostle name.pl = św. Tomasza, apostoła name.la = S. Thomae, apostoli +reading.first = Ephesians 2:19-22 +reading.psalm = Psalms 117:1bc,2 +reading.gospel = John 20:24-29 [elizabeth-of-portugal] date = 07-04 @@ -848,6 +901,9 @@ colour = white name.en = Saint Mary Magdalene name.pl = św. Marii Magdaleny name.la = S. Mariae Magdalenae +reading.first = Song of Solomon 3:1-4b +reading.psalm = Psalms 63:2,3-4,5-6,8-9 +reading.gospel = John 20:1-2,11-18 [birgitta-religious] date = 07-23 @@ -873,6 +929,9 @@ colour = red name.en = Saint James, apostle name.pl = św. Jakuba, apostoła name.la = S. Iacobi, apostoli +reading.first = 2 Corinthians 4:7-15 +reading.psalm = Psalms 126:1bc-2ab,2cd-3,4-5,6 +reading.gospel = Matthew 20:20-28 [saints-joachim-and-anne] date = 07-26 @@ -882,6 +941,9 @@ colour = white name.en = Saints Joachim and Anne name.pl = św. Joachima i Anny, rodziców Najświętszej Maryi Panny name.la = Ss. Ioachim et Annae, parentum B. Mariae Virginis +reading.first = Sirach 44:1,10-15 +reading.psalm = Psalms 132:11,13-14,17-18 +reading.gospel = Matthew 13:16-17 [martha] date = 07-29 @@ -891,6 +953,9 @@ colour = red name.en = Saint Martha name.pl = św. Marty, Marii i Łazarza name.la = S. Marthae +reading.first = 1 John 4:7-16 +reading.psalm = Psalms 34:2-3,4-5,6-7,8-9,10-11 +reading.gospel = John 11:19-27 [peter-chrysologus-bishop-and-doctor] date = 07-30 @@ -959,6 +1024,10 @@ colour = white name.en = Transfiguration of the Lord name.pl = Przemienienia Pańskiego name.la = IN TRANSFIGURATIONE DOMINI +reading.first = Daniel 7:9-10,13-14 +reading.psalm = Psalms 97:1-2,5-6,9 +reading.second = 2 Peter 1:16-19 +reading.gospel = Matthew 17:1-9 [sixtus-ii-pope-and-companions-martyrs] date = 08-07 @@ -1000,6 +1069,9 @@ class = saint colour = red name.en = Saint Lawrence, deacon and martyr name.la = S. Laurentii, diaconi et martyris +reading.first = 2 Corinthians 9:6-10 +reading.psalm = Psalms 112:1-2,5-6,7-8,9 +reading.gospel = John 12:24-26 [clare-virgin] date = 08-11 @@ -1043,6 +1115,10 @@ colour = white name.en = Assumption of the Blessed Virgin Mary name.pl = Wniebowzięcia Najświętszej Maryi Panny name.la = IN ASSUMPTIONE B. MARIAE VIRGINIS +reading.first = Revelation 11:19a;12:1-6a,10ab +reading.psalm = Psalms 45:10,11,12,16 +reading.second = 1 Corinthians 15:20-27 +reading.gospel = Luke 1:39-56 [stephen-of-hungary] date = 08-16 @@ -1102,6 +1178,9 @@ class = saint colour = red name.en = Saint Bartholomew the Apostle name.la = S. Bartholomaei, apostoli +reading.first = Revelation 21:9b-14 +reading.psalm = Psalms 145:10-11,12-13,17-18 +reading.gospel = John 1:45-51 [joseph-of-calasanz-priest] date = 08-25 @@ -1145,6 +1224,9 @@ colour = red name.en = The Beheading of Saint John the Baptist, martyr name.pl = męczeństwa św. Jana Chrzciciela name.la = In Passione S. Ioannis Baptistae, martyris +reading.first = Jeremiah 1:17-19 +reading.psalm = Psalms 71:1-2,3-4a,5-6ab,15ab+17 +reading.gospel = Mark 6:17-29 [gregory-the-great-pope-and-doctor] date = 09-03 @@ -1163,6 +1245,9 @@ colour = white name.en = Birth of the Blessed Virgin Mary name.pl = Narodzenia Najświętszej Maryi Panny name.la = In Nativitate B. Mariae Virginis +reading.first = Micah 5:1-4a +reading.psalm = Psalms 13:6ab,6c +reading.gospel = Matthew 1:1-16,18-23 [peter-claver-priest] date = 09-09 @@ -1196,6 +1281,10 @@ class = lord colour = red name.en = Triumph of the Holy Cross name.la = IN EXALTATIONE SANCTAE CRUCIS +reading.first = Numbers 21:4b-9 +reading.psalm = Psalms 78:1bc-2,34-35,36-37,38 +reading.second = Philippians 2:6-11 +reading.gospel = John 3:13-17 [our-lady-of-sorrows] date = 09-15 @@ -1205,6 +1294,9 @@ colour = white name.en = Our Lady of Sorrows name.pl = Najświętszej Maryi Panny Bolesnej name.la = B. Mariae Virginis Perdolentis +reading.first = Hebrews 5:7-9 +reading.psalm = Psalms 31:2-3b,3cd-4,5-6,15-16,20 +reading.gospel = John 19:25-27 [saints-cornelius-pope-and-cyprian-bishop-martyrs] date = 09-16 @@ -1247,6 +1339,9 @@ class = saint colour = red name.en = Saint Matthew the Evangelist, Apostle, Evangelist name.la = S. Matthaei, apostoli et evangelistae +reading.first = Ephesians 4:1-7,11-13 +reading.psalm = Psalms 19:2-3,4-5 +reading.gospel = Matthew 9:9-13 [pio-of-pietrelcina-padre-pio-priest] date = 09-23 @@ -1298,6 +1393,9 @@ colour = white name.en = Saints Michael, Gabriel and Raphael, Archangels name.pl = świętych Archaniołów Michała, Gabriela i Rafała name.la = Ss. Michaelis, Gabrielis et Raphaelis, archangelorum +reading.first = Daniel 7:9-10,13-14 +reading.psalm = Psalms 138:1-2ab,2cde-3,4-5 +reading.gospel = John 1:47-51 [jerome-priest-and-doctor] date = 09-30 @@ -1325,6 +1423,9 @@ colour = white name.en = Guardian Angels name.pl = świętych Aniołów Stróżów name.la = Ss. Angelorum Custodum +reading.first = Exodus 23:20-23 +reading.psalm = Psalms 91:1-2,3-4ab,4c-6,10-11 +reading.gospel = Matthew 18:1-5,10 [francis-of-assisi] date = 10-04 @@ -1426,6 +1527,9 @@ colour = red name.en = Saint Luke the Evangelist name.pl = św. Łukasza, ewangelisty name.la = S. Lucae, evangelistae +reading.first = 2 Timothy 4:10-17b +reading.psalm = Psalms 145:10-11,12-13,17-18 +reading.gospel = Luke 10:1-9 [paul-of-the-cross-priest] date = 10-19 @@ -1475,6 +1579,9 @@ colour = red name.en = Saint Simon and Saint Jude, apostles name.pl = świętych Apostołów Szymona i Judy Tadeusza name.la = Ss. Simonis et Iudae, apostolorum +reading.first = Ephesians 2:19-22 +reading.psalm = Psalms 19:2-3,4-5 +reading.gospel = Luke 6:12-19 [all-saints] date = 11-01 @@ -1484,6 +1591,10 @@ colour = white name.en = All Saints name.pl = Wszystkich Świętych name.la = OMNIUM SANCTORUM +reading.first = Revelation 7:2-4,9-14 +reading.psalm = Psalms 24:1bc-2,3-4ab,5-6 +reading.second = 1 John 3:1-3 +reading.gospel = Matthew 5:1-12a [all-souls] date = 11-02 @@ -1517,6 +1628,10 @@ class = lord colour = white name.en = Dedication of the Lateran basilica name.la = IN DEDICATIONE BASILICAE LATERANENSIS +reading.first = Ezekiel 47:1-2,8-9,12 +reading.psalm = Psalms 46:2-3,5-6,8-9 +reading.second = 1 Corinthians 3:9c-11,16-17 +reading.gospel = John 2:13-22 [leo-the-great-pope-and-doctor] date = 11-10 @@ -1585,6 +1700,9 @@ class = saint colour = white name.en = Dedication of the basilicas of Saints Peter and Paul, Apostles name.la = In Dedicatione basilicarum Ss. Petri et Pauli, apostolorum +reading.first = The Acts 28:11-16,30-31 +reading.psalm = Psalms 98:1,2-3ab,3cd-4,5-6 +reading.gospel = Matthew 14:22-33 [presentation-of-the-blessed-virgin-mary] date = 11-21 @@ -1644,6 +1762,9 @@ class = saint colour = red name.en = Saint Andrew the Apostle name.la = S. Andreae, apostoli +reading.first = Romans 10:9-18 +reading.psalm = Psalms 19:8,9,10,11 +reading.gospel = Matthew 4:18-22 [francis-xavier-priest] date = 12-03 @@ -1686,6 +1807,10 @@ colour = white name.en = Immaculate Conception of the Blessed Virgin Mary name.pl = Niepokalanego Poczęcia Najświętszej Maryi Panny name.la = IN CONCEPTIONE IMMACULATA BEATAE MARIAE VIRGINIS +reading.first = Genesis 3:9-15,20 +reading.psalm = Psalms 98:1,2-3ab,3cd-4 +reading.second = Ephesians 1:3-6,11-12 +reading.gospel = Luke 1:26-38 [juan-diego] date = 12-09 @@ -1752,6 +1877,9 @@ colour = red name.en = Saint Stephen, the first martyr name.pl = św. Szczepana, pierwszego męczennika name.la = S. Stephani, protomartyris +reading.first = The Acts 6:8-10;7:54-59 +reading.psalm = Psalms 31:3cd-4,6+8ab,16bc+17 +reading.gospel = Matthew 10:17-22 [john-the-apostle-and-evangelist] date = 12-27 @@ -1761,6 +1889,9 @@ colour = white name.en = Saint John the Apostle and evangelist name.pl = św. Jana, apostoła i ewangelisty name.la = S. Ioannis, apostoli et evangelistae +reading.first = 1 John 1:1-4 +reading.psalm = Psalms 97:1-2,5-6,11-12 +reading.gospel = John 20:1a+2-8 [holy-innocents-martyrs] date = 12-28 @@ -1769,6 +1900,9 @@ class = saint colour = red name.en = Holy Innocents, martyrs name.la = Ss. Innocentium, martyrum +reading.first = 1 John 1:5-2:2 +reading.psalm = Psalms 124:2-3,4-5,7b-8 +reading.gospel = Matthew 2:13-18 [thomas-becket-bishop-and-martyr] date = 12-29 diff --git a/internal/config/config.go b/internal/config/config.go index e84eabe..35f5990 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -63,7 +63,7 @@ const configHeader = `# lectio configuration (INI). Full-line comments only (# o // Version is lectio's release version, shared by every binary's // -v/--version output (lectio, lectio-ui, lectio-web). -const Version = "0.40.0" +const Version = "0.41.0" // validVersions are the five scripture versions lectio understands. var validVersions = map[string]bool{ diff --git a/scripts/gen-of-sanctoral-readings.py b/scripts/gen-of-sanctoral-readings.py new file mode 100644 index 0000000..fd9c035 --- /dev/null +++ b/scripts/gen-of-sanctoral-readings.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +"""Add PROPER Mass readings to the Ordinary Form sanctoral +(internal/caldata/roman-calendar.ini) from catholic-resources.org's Proper of +Saints table (Fr. Felix Just, S.J.). + +Without this, an OF feast/solemnity or a proper-reading memorial (e.g. the +Apostles' feasts, the Assumption, All Saints, St Barnabas, Our Lady of Sorrows, +the Guardian Angels) has no readings of its own and falls back to the day's +ferial. Only celebrations whose readings are marked PROPER (and every feast / +solemnity) are imported; memorials that draw on a Common keep the ferial, which +is the OF default. Scripture CITATIONS only; text is rendered from the public- +domain corpora. See NOTICE. + +Requires network. From the repo root: python3 scripts/gen-of-sanctoral-readings.py +Adds reading.first / reading.psalm / reading.second / reading.gospel lines to the +matching entries in internal/caldata/roman-calendar.ini. +""" +import re, sys, html, urllib.request + +INI = "internal/caldata/roman-calendar.ini" +URL = "https://www.catholic-resources.org/Lectionary/2002USL-Sanctoral.htm" +MONS = {m: i + 1 for i, m in enumerate("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split())} + +# CR abbreviation -> lectio full book name (same table as genlect-of-cr.py). +BOOK = {} +def reg(full, *al): + for a in al: BOOK[a.lower().replace(".", "").strip()] = full +reg("Genesis","gen");reg("Exodus","exod","ex");reg("Leviticus","lev");reg("Numbers","num") +reg("Deuteronomy","deut");reg("Joshua","josh");reg("Judges","judg");reg("Ruth","ruth") +reg("1 Samuel","1 sam");reg("2 Samuel","2 sam");reg("1 Kings","1 kgs");reg("2 Kings","2 kgs") +reg("1 Chronicles","1 chr");reg("2 Chronicles","2 chr");reg("Ezra","ezra");reg("Nehemiah","neh") +reg("Tobit","tob");reg("Judith","jdt");reg("Esther","esth");reg("1 Maccabees","1 macc");reg("2 Maccabees","2 macc") +reg("Job","job");reg("Psalms","ps","pss");reg("Proverbs","prov");reg("Ecclesiastes","eccl") +reg("Song of Solomon","song","cant");reg("Wisdom","wis");reg("Sirach","sir") +reg("Isaiah","isa");reg("Jeremiah","jer");reg("Lamentations","lam");reg("Baruch","bar") +reg("Ezekiel","ezek");reg("Daniel","dan");reg("Hosea","hos");reg("Joel","joel");reg("Amos","amos") +reg("Obadiah","obad");reg("Jonah","jonah");reg("Micah","mic","micah");reg("Nahum","nah");reg("Habakkuk","hab") +reg("Zephaniah","zeph");reg("Haggai","hag");reg("Zechariah","zech");reg("Malachi","mal") +reg("Matthew","matt","mat");reg("Mark","mark");reg("Luke","luke");reg("John","john") +reg("The Acts","acts");reg("Romans","rom");reg("1 Corinthians","1 cor");reg("2 Corinthians","2 cor") +reg("Galatians","gal");reg("Ephesians","eph");reg("Philippians","phil");reg("Colossians","col") +reg("1 Thessalonians","1 thess");reg("2 Thessalonians","2 thess");reg("1 Timothy","1 tim");reg("2 Timothy","2 tim") +reg("Titus","titus");reg("Philemon","phlm");reg("Hebrews","heb");reg("James","jas") +reg("1 Peter","1 pet");reg("2 Peter","2 pet");reg("1 John","1 john");reg("2 John","2 john");reg("3 John","3 john") +reg("Jude","jude");reg("Revelation","rev","apoc") + +def clean_cite(raw): + s = html.unescape(raw or "") + s = re.sub(r"[–—]", "-", s) + s = re.sub(r"\(#[^)]*\)", "", s) # drop lectionary numbers + s = re.sub(r"\bo?r\s+elsewhere.*", "", s, flags=re.I) + # cycle-specific gospel "A: ... B: ... C: ..." -> take the A option + m = re.search(r"\bA:\s*(.+?)(?:\s+B:|$)", s) + if m: + s = m.group(1) + s = re.split(r"\s+or\b", s, maxsplit=1)[0] # first of alternative readings + s = s.strip().strip(";,").strip() + if not s or s in (".", "x") or s.startswith("[") or s.startswith("("): + return "" + m = re.match(r"((?:[1-4]\s+)?[A-Za-z][A-Za-z]*\.?)\s*(.*)", s) + if not m: + return "" + tok = m.group(1).lower().replace(".", "").strip() + full = BOOK.get(tok) + if not full: + sys.stderr.write(f" ! unknown book {m.group(1)!r} in {raw!r}\n") + return "" + verses = re.sub(r",\s+", ",", m.group(2)).replace(" ", "") + return f"{full} {verses}".strip() + +def strip_tags(s): + return html.unescape(re.sub(r"<[^>]+>", "", s)).replace("\xa0", " ").replace("\n", " ").strip() + +def scrape(): + req = urllib.request.Request(URL, headers={"User-Agent": "Mozilla/5.0 (X11; Linux) lectio"}) + h = urllib.request.urlopen(req, timeout=60).read().decode("utf-8", "replace") + datere = re.compile(r"\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\.?\s+(\d{1,2})\b") + rows = [] + for tr in re.split(r"<tr\b", h): + c = [strip_tags(x) for x in re.findall(r"<td\b[^>]*>(.*?)</td>", tr, re.S)] + if len(c) < 10: + continue + m = datere.search(c[1]) + if not m: + continue + mmdd = f"{MONS[m.group(1)]:02d}-{int(m.group(2)):02d}" + rank, src = c[3], c[4] + if rank.startswith("USA") or "Calif" in rank: + continue # national additions not in the universal calendar + if not ("PROPER" in src.upper() or "Solemnity" in rank or "Feast" in rank): + continue # Common-based memorial -> keep the ferial + rows.append((mmdd, c[2], "vigil" in c[2].lower(), + {"first": clean_cite(c[5]), "psalm": clean_cite(c[6]), + "second": clean_cite(c[7]), "gospel": clean_cite(c[9])})) + # one Mass per date: prefer the day Mass over a vigil; later row breaks ties + best = {} + for mmdd, name, isvig, parts in rows: + if not (parts["first"] and parts["gospel"]): + continue # need at least first + gospel + cur = best.get(mmdd) + if cur is None or (cur[0] and not isvig): # replace a vigil with a day Mass + best[mmdd] = (isvig, name, parts) + return {k: v[2] for k, v in best.items()} + +def main(): + props = scrape() + sys.stderr.write(f"scraped {len(props)} proper-reading celebrations\n") + text = open(INI).read() + head = text[:text.index("\n[")] + blocks = re.split(r"\n(?=\[)", text[text.index("\n[") + 1:]) + # index blocks by date + name + out_blocks = [] + used = set() + for b in blocks: + m = re.match(r"\[(.+?)\]", b) + if not m or m.group(1) == "layer": + out_blocks.append(b.rstrip("\n")) + continue + f = dict(re.findall(r"^([\w.]+)\s*=\s*(.+)$", b, re.M)) + date = f.get("date", "") + # strip any pre-existing reading.* lines (idempotent re-run) + b = re.sub(r"^reading\.\w+\s*=.*\n?", "", b, flags=re.M).rstrip("\n") + if date in props: + p = props[date] + add = "".join(f"\nreading.{part} = {p[part]}" for part in + ("first", "psalm", "second", "gospel") if p.get(part)) + b += add + used.add(date) + out_blocks.append(b) + open(INI, "w").write(head.rstrip("\n") + "\n\n" + "\n\n".join(out_blocks) + "\n") + missing = sorted(set(props) - used) + sys.stderr.write(f"added readings to {len(used)} entries; " + f"{len(missing)} CR dates had no matching universal-calendar entry: {missing}\n") + +if __name__ == "__main__": + main() |
