Compare commits

...

2 Commits

Author SHA1 Message Date
b687a260c5 Improve apply_subscriptions
Events now have a subscriptions_applied flag. Using apply_subscriptions without passing an event now processes all events in the next week, which have not yet been processed
2025-10-27 15:32:00 +01:00
a2722d5f9f Add doc to grist sync 2025-10-27 11:35:52 +01:00
9 changed files with 104 additions and 23 deletions

6
.gitignore vendored
View File

@@ -1,5 +1,7 @@
config.php
test.php
melly-to-grist/.env
.env
**/__pycache__
new-registration-app/database.db
database.db*
.idea
db_fixtures

9
justfile Normal file
View File

@@ -0,0 +1,9 @@
lint:
uv run isort src
uv run black src
reset_db:
rm -f database.db
touch database.db
alembic upgrade heads
bash -c 'shopt -s nullglob; for file in db_fixtures/*.sql; do sqlite3 database.db < "$file"; done'

View File

@@ -24,7 +24,7 @@ dev = [
]
[project.scripts]
apply-subscriptions = "meal_manager.scripts:apply_subscriptions"
apply-subscriptions = "meal_manager.scripts:apply_subscriptions_cli"
[tool.isort]
@@ -36,7 +36,7 @@ profile = "black"
# this is typically a path given in POSIX (e.g. forward slashes)
# format, relative to the token %(here)s which refers to the location of this
# ini file
script_location = "%(here)s/alembic"
script_location = "%(here)s/src/meal_manager/alembic"
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time

View File

@@ -84,7 +84,7 @@ def upgrade() -> None:
sa.PrimaryKeyConstraint("household_id"),
)
op.create_table(
"team_registration",
"teamregistration",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("event_id", sa.Integer(), nullable=False),
sa.Column("person_name", sa.String(), nullable=False),

View File

@@ -0,0 +1,34 @@
"""Add subscriptions_applied column to Event
Revision ID: 13084c5c1f68
Revises: 299a83240036
Create Date: 2025-10-27 12:25:14.633641
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "13084c5c1f68"
down_revision: Union[str, Sequence[str], None] = "299a83240036"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"event", sa.Column("subscriptions_applied", sa.Boolean(), nullable=False)
)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("event", "subscriptions_applied")
# ### end Alembic commands ###

View File

@@ -433,7 +433,14 @@ def get_event_attendance_pdf(event_id: int, session: SessionDep):
@app.get("/event/{event_id}/sync_with_grist")
def sync_with_grist_route(event_id: int, session: SessionDep, user: StrictUserDep):
"""
Synchronizes the specified event with Grist and redirects the user.
This function retrieves the event by its identifier, synchronizes it with Grist,
and then redirects the user to the event page with a success message.
TODO: Error handling
"""
statement = select(Event).where(Event.id == event_id)
event = session.scalars(statement).one()

View File

@@ -33,6 +33,8 @@ class Event(Base):
team_prep_min: Mapped[int] = mapped_column(default=1, nullable=False)
team_prep_max: Mapped[int] = mapped_column(default=1, nullable=False)
subscriptions_applied: Mapped[bool] = mapped_column(default=False, nullable=False)
registrations: Mapped[list["Registration"]] = relationship(
"Registration", cascade="all, delete"
)

View File

@@ -1,33 +1,38 @@
import argparse
import datetime
from sqlalchemy import select
from sqlalchemy import Date, cast, func, select
from sqlalchemy.orm import Session
from meal_manager.main import engine
from meal_manager.models import Event, Registration, Subscription
def apply_subscriptions():
parser = argparse.ArgumentParser(description="Apply subscriptions for an event")
parser.add_argument("event_id", type=int, help="Event ID (required)")
parser.add_argument(
"--dry-run", action="store_true", help="Run without making changes"
)
def apply_subscriptions(session: Session, event: Event = None, dry_run: bool = False):
args = parser.parse_args()
subscriptions = session.scalars(select(Subscription)).all()
# Access the arguments
event_id = args.event_id
dry_run = args.dry_run
if event is not None:
events = [event]
else:
today = datetime.date.today()
query = select(Event).where(
~Event.subscriptions_applied,
func.strftime("%Y-%m-%d %H:%M:%S", Event.event_time) >= today.isoformat(),
func.strftime("%Y-%m-%d %H:%M:%S", Event.event_time)
<= (today + datetime.timedelta(days=7)).isoformat(),
)
events = session.scalars(query).all()
with Session(engine) as session:
subscriptions = session.scalars(select(Subscription)).all()
event = session.scalars(select(Event).where(Event.id == event_id)).one()
if len(events) == 0:
print("No events to process")
return
for event in events:
if dry_run:
print(f"DRY RUN: Would process event {event_id}")
print(f"DRY RUN: Would process event {event.title} ({event.id})")
else:
print(f"Processing event {event_id}")
print(f"Processing event {event.title} ({event.id})")
print(f"There are {len(subscriptions)} subscriptions to process")
relevant_subscriptions = [
@@ -60,6 +65,28 @@ def apply_subscriptions():
else:
session.add(reg)
print(f"Registered {subscription.household.name}")
event.subscriptions_applied = True
if not dry_run:
session.commit()
if not dry_run:
session.commit()
def apply_subscriptions_cli():
parser = argparse.ArgumentParser(description="Apply subscriptions for an event")
parser.add_argument("--event_id", type=int, help="Event ID (required)")
parser.add_argument(
"--dry-run", action="store_true", help="Run without making changes"
)
args = parser.parse_args()
# Access the arguments
event_id = args.event_id
dry_run = args.dry_run
with Session(engine) as session:
if event_id is not None:
event = session.scalars(select(Event).where(Event.id == event_id)).one()
apply_subscriptions(session, event, dry_run)
else:
apply_subscriptions(session, dry_run=dry_run)