mirror of
https://git.pumpin.online/funkpower/typikon.git
synced 2026-10-22 18:57:13 +00:00
a fetched menaion stands in for the common one, and its festal markers become the cycle
Two things went wrong when the Ruthenian set was overlaid on the packaged menologion. Every day listed each saint twice, once in each set's wording, because the overlay only ever appended. And MCI writes the festal cycle inline as though it were a commemoration -- 'Post-festive Day of the Exaltation' -- so it crowded out the saint that makes the day, which is the problem the structured cycle field existed to solve. A set can now declare mode=replace in a reserved _meta key, which the fetcher writes because a published calendar of saints is a complete calendar, not a few additions. Rank and cycle still come from the common set, since a published list carries neither and the fasting rules need the rank. A set without the key still appends, now without duplicating. The MCI parser turns the inline markers into the cycle field instead of leaving them in the list.
This commit is contained in:
+33
-5
@@ -72,20 +72,42 @@ def _parse_mci(html):
|
||||
dated = re.compile(r"^(" + "|".join(MONTHS) + r")\s+(\d{1,2})\s*(.*)$")
|
||||
heading = re.compile(r"^(" + "|".join(MONTHS) + r")\.?$")
|
||||
|
||||
# MCI writes the festal cycle inline, as a commemoration among the saints:
|
||||
# "Pre-festive Day of the Theophany". It repeats for days at a time and is
|
||||
# not a saint, so it becomes the day's cycle and leaves the list alone.
|
||||
festal = re.compile(r"^(Pre-festive|Post-festive|Fore-feast|After-feast|"
|
||||
r"Leave-taking)\b[ ,]*(?:Day\s+)?(?:of\s+)?(.*)$", re.I)
|
||||
cycle_word = {"pre-festive": "forefeast", "fore-feast": "forefeast",
|
||||
"post-festive": "afterfeast", "after-feast": "afterfeast",
|
||||
"leave-taking": "leave-taking"}
|
||||
|
||||
out, cur, buf, corrections = {}, None, [], []
|
||||
prev_month = prev_day = None
|
||||
|
||||
def flush():
|
||||
if not (cur and buf):
|
||||
return
|
||||
items = []
|
||||
items, cycle = [], None
|
||||
for piece in buf:
|
||||
for part in re.split(r"(?<=\.)\s+(?=[A-Z(])", piece):
|
||||
part = part.strip(" .;")
|
||||
if len(part) > 3 and not dated.match(part) and not heading.match(part):
|
||||
items.append(part)
|
||||
if len(part) <= 3 or dated.match(part) or heading.match(part):
|
||||
continue
|
||||
m = festal.match(part)
|
||||
if m:
|
||||
if cycle is None:
|
||||
word = cycle_word[m.group(1).lower()]
|
||||
rest = m.group(2).strip(" .,")
|
||||
cycle = f"{word} of {rest}" if rest else word
|
||||
continue
|
||||
items.append(part)
|
||||
entry = {}
|
||||
if items:
|
||||
out[cur] = {"commemorations": items}
|
||||
entry["commemorations"] = items
|
||||
if cycle:
|
||||
entry["cycle"] = cycle
|
||||
if entry:
|
||||
out[cur] = entry
|
||||
|
||||
for line in lines:
|
||||
if heading.match(line):
|
||||
@@ -135,9 +157,15 @@ def update(name):
|
||||
pass
|
||||
missing = sorted(expected - set(entries))
|
||||
|
||||
# This is a complete calendar published by that church, so it stands in
|
||||
# for the common commemorations rather than piling on top of them.
|
||||
payload = {"_meta": {"mode": "replace", "source": source["url"],
|
||||
"title": source["title"]}}
|
||||
payload.update(entries)
|
||||
|
||||
path = os.path.join(target_dir(), f"{name}.json")
|
||||
with open(path, "w", encoding="utf-8") as fh:
|
||||
json.dump(entries, fh, ensure_ascii=False, indent=1, sort_keys=True)
|
||||
json.dump(payload, fh, ensure_ascii=False, indent=1, sort_keys=True)
|
||||
|
||||
return {"path": path, "days": len(entries), "corrections": corrections,
|
||||
"missing": missing, "source": source}
|
||||
|
||||
+24
-5
@@ -19,23 +19,42 @@ _cache = {}
|
||||
|
||||
|
||||
class Menologion:
|
||||
"""A fixed calendar, plus whatever a jurisdiction overlays on it.
|
||||
|
||||
``_meta`` is a reserved key, not a day. It carries ``mode``:
|
||||
|
||||
add the set's commemorations join the common ones (the default,
|
||||
right for a local set that adds a few of its own saints)
|
||||
replace the set's commemorations stand in for them, because it is
|
||||
itself a complete calendar published by that church
|
||||
|
||||
Either way the common set's ``rank`` and ``cycle`` survive unless the
|
||||
overlay gives its own, since a published list of saints usually carries
|
||||
neither and the fasting rules need the rank.
|
||||
"""
|
||||
|
||||
def __init__(self, entries):
|
||||
self.entries = entries
|
||||
self.meta = entries.get("_meta", {})
|
||||
self.entries = {k: v for k, v in entries.items() if k != "_meta"}
|
||||
|
||||
def get(self, month, day):
|
||||
return self.entries.get(f"{month:02d}-{day:02d}", {})
|
||||
|
||||
def overlay(self, other):
|
||||
"""Add another set on top of this one, keeping both days' entries."""
|
||||
replace = other.meta.get("mode") == "replace"
|
||||
merged = {k: dict(v) for k, v in self.entries.items()}
|
||||
for key, add in other.entries.items():
|
||||
if key not in merged:
|
||||
merged[key] = dict(add)
|
||||
continue
|
||||
base = merged[key]
|
||||
base["commemorations"] = (list(base.get("commemorations", []))
|
||||
+ list(add.get("commemorations", [])))
|
||||
# A supplementary set may raise a day's rank but never lower it.
|
||||
extra = list(add.get("commemorations", []))
|
||||
if replace:
|
||||
base["commemorations"] = extra or base.get("commemorations", [])
|
||||
else:
|
||||
seen = list(base.get("commemorations", []))
|
||||
base["commemorations"] = seen + [c for c in extra if c not in seen]
|
||||
# A supplementary set may state a rank or a cycle; it never erases one.
|
||||
for field in ("rank", "cycle"):
|
||||
if add.get(field):
|
||||
base[field] = add[field]
|
||||
|
||||
Reference in New Issue
Block a user