Skip to content

Speed up the feasts page and story checks - #284

Merged
brianglass merged 3 commits into
mainfrom
feasts-speedup
Nov 6, 2026
Merged

brianglass merged 3 commits into
mainfrom
feasts-speedup

Conversation

@brianglass

@brianglass brianglass commented Nov 6, 2026 •

Copy link
Copy Markdown
Owner

Two speedups: the feasts page no longer builds a liturgical Day at all, and every Day gets cheaper because it no longer strips each story's HTML.

Feasts page

The page used to build a Day for all 365 dates. Each one costs two to five queries, mostly for saints the page never shows. In production a year took about 1 second (New Relic median 961ms).

What changed.

  • One query fetches the Day rows that can make a date a feast or put it in a fasting season: feast level 6 and up, fast level 2 and up, fast-free days, and every tradition row (an override can lower a common row's level).
  • Only the dates that matter are visited. Each row's civil dates are worked out directly: fixed rows from their church date, and Paschal and floating rows from each of the two Paschal years a civil year spans. The Apostles' Fast is added too, since no row marks it. The walk goes over those dates in order instead of all 365.
  • Each date's rows are picked out the way Day._collect_commemorations does and merged with the same _merge_tradition_days. Feast names come from the merged rows, so no Day is built.

Shared rules instead of copies.

  • fasting.fast_level now holds the two rules fasting.apply used to set inline: fast-free days and the Apostles' fast. Both paths call it.
  • church_date in day.py is Day's civil-to-church date conversion, pulled out for the same reason.

Speed (local, 2027):

Before After
Gregorian 214ms, 730 queries 3ms, 1 query
Julian 388ms, 1,628 queries 4ms, 1 query

Stories

_has_story stripped the HTML from every story on every Day. It did that to catch markup-only stories like <p></p>, and it took about a fifth of building a month calendar. #172 cleared the nine such stories from the data, and none are left among the 892. _has_story now just checks for text, and a new test fails if the data ever brings a markup-only story back.

A month calendar now renders in 15.8ms, down from 19.4ms (local median); the readings page and the day API save the same per Day.

Testing

  • Equivalence: I ran the old and new feasts code side by side for every year from 1990 to 2110, plus 1585, 4098 and 4099, on both calendars and both traditions. Feasts (date, name, level, Pascha distance) and seasons matched in every case. The only difference: Greek 4099 used to raise an error (a 404 on the page) and now computes.
  • Full suite: all 309 tests pass, including the fasting snapshot test.

🤖 Generated with Claude Code

brianglass and others added 3 commits November 6, 2026 13:11
year_of_feasts built a liturgical Day for all 365 dates, two to five
queries each and mostly for saints the page never shows. It now fetches
the rows that can make a date a feast or a fasting day in one query,
merges each date's rows with _merge_tradition_days as Day does, and
builds a whole Day only for the twenty or so feasts, for their names.

The fast-level rules that fasting.apply used to set inline (fast-free
days and the Apostles' fast) move into fasting.fast_level so both paths
share them, and Day's civil-to-church date conversion becomes
church_date for the same reason.

A year now takes 15-23ms and 37-73 queries, down from 214-388ms and
730-1628. The old and new code agree on every year from 1990 to 2110 on
both calendars and traditions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
year_of_feasts walked all 365 dates and built a full Day for each feast
just for its name. It now works out the civil dates its rows fall on --
fixed rows from their church date, Paschal and floating rows from each
of the two Paschal years a civil year spans, plus the Apostles' Fast --
and names each feast from the merged rows it already has.

A year is now 3-4ms and one query. It still matches the old Day-per-date
code on every year from 1990 to 2110.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_has_story stripped every story's HTML on every Day to catch markup-only
stories like '<p></p>', a fifth of building a month calendar. #172
cleared the nine there were, so it now checks for text directly, and a
test fails if the data ever brings one back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brianglass brianglass changed the title Compute the feasts page from one query instead of a Day per date Speed up the feasts page and story checks Nov 6, 2026
@brianglass
brianglass merged commit a2134aa into main Nov 6, 2026
4 checks passed
This was referenced Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant