aboutsummaryrefslogtreecommitdiff
path: root/internal/tradlit/tradlit.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 12:50:31 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-28 12:50:31 +0200
commit7b220084cf3951c8cde0582efdfcf628afc64336 (patch)
treee74bae03c61b62479ef4f68b046e3345618933c8 /internal/tradlit/tradlit.go
parentfeede51697be870ae183a95b513ef64f031dbd0f (diff)
downloadlectio-7b220084cf3951c8cde0582efdfcf628afc64336.tar.gz
lectio-7b220084cf3951c8cde0582efdfcf628afc64336.zip
refactor: remove the niedziela/missalemeum scrapers, bt, traditional_lang
The daily view now computes entirely offline (previous commit), so retire the network path and everything that served it: - Delete internal/tradlit (missalemeum) and the niedziela scraper from internal/liturgy (fetch/store/parse + fixtures); keep Section, DayInfo and ExtractCitation. - Remove the "bt" version everywhere (render gatherBT + branches, config, i18n, web form, TUI) and bible.ToEnglishRef (Polish citation converter). A legacy config carrying "bt" migrates to "wuj" on load (config.migrateBT). - Remove the traditional_lang config field and the -g/--lang flag from all three binaries. - Drop the now-dead flags -R/--refresh, -o/--offline, -u/--update, -C/--clean and the harvest/clean commands. - New defaults: versions = wuj,vul,grb,drb; default_version = vul. Update the README (offline-by-design, no harvest/update), help text, and stale niedziela/bt doc comments. Tests updated for the offline reality; go test ./... and go vet ./... are clean, all three binaries build and run offline (OF + EF, compare, web).
Diffstat (limited to 'internal/tradlit/tradlit.go')
-rw-r--r--internal/tradlit/tradlit.go216
1 files changed, 0 insertions, 216 deletions
diff --git a/internal/tradlit/tradlit.go b/internal/tradlit/tradlit.go
deleted file mode 100644
index b464e56..0000000
--- a/internal/tradlit/tradlit.go
+++ /dev/null
@@ -1,216 +0,0 @@
-// Package tradlit fetches and parses the Traditional (1962 Missal) Latin
-// propers from the missalemeum JSON API into the shared liturgy.Section
-// type, so the rest of the app can treat the traditional and modern
-// (niedziela.pl) lectionaries interchangeably.
-package tradlit
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "os"
- "path/filepath"
- "regexp"
- "strings"
-
- "github.com/lukaszkasprzak/lectio/internal/liturgy"
-)
-
-// baseURL is the missalemeum proper-of-the-day API template ("%s" is lang,
-// then date, YYYY-MM-DD). It is a package var so tests can point it at an
-// httptest server.
-var baseURL = "https://www.missalemeum.com/%s/api/v5/proper/%s"
-
-// userAgent is sent on every fetch; matches the browser UA used elsewhere
-// in this project (see the plan's Global Constraints).
-const userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " +
- "(KHTML, like Gecko) Chrome/124.0 Safari/537.36"
-
-// citationRe matches the first *...* marker in a section's body text, e.g.
-// "*Luke 7:36-50*" or "*Ps 44:2*".
-var citationRe = regexp.MustCompile(`\*([^*\n]+)\*`)
-
-// apiResponse mirrors the shape of the missalemeum proper-of-the-day API:
-// a single-element list of { info, sections }.
-type apiResponse struct {
- Info apiInfo `json:"info"`
- Sections []apiSection `json:"sections"`
-}
-
-// apiInfo mirrors the missalemeum API's "info" object: the day's celebration
-// title, its temporal context ("tempora"), and its liturgical colour code(s)
-// -- see dayInfoFromAPI, which turns it into a liturgy.DayInfo.
-type apiInfo struct {
- Title string `json:"title"`
- Tempora string `json:"tempora"`
- Colors []string `json:"colors"`
-}
-
-type apiSection struct {
- ID string `json:"id"`
- Label string `json:"label"`
- Body [][]string `json:"body"`
-}
-
-// tradColours maps missalemeum's single-letter liturgical colour codes to
-// DayInfo's normalized colour names; a code not listed here (or an empty
-// Colors list) leaves DayInfo.Colour "".
-var tradColours = map[string]string{
- "w": "white",
- "r": "red",
- "v": "violet",
- "g": "green",
- "p": "rose",
-}
-
-// dayInfoFromAPI turns a response's info object into a liturgy.DayInfo:
-// Title -> Name, Tempora -> Season, and the first Colors code -> Colour via
-// tradColours (unknown/missing -> ""). A zero-value apiInfo (no "info" key
-// in the response) yields a zero DayInfo.
-func dayInfoFromAPI(info apiInfo) liturgy.DayInfo {
- colour := ""
- if len(info.Colors) > 0 {
- colour = tradColours[strings.ToLower(info.Colors[0])]
- }
- return liturgy.DayInfo{
- Name: info.Title,
- Season: info.Tempora,
- Colour: colour,
- }
-}
-
-// Parse decodes a missalemeum proper-of-the-day API response body into
-// liturgy.Sections plus the day's liturgical identity (its "info" object,
-// see dayInfoFromAPI). Sections with an empty id or empty body are skipped.
-func Parse(jsonBody []byte) ([]liturgy.Section, liturgy.DayInfo, error) {
- var resp []apiResponse
- if err := json.Unmarshal(jsonBody, &resp); err != nil {
- return nil, liturgy.DayInfo{}, fmt.Errorf("tradlit: parse: %w", err)
- }
- if len(resp) == 0 {
- return nil, liturgy.DayInfo{}, fmt.Errorf("tradlit: parse: empty response")
- }
-
- info := dayInfoFromAPI(resp[0].Info)
-
- var out []liturgy.Section
- for _, sec := range resp[0].Sections {
- if sec.ID == "" || len(sec.Body) == 0 || len(sec.Body[0]) == 0 {
- continue
- }
-
- text := strings.Join(sec.Body[0], "\n")
-
- var lines []string
- for _, line := range strings.Split(text, "\n") {
- line = strings.TrimSpace(line)
- if line != "" {
- lines = append(lines, line)
- }
- }
-
- citation := ""
- if m := citationRe.FindStringSubmatch(text); m != nil {
- citation = strings.TrimSpace(m[1])
- }
-
- out = append(out, liturgy.Section{
- Heading: sec.Label,
- Citation: citation,
- PartID: strings.ToLower(sec.ID),
- Paragraphs: [][]string{lines},
- })
- }
- return out, info, nil
-}
-
-// cachePath returns where Load caches a (date, lang) day's raw API response:
-// <CacheDir>/<date>.trad.<lang>.json -- date-prefixed (like the modern
-// lectionary's own cache files) so liturgy.CleanCache can prune it by date.
-func cachePath(date, lang string) string {
- return filepath.Join(liturgy.CacheDir(), date+".trad."+lang+".json")
-}
-
-// Load returns a day's traditional propers for lang, either read from the
-// on-disk cache (offline) or fetched live from missalemeum and cached for
-// next time (online). Both paths share Parse, so cached and live results
-// are identical.
-//
-// - offline: reads the cache file written by a prior online Load (see
-// cachePath); if it doesn't exist, returns a clear error telling the
-// caller to go online or run 'lectio update' first.
-// - online: fetches https://www.missalemeum.com/{lang}/api/v5/proper/{date}
-// as before. On a successful 200, the raw response body is written to
-// the cache path (best-effort -- a cache-write failure never fails the
-// request) before being parsed. On HTTP 404 (no propers published for
-// that date) it returns a clear error and writes nothing to the cache.
-func Load(date, lang string, offline bool) ([]liturgy.Section, liturgy.DayInfo, error) {
- if offline {
- return loadCached(date, lang)
- }
- return loadLive(date, lang)
-}
-
-// loadCached implements Load's offline path.
-func loadCached(date, lang string) ([]liturgy.Section, liturgy.DayInfo, error) {
- body, err := os.ReadFile(cachePath(date, lang))
- if err != nil {
- return nil, liturgy.DayInfo{}, fmt.Errorf("tradlit: no cached traditional propers for %s (%s); view it online or run 'lectio update' first", date, lang)
- }
- return Parse(body)
-}
-
-// loadLive implements Load's online path: fetch, best-effort cache the raw
-// body, then parse.
-func loadLive(date, lang string) ([]liturgy.Section, liturgy.DayInfo, error) {
- body, err := fetch(date, lang)
- if err != nil {
- return nil, liturgy.DayInfo{}, err
- }
- writeCache(date, lang, body)
- return Parse(body)
-}
-
-// fetch GETs the day's proper-of-the-day JSON from missalemeum and returns
-// the raw response body. On HTTP 404 (no propers published for that date)
-// it returns a clear error rather than the raw 404 body.
-func fetch(date, lang string) ([]byte, error) {
- url := fmt.Sprintf(baseURL, lang, date)
-
- req, err := http.NewRequest(http.MethodGet, url, nil)
- if err != nil {
- return nil, fmt.Errorf("tradlit: load: %w", err)
- }
- req.Header.Set("User-Agent", userAgent)
-
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- return nil, fmt.Errorf("tradlit: load %s: %w", date, err)
- }
- defer resp.Body.Close()
-
- if resp.StatusCode == http.StatusNotFound {
- return nil, fmt.Errorf("tradlit: no propers published for %s", date)
- }
- if resp.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("tradlit: load %s: unexpected status %s", date, resp.Status)
- }
-
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, fmt.Errorf("tradlit: load %s: %w", date, err)
- }
- return body, nil
-}
-
-// writeCache best-effort writes a day's raw API response body to its cache
-// path; a failure to cache (e.g. an unwritable cache dir) must never fail
-// the live request that produced body.
-func writeCache(date, lang string, body []byte) {
- path := cachePath(date, lang)
- if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
- return
- }
- _ = os.WriteFile(path, body, 0o644)
-}