diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 23:44:36 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 23:44:36 +0200 |
| commit | e6eba32facf520d202f7941f43206e0f95141452 (patch) | |
| tree | 7519e518ac892d43a6bb4c9802fe5236cedf98dc /scripts | |
| parent | 4a35093c4358be17da6f0f374197caed743bda26 (diff) | |
| download | lectio-e6eba32facf520d202f7941f43206e0f95141452.tar.gz lectio-e6eba32facf520d202f7941f43206e0f95141452.zip | |
feat(caldata): generate OF Sunday/solemnity lectionary from niedziela
Task 2. scripts/genlect-of.go harvests niedziela over the PAST cycle-equivalent
years 2023(A)/2024(B)/2025(C) -- niedziela only publishes ~6 weeks ahead, so the
future 2025-2027 span was unfetchable; the 3-year cycle repeats, so a liturgical
position's cycle-A readings are identical whether they fall in 2023 or 2026.
Produces internal/caldata/of-lectionary.ini: 254 entries (A=86, B=87, C=81),
keyed <slug>-<cycle>, citations English-canonical via ToEnglishRef.
Also: books.ini [en] Matthew gains "Mat" (ToEnglishRef maps Polish Mt->Mat; now
resolves and displays as Matt). of_test.go assertion relaxed to accept either
spelling.
KNOWN GAP: 8 cross-chapter range citations (e.g. Sirach 27:30-28:7, John
18:1-19:42) don't yet resolve -- the bible ref parser handles semicolon groups
but not a dash-range spanning a chapter boundary. Fix next.
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/genlect-of.go | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/scripts/genlect-of.go b/scripts/genlect-of.go new file mode 100644 index 0000000..6126dea --- /dev/null +++ b/scripts/genlect-of.go @@ -0,0 +1,162 @@ +//go:build ignore + +// genlect-of generates the OF (Ordinary Form) Sunday/solemnity temporal +// lectionary from niedziela.pl (via internal/readings), keyed by lectio's +// computed <Observed.Slug>-<SundayCycle>. One-time; requires network. Run +// from the repo root: +// +// go run scripts/genlect-of.go +// +// Writes internal/caldata/of-lectionary.ini. +package main + +import ( + "fmt" + "os" + "regexp" + "sort" + "strings" + "time" + + "github.com/lukaszkasprzak/lectio/internal/bible" + "github.com/lukaszkasprzak/lectio/internal/caldata" + "github.com/lukaszkasprzak/lectio/internal/calendar" + "github.com/lukaszkasprzak/lectio/internal/config" + "github.com/lukaszkasprzak/lectio/internal/readings" +) + +// bookCommaRe strips a stray comma between the book name and the first +// chapter ("4 Kings, 5:1-15" -> "4 Kings 5:1-15"), a source data glitch. +// The [^:] guard means it only fires before any chapter:verse, so legitimate +// multi-chapter commas ("Gen 1:1, 2:3") are left intact. +// Copied verbatim from scripts/genlect.go. +var bookCommaRe = regexp.MustCompile(`^([^:]*?),\s+(\d+:)`) + +// chapDotRe rewrites a European-style "chapter. verse" separator to a colon +// ("John 20. 19-31" -> "John 20:19-31"), another source data glitch. It is +// applied only when the citation carries no colon at all, so disjoint verse +// groups in a normal citation ("Ps 62:2. 3-4") are never touched. +// Copied verbatim from scripts/genlect.go. +var chapDotRe = regexp.MustCompile(`(\d+)\.\s+(\d)`) + +func cleanCite(s string) string { + s = bookCommaRe.ReplaceAllString(strings.TrimSpace(s), "$1 $2") + if !strings.Contains(s, ":") { + s = chapDotRe.ReplaceAllString(s, "$1:$2") + } + return s +} + +// partIDToPart maps a niedziela.pl (liturgy.Section) PartID to our lectionary +// field name. Anything absent (e.g. "aklamacja") is not a reading and is +// ignored. +var partIDToPart = map[string]string{ + "pierwsze_czytanie": "first", + "psalm": "psalm", + "drugie_czytanie": "second", + "ewangelia": "gospel", +} + +func main() { + sel := calendar.DefaultSelection() + sel.Form = "new" + layers := []calendar.Layer{caldata.Universal()} + + cfg := config.Default() + cfg.Lectionary = "new" + + lect := map[string]map[string]string{} // key -> part -> citation + seen := map[string]bool{} + var missing []string // date+key kept but missing first/gospel + + // Harvest the PAST cycle-equivalent years: 2023=A, 2024=B, 2025=C. niedziela + // only publishes ~6 weeks ahead, so future years (2026-2027) are unavailable; + // the 3-year cycle repeats, so a liturgical position's cycle-A readings are the + // same whether they fall in 2023 or 2026. All three years are past and archived. + start := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(2025, 12, 31, 0, 0, 0, 0, time.UTC) + + for d := start; !d.After(end); d = d.AddDate(0, 0, 1) { + day := calendar.Compute(d, sel, layers) + keep := d.Weekday() == time.Sunday || + day.Observed.Rank == calendar.RankSolemnity || + day.Observed.Rank == calendar.RankFeast + if !keep { + continue + } + + key := day.Observed.Slug + "-" + day.SundayCycle + if seen[key] { + continue + } + seen[key] = true + + dateStr := d.Format("2006-01-02") + secs, _, err := readings.Load(cfg, readings.Options{Date: dateStr, All: true}) + if err != nil { + fmt.Fprintf(os.Stderr, "%s %-32s ERROR readings.Load: %v\n", dateStr, key, err) + missing = append(missing, fmt.Sprintf("%s %s (readings.Load error: %v)", dateStr, key, err)) + continue + } + + parts := map[string]string{} + for _, s := range secs { + part, ok := partIDToPart[s.PartID] + if !ok { + continue + } + system := "" + if part == "psalm" { + system = "drb" + } + eng, err := bible.ToEnglishRef(s.Citation, system) + if err != nil { + fmt.Fprintf(os.Stderr, "%s %-32s part=%-6s citation=%q: ToEnglishRef error: %v\n", + dateStr, key, part, s.Citation, err) + continue + } + parts[part] = cleanCite(eng) + } + + if parts["first"] == "" || parts["gospel"] == "" { + missing = append(missing, fmt.Sprintf("%s %s (first=%q gospel=%q)", dateStr, key, parts["first"], parts["gospel"])) + continue // require at least first+gospel to store the entry + } + + lect[key] = parts + fmt.Fprintf(os.Stderr, "%s %-32s first=%-22s psalm=%-22s second=%-22s gospel=%s\n", + dateStr, key, parts["first"], parts["psalm"], parts["second"], parts["gospel"]) + } + + keys := make([]string, 0, len(lect)) + for k := range lect { + keys = append(keys, k) + } + sort.Strings(keys) + + var b strings.Builder + b.WriteString("; OF (Ordinary Form) Sunday & solemnity lectionary, keyed by\n") + b.WriteString("; <computed-temporal-slug>-<SundayCycle>. Citations English-canonical.\n") + b.WriteString("; Generated from niedziela.pl by scripts/genlect-of.go (2025-2027).\n") + for _, k := range keys { + v := lect[k] + fmt.Fprintf(&b, "\n[%s]\n", k) + for _, part := range []string{"first", "psalm", "second", "gospel"} { + if c := v[part]; c != "" { + fmt.Fprintf(&b, "%s = %s\n", part, c) + } + } + } + if err := os.WriteFile("internal/caldata/of-lectionary.ini", []byte(b.String()), 0o644); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + fmt.Fprintf(os.Stderr, "\nwrote %d OF temporal entries\n", len(lect)) + if len(missing) > 0 { + fmt.Fprintf(os.Stderr, "%d kept days missing first/gospel (not stored):\n", len(missing)) + for _, m := range missing { + fmt.Fprintln(os.Stderr, m) + } + } +} |
