summaryrefslogtreecommitdiff
path: root/scripts/genlect-of-cr.py
blob: 468a85dc6bda5cdb117d30a3be92e44037f8211b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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"[–—]|&mdash;|&ndash;", "-", 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("&nbsp;", " ").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()