diff options
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/genlect-of-cr.py | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/scripts/genlect-of-cr.py b/scripts/genlect-of-cr.py new file mode 100644 index 0000000..468a85d --- /dev/null +++ b/scripts/genlect-of-cr.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +"""Regenerate the Ordinary Form WEEKDAY (ferial) lectionary for Ordinary Time +from catholic-resources.org (Fr. Felix Just, S.J.), replacing the entries that +were harvested from niedziela.pl. + +Why: the niedziela harvest is by-date, so it (a) stores a saint's readings under +a ferial key whenever a saint displaced the ferial on the queried date, and +(b) inherits lectio's old Ordinary-Time week-number off-by-one. catholic- +resources.org is keyed by liturgical POSITION (week + weekday + year), immune to +both, complete (all 34 weeks x both years), and gives citations only (no text), +which matches lectio's licensing. Scripture citations only; see NOTICE. + +Requires network. From the repo root: python3 scripts/genlect-of-cr.py +Rewrites the ordinary-<week>-<day>-<I|II> entries in +internal/caldata/of-lectionary.ini, keyed by the canonical week number (the same +number temporalEF/temporal now computes). +""" +import re, sys, urllib.request + +INI = "internal/caldata/of-lectionary.ini" +SRC = {"I": "https://www.catholic-resources.org/Lectionary/2002USL-Weekdays-OT-I.htm", + "II": "https://www.catholic-resources.org/Lectionary/2002USL-Weekdays-OT-II.htm"} +DAYMAP = {"mon": "mon", "tues": "tue", "tue": "tue", "wed": "wed", + "thurs": "thu", "thu": "thu", "fri": "fri", "sat": "sat"} + +# CR abbreviation -> lectio full book name (matches the existing file's style). +BOOK = {} +def reg(full, *aliases): + for a in aliases: + 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");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");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") + +def norm_cite(c): + c = re.sub(r"^\s*See\s+", "", c.strip()) + c = re.sub(r"[–—]|—|–", "-", c) # en/em dash -> hyphen + c = re.sub(r"\s+", " ", c).strip() + m = re.match(r"((?:[1-4]\s+)?[A-Za-z][A-Za-z]*\.?)\s*(.*)", c) + if not m: + return c + 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 {c!r}\n") + full = m.group(1) + verses = re.sub(r",\s+", ",", m.group(2)).replace(" ", "") # match file style: no spaces + return f"{full} {verses}".strip() + +def strip_tags(s): + return re.sub(r"<[^>]+>", "", s).replace(" ", " ").strip() + +def scrape(url, year): + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (lectio OF lectionary generator)"}) + html = urllib.request.urlopen(req, timeout=40).read().decode("utf-8", "replace") + out = {} + for tr in re.split(r"<tr\b", html): + cells = [strip_tags(c) for c in re.findall(r"<td\b[^>]*>(.*?)</td>", tr, re.S)] + if len(cells) < 6: + continue + m = re.match(r"Week (\d+)\s*-\s*(Mon|Tues|Tue|Wed|Thurs|Thu|Fri|Sat)\b", cells[1]) + if not m: + continue + wk, day = int(m.group(1)), DAYMAP[m.group(2).lower()] + out[(wk, day, year)] = {"first": norm_cite(cells[2]), + "psalm": norm_cite(cells[3]), + "gospel": norm_cite(cells[5])} + return out + +def main(): + cr = {} + for yr, url in SRC.items(): + d = scrape(url, yr) + sys.stderr.write(f"{url}: {len(d)} rows\n") + cr.update(d) + + # parse existing file, keep header comment + all non-OT-ferial entries verbatim + text = open(INI).read() + head = text[:text.index("\n[")] + entries = {} + for block in re.split(r"\n(?=\[)", text[text.index("\n[") + 1:]): + m = re.match(r"\[(.+?)\]", block) + if m: + entries[m.group(1)] = block.rstrip("\n") + ot = re.compile(r"^ordinary-\d+-(mon|tue|wed|thu|fri|sat)-(I|II)$") + kept = {k: v for k, v in entries.items() if not ot.match(k)} + + for (wk, day, yr), parts in cr.items(): + key = f"ordinary-{wk}-{day}-{yr}" + lines = [f"[{key}]"] + for p in ("first", "psalm", "gospel"): + if parts[p]: + lines.append(f"{p} = {parts[p]}") + kept[key] = "\n".join(lines) + + out = head.rstrip("\n") + "\n" + for k in sorted(kept): + out += "\n" + kept[k] + "\n" + open(INI, "w").write(out) + n = sum(1 for k in kept if k.startswith("ordinary-") and ot.match(k)) + sys.stderr.write(f"wrote {len(kept)} entries ({n} OT ferials from catholic-resources.org)\n") + +if __name__ == "__main__": + main() |
