aboutsummaryrefslogtreecommitdiff
path: root/scripts/genlect-of.go
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/genlect-of.go')
-rw-r--r--scripts/genlect-of.go162
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)
+ }
+ }
+}