#!/usr/bin/env python3 """Re-source the Ordinary Form SEASONAL weekday (ferial) lectionary -- the proper weekdays of Advent (weeks 1-3), Lent, and Eastertide -- from catholic-resources.org (Fr. Felix Just, S.J.). These are single-cycle (the same every year), so both the -I and -II weekday-cycle keys get identical readings. Fixes niedziela-harvest defects found by diffing: contaminated Easter weekday first readings (easter-3-mon had 1 Cor 15 instead of Acts 6:8-15), the missing Easter-octave weekdays, and stray psalm-numbering artifacts. The DATE-based days (late Advent Dec 17-24 and the Christmas weekdays) are handled elsewhere. Scripture citations only; text rendered from the public-domain corpora. See NOTICE. Requires network. python3 scripts/genlect-of-season-ferials-cr.py """ import re, sys, html, urllib.request INI = "internal/caldata/of-lectionary.ini" PAGES = ["Weekdays-AdventChristmas", "Weekdays-Lent", "Weekdays-Easter"] BASE = "https://www.catholic-resources.org/Lectionary/2002USL-{}.htm" WD = {"m": "mon", "mon": "mon", "monday": "mon", "t": "tue", "tue": "tue", "tues": "tue", "tuesday": "tue", "w": "wed", "wed": "wed", "wednesday": "wed", "th": "thu", "thu": "thu", "thurs": "thu", "thursday": "thu", "f": "fri", "fri": "fri", "friday": "fri", "s": "sat", "sat": "sat", "saturday": "sat"} 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","2 mac") reg("Job","job");reg("Psalms","ps","pss");reg("Proverbs","prov");reg("Ecclesiastes","eccl") reg("Song of Solomon","song","cant","songs");reg("Wisdom","wis");reg("Sirach","sir") reg("Isaiah","isa","is");reg("Jeremiah","jer");reg("Lamentations","lam");reg("Baruch","bar") reg("Ezekiel","ezek","ez");reg("Daniel","dan");reg("Hosea","hos");reg("Joel","joel");reg("Amos","amos") reg("Obadiah","obad");reg("Jonah","jonah","jon");reg("Micah","mic","micah");reg("Nahum","nah");reg("Habakkuk","hab") reg("Zephaniah","zeph");reg("Haggai","hag");reg("Zechariah","zech","zac");reg("Malachi","mal") reg("Matthew","matt","mat","mt");reg("Mark","mark");reg("Luke","luke");reg("John","john","jn") reg("The Acts","acts","act");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","hebr");reg("James","jas") reg("1 Peter","1 pet","1 petr");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") for _f in list(set(BOOK.values())): BOOK.setdefault(_f.lower(), _f) def st(s): return html.unescape(re.sub(r"<[^>]+>", "", s)).replace("\xa0", " ").replace("\n", " ").replace("\r", " ").strip() def clean_cite(raw): s = st(raw) s = re.sub(r"[†‡*]", "", s) # dagger/star alternate-reading markers s = re.sub(r"[–—]", "-", s) s = re.sub(r"\([^)]*\)", "", s) s = re.sub(r"\s+and\s+.*", "", s) # Ash Wed lists 2nd reading after "and" -> drop for first s = re.split(r"\s+or\b", s, maxsplit=1)[0] s = re.sub(r"^(cf\.?|see)\s+", "", s.strip(), flags=re.I).strip().strip(";,").strip() m = re.match(r"((?:[1-4]\s+)?[A-Za-z][A-Za-z]*)\.?\s*(.*)", s) if not m: return "" full = BOOK.get(m.group(1).lower().strip()) if not full: sys.stderr.write(f" ! unknown book {m.group(1)!r} in {raw!r}\n"); return "" return f"{full} {re.sub(r',\s+', ',', m.group(2)).replace(' ', '')}".strip() def slug_of(day): d = re.sub(r"\s+", " ", st(day).lower()) m = re.match(r"december (\d+)", d) # late Advent Dec 17-24: proper to the date if m and 17 <= int(m.group(1)) <= 24: return "advent-dec-" + m.group(1) m = re.match(r"dec\.? (\d+)", d) # Christmas octave ferials Dec 29-31 if m and 29 <= int(m.group(1)) <= 31: return "christmas-dec-" + m.group(1) m = re.match(r"\[?\s*jan\.? (\d+)", d) # Christmas weekdays before Epiphany, Jan 2-7 if m and 2 <= int(m.group(1)) <= 7: return "christmas-jan-" + m.group(1) m = re.match(r"(monday|tuesday|wednesday|thursday|friday|saturday) after epiphany", d) if m: return "christmas-after-epiphany-" + WD[m.group(1)] if "ash wednesday" in d: return "lent-after-ashes-wed" m = re.match(r"(thursday|friday|saturday) after ash", d) if m: return "lent-after-ashes-" + WD[m.group(1)] m = re.search(r"octave of easter\s*[-–—]\s*(\w+)", d) if m and m.group(1) in WD: return "easter-octave-" + WD[m.group(1)] m = re.search(r"(\d+)\w* week of (advent|lent|easter)\s*[-–—]\s*(\w+)", d) if m and m.group(3) in WD: return f"{m.group(2)}-{m.group(1)}-" + WD[m.group(3)] return None # date-based (Dec 17-24, Christmas weekdays): handled by the temporal engine, not here def firstbook(c): m = re.match(r"((?:[1-4] )?[A-Za-z ]+?) (\d+)", c or "") return f"{m.group(1)} {m.group(2)}" if m else (c or "") def main(): cr = {} for p in PAGES: req = urllib.request.Request(BASE.format(p), headers={"User-Agent": "Mozilla/5.0 (X11; Linux) lectio"}) h = urllib.request.urlopen(req, timeout=60).read().decode("utf-8", "replace") n = 0 for tr in re.split(r"