Skip to content

Add camp Google Sheets snapshots - #701

Draft
cytadela8-ai wants to merge 15 commits into
warsztatywww:masterfrom
cytadela8-ai:camp-sheets-integration
Draft

Add camp Google Sheets snapshots#701
cytadela8-ai wants to merge 15 commits into
warsztatywww:masterfrom
cytadela8-ai:camp-sheets-integration

Conversation

@cytadela8-ai

@cytadela8-ai cytadela8-ai commented Jul 24, 2026

Copy link
Copy Markdown

What changed

Adds per-camp Google Sheets snapshot publishing for participant, lecturer, and workshop administration data. Includes the integration state model, transaction-safe queue and worker, Google API adapter, managed-tab provisioning, change scheduling, Camp admin controls, and operational documentation.

Why

Camp administration tables can now be published as recoverable full snapshots without blocking application writes.

Validation

  • manage.py test wwwapp.tests.test_google_sheets_queue wwwapp.tests.test_google_sheets_projections wwwapp.tests.test_google_sheets_google wwwapp.tests.test_google_sheets_worker wwwapp.tests.test_google_sheets_signals -v 1
  • manage.py makemigrations --check --dry-run
  • git diff --check

Type checking was intentionally excluded by request.


This change is Reviewable

@cytadela8

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7ded3a23df

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread wwwapp/admin.py
for instance in formset.deleted_objects:
instance.delete()
for instance in instances:
enabled_before = instance.pk and CampGoogleSheetsIntegration.objects.get(pk=instance.pk).enabled

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve non-Sheets Camp inlines when saving

When a Camp admin save includes a changed WorkshopTypeInline or WorkshopCategoryInline, Django calls this same save_formset for that inline too, but this branch treats every inline object as a CampGoogleSheetsIntegration. Editing an existing type/category will try to load an integration with the type/category primary key (often raising DoesNotExist), and adding one then hits instance.enabled; either way ordinary Camp formset saves break unless only the Sheets inline changed. Gate this logic to the Sheets formset/model and delegate other formsets to the default save path.

Useful? React with 👍 / 👎.

Comment on lines +34 to +37
participants = UserProfile.objects.filter(camp_participation__year=camp).exclude(
lecturer_workshops__in=Workshop.objects.filter(
year=camp, status=Workshop.STATUS_ACCEPTED)).select_related('user').prefetch_related(
Prefetch('camp_participation', queryset=participation_queryset)).distinct()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include interested-email rows in participant snapshots

The participant admin view also appends CampInterestEmail rows for people who registered interest without an account, and this change even marks CampInterestEmail saves/deletes dirty, but the snapshot projection only reads UserProfile rows with camp_participation. Any camp relying on that interested-email list will publish a participant sheet that silently omits those entries, so the Google snapshot no longer matches the administration table it is meant to mirror.

Useful? React with 👍 / 👎.

Comment on lines +50 to +51
columns = (TableColumn('Imię i nazwisko'), TableColumn('Email'),
TableColumn('Warsztaty'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Export the full lecturer administration table

The /lecturers/ admin table is rendered through the same people datatable as participants, including hidden profile fields, adulthood, previous editions, and dynamic camp-form answers; this projection reduces lecturers to just name, email, and workshops. For enabled Sheets integrations, lecturer snapshots therefore drop most of the administration data and form responses that staff can see in the Django table, so reuse the person-column/row projection (with the lecturer-specific workshop column) rather than this three-column table.

Useful? React with 👍 / 👎.

participation = next((item for item in profile.camp_participation.all()
if item.year_id == camp.pk), None)
birth = _birth_date(camp, profile)
adult = '-' if birth is None else _yes_no(camp.start_date >= birth.replace(year=birth.year + 18))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid crashing age calculation for leap-day births

If a camp has a birth-date/PESEL form question and a participant was born on February 29, birth.replace(year=birth.year + 18) raises ValueError for non-leap 18th years, so one such participant prevents the whole worker from publishing any tabs for that camp. The existing view code uses relativedelta(years=18) for this case; use the same approach here (and handle camps without start_date) before writing the snapshot.

Useful? React with 👍 / 👎.

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.

2 participants