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