Add optional nursery roster composition

This commit is contained in:
Media
2026-08-30 20:04:00 -05:00
parent 3eab672c6c
commit 7651a3ed9d
12 changed files with 1578 additions and 116 deletions
+167
View File
@@ -1,4 +1,5 @@
from pathlib import Path
from typing import Literal, Sequence
import fitz # type: ignore
@@ -36,3 +37,169 @@ def make_geometry_pdf(path: Path) -> Path:
doc.save(path)
doc.close()
return path
def _make_subsplash_roster_pdf(
path: Path,
roster_type: Literal["main", "nursery_only"],
dates: Sequence[str],
) -> Path:
"""Create a realistic one-page, vector Subsplash-style roster fixture.
The fixture deliberately resembles the structure of ``input/original.pdf``:
dates run across the top, role headings and assignment labels run down the
left, and the cells contain volunteer names. It is not used by the legacy
geometry golden, so changes here cannot mask a single-roster regression.
"""
if not dates:
raise ValueError("at least one date is required")
document = fitz.open()
page = document.new_page(width=792, height=612)
page.draw_rect(page.rect, color=None, fill=(1, 1, 1), width=0)
if roster_type == "main":
table = fitz.Rect(34, 48, 758, 446)
roles = (
("MEDIA", "Sound", "Alex Morgan"),
("NURSERY", "Nursery Workers", "Jamie Rivera"),
("LITTLE WADERS", "Teacher", "Taylor Brooks"),
("RIVER KID", "Teacher", "Jordan Lee"),
("GREETERS", "Greet", "Morgan Reed"),
("USHERS", "Usher", "Casey Miller"),
("OFFERING MESSAGE", "Speaker", "Riley Davis"),
("PRAYER", "Prayer", "Avery Wilson"),
)
else:
# Mirrors the compact real nursery strip: roughly 36 points of date
# header plus about 66 points for the band and worker row.
table = fitz.Rect(14, 188, 778, 290)
roles = (("NURSERY WORKERS", "Nursery Workers", "Jamie Rivera"),)
first_column_width = 154
header_height = 42 if roster_type == "main" else 36
date_width = (table.width - first_column_width) / len(dates)
role_height = (table.height - header_height) / len(roles)
navy = (0.06, 0.18, 0.25)
pale_blue = (0.90, 0.95, 0.97)
grid = (0.36, 0.43, 0.47)
page.draw_rect(table, color=grid, fill=(1, 1, 1), width=0.8)
page.draw_rect(
fitz.Rect(table.x0, table.y0, table.x1, table.y0 + header_height),
color=grid,
fill=navy,
width=0.8,
)
page.insert_text(
(table.x0 + 8, table.y0 + 26),
"SERVING SCHEDULE",
fontname="helv",
fontsize=10,
color=(1, 1, 1),
)
first_date_x = table.x0 + first_column_width
page.draw_line(
fitz.Point(first_date_x, table.y0),
fitz.Point(first_date_x, table.y1),
color=grid,
width=0.8,
)
for index, date_label in enumerate(dates):
x = first_date_x + index * date_width
if index:
page.draw_line(
fitz.Point(x, table.y0),
fitz.Point(x, table.y1),
color=grid,
width=0.6,
)
page.insert_text(
(x + 8, table.y0 + 18),
date_label,
fontname="hebo",
fontsize=9,
color=(1, 1, 1),
)
page.insert_text(
(x + 8, table.y0 + 32),
"7:00pm" if date_label.lower().startswith(("wed", "thu")) else "10:00am",
fontname="helv",
fontsize=8,
color=(1, 1, 1),
)
for role_index, (heading, assignment, volunteer) in enumerate(roles):
y0 = table.y0 + header_height + role_index * role_height
y1 = y0 + role_height
if role_index:
page.draw_line(
fitz.Point(table.x0, y0),
fitz.Point(table.x1, y0),
color=grid,
width=0.6,
)
page.draw_rect(
fitz.Rect(table.x0, y0, first_date_x, y1),
color=None,
fill=pale_blue,
width=0,
)
page.insert_text(
(table.x0 + 7, y0 + 15),
heading,
fontname="hebo",
fontsize=7.5,
color=navy,
)
page.insert_text(
(
table.x0 + 7,
min(y1 - 5, y0 + (31 if roster_type == "main" else 43)),
),
assignment,
fontname="helv",
fontsize=7.5,
color=(0.12, 0.12, 0.12),
)
for date_index in range(len(dates)):
x = first_date_x + date_index * date_width + 7
name = volunteer if date_index % 2 == 0 else f"{volunteer.split()[0]} Kim"
page.insert_text(
(
x,
y0
+ min(
27 if roster_type == "main" else 41,
role_height * 0.72,
),
),
name,
fontname="helv",
fontsize=7.5,
color=(0.08, 0.08, 0.08),
)
document.save(path)
document.close()
return path
def make_main_roster_pdf(
path: Path,
dates: Sequence[str] = ("Sun 6 Sep", "Sun 13 Sep", "Sun 20 Sep", "Sun 27 Sep"),
) -> Path:
"""Create a full roster with several broad ministry-role headings."""
return _make_subsplash_roster_pdf(path, "main", dates)
def make_nursery_only_roster_pdf(
path: Path,
dates: Sequence[str] = ("Wed 2 Sep", "Wed 9 Sep", "Wed 16 Sep", "Wed 23 Sep"),
) -> Path:
"""Create a roster whose only ministry role is Nursery Workers."""
return _make_subsplash_roster_pdf(path, "nursery_only", dates)