Skip to content

feat: add on-site check-in, door checks, and product redemption#53

Merged
JacobCoffee merged 6 commits intomainfrom
phase-17/on-site-checkin-scanner
Mar 19, 2026
Merged

feat: add on-site check-in, door checks, and product redemption#53
JacobCoffee merged 6 commits intomainfrom
phase-17/on-site-checkin-scanner

Conversation

@JacobCoffee
Copy link
Copy Markdown
Owner

Summary

  • Add CheckIn, DoorCheck, and ProductRedemption models to the registration app for on-site conference operations
  • Add CheckInService and RedemptionService with lookup-by-access-code, check-in recording, door checks, and quantity-tracked product redemption (prevents double-use)
  • Add staff-facing JSON API endpoints: scan, lookup, redeem, and offline preload (bulk export for offline scanner fallback)
  • Add scanner UI in the manage app with barcode/QR input, attendee display, redemption buttons, station tracking (localStorage), and visual status indicators
  • Add check-in dashboard with real-time stats: check-in rate, station activity, recent check-ins, redemption counts
  • 36 tests covering models, services, and API views

Design notes

  • Modeled after pycon-site's DoorCheck + ItemController redemption flow
  • DoorCheck tracks attendance at sub-events; ProductRedemption tracks line-item consumption (separate concerns)
  • Scanner UI uses vanilla JS + fetch() against the JSON API (no framework dependency)
  • Offline preload endpoint supports ?ticket_type=<slug> filtering for targeted scanner deployment

Test plan

  • 36 new tests pass (tests/test_registration/test_checkin.py)
  • All 1875 existing tests pass (1 pre-existing flaky test in voucher bulk views)
  • Lint, format, and type-check pass (make ci)
  • Manual verification of scanner UI in dev server
  • Verify check-in dashboard renders with seeded data

🤖 Generated with Claude Code

Implements Phase 17: on-site check-in scanner with staff-facing UI,
per-product door checks, and line-item redemption with double-use
prevention. Modeled after pycon-site's DoorCheck + redemption flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 19, 2026 02:26
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds on-site operations support to the registration/manage apps: server-side check-in/redemption models + services, staff-only JSON endpoints, and a staff UI (scanner + dashboard) to drive check-ins and product redemption during the conference.

Changes:

  • Introduces CheckIn, DoorCheck, and ProductRedemption models (with admin registration) plus migration.
  • Adds CheckInService / RedemptionService business logic and staff-only JSON API views (scan/lookup/redeem/preload) wired into registration URLs.
  • Adds manage-side check-in dashboard + scanner templates and routes, plus a new test suite for the feature set.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
tests/test_registration/test_checkin.py New unit/integration tests for models, services, and check-in API endpoints
src/django_program/registration/views_checkin.py New staff-only JSON API views for scan/lookup/redeem/preload
src/django_program/registration/urls.py Registers the new check-in API endpoints under registration routes
src/django_program/registration/services/checkin.py New service layer for check-in, door checks, and redemption logic
src/django_program/registration/checkin.py New on-site operations models (check-in, door checks, redemptions)
src/django_program/registration/migrations/0016_checkin_doorcheck_productredemption.py Migration creating the new on-site operation tables/constraints
src/django_program/registration/models.py Re-exports the new models from registration.checkin
src/django_program/registration/admin.py Adds read-only admin views for check-in / door check / redemption records
src/django_program/manage/views_checkin.py Adds dashboard and scanner page views (manage app)
src/django_program/manage/urls.py Adds manage routes for dashboard/scanner pages
src/django_program/manage/templates/django_program/manage/checkin_scanner.html Adds the scanner UI and client-side API integration
src/django_program/manage/templates/django_program/manage/checkin_dashboard.html Adds dashboard template for stats/recent activity
src/django_program/manage/templates/django_program/manage/base.html Adds sidebar navigation entry for check-in

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

JacobCoffee and others added 2 commits March 18, 2026 21:44
…ue_together

- Remove unique_together on ProductRedemption to allow quantity-tracked
  redemption (multiple rows per line item up to purchased quantity)
- Add select_for_update() locking in redeem_product() to prevent
  concurrent over-redemption race conditions
- Remove csrf_exempt from ScanView/RedeemView; scanner JS sends
  X-CSRFToken from cookie
- Fix scanner UI to match actual API response shapes (nested attendee
  object, redeemable list, correct field names)
- Sanitize error messages to avoid leaking internal IDs
- Validate line_item_id as integer before DB lookup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mption

- Align API permission check with ManagePermissionMixin (require
  change_conference perm, not just is_staff)
- Add order status validation on scan/redeem (reject refunded/cancelled),
  include PARTIALLY_REFUNDED in preload export
- Return redeemed_count/remaining instead of boolean for multi-quantity
  redemption tracking
- Filter redeemable products to add-ons only (tickets use check-in)
- Add has_delete_permission=False to CheckIn/DoorCheck admin
- Add conference validation to record_door_check()
- Fix scanner UI: session-based re-entry detection, multi-qty display,
  error banner persistence, redemption toast key, order status warning

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 19, 2026 03:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds on-site operational support to the registration and manage apps by introducing check-in / door-check / redemption data models, staff-facing JSON endpoints for scanning + redemption, and a staff UI (scanner + dashboard) for real-time venue operations.

Changes:

  • Introduce CheckIn, DoorCheck, and ProductRedemption models (plus admin read-only views) and related migrations.
  • Add CheckInService / RedemptionService business logic and staff-only JSON API endpoints (scan/lookup/redeem/offline preload).
  • Add manage app scanner + dashboard pages and navigation entry; add test suite for core model/service/API behavior.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/test_registration/test_checkin.py New tests covering check-in, door checks, redemptions, and API endpoints
src/django_program/registration/views_checkin.py Staff-only JSON API endpoints for scan/lookup/redeem/offline preload
src/django_program/registration/urls.py Exposes check-in API routes under the registration app
src/django_program/registration/services/checkin.py Business logic for check-in and redemption (incl. locking for redemption)
src/django_program/registration/models.py Re-exports new check-in models via registration models module
src/django_program/registration/checkin.py New models for check-in, door checks, and redemptions
src/django_program/registration/admin.py Admin registrations for the new audit models (read-only)
src/django_program/registration/migrations/0016_checkin_doorcheck_productredemption.py Creates new DB tables for check-in/door check/redemption
src/django_program/registration/migrations/0017_alter_productredemption_unique_together.py Removes redemption uniqueness constraint to support quantity-based redemption
src/django_program/manage/views_checkin.py Manage UI views for scanner + dashboard
src/django_program/manage/urls.py Adds manage routes for dashboard + scanner
src/django_program/manage/templates/django_program/manage/checkin_scanner.html Scanner UI (vanilla JS) wired to JSON API
src/django_program/manage/templates/django_program/manage/checkin_dashboard.html Dashboard UI for check-in/redemption stats
src/django_program/manage/templates/django_program/manage/base.html Adds “Check-in” entry to manage sidebar nav

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

JacobCoffee and others added 2 commits March 18, 2026 22:15
- Use prefetched redemptions in Python instead of per-attendee annotated
  queries in OfflinePreloadView
- Add 6 tests for manage:checkin-dashboard and manage:checkin-scanner
  (permission checks, 200 responses, context stats)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e except syntax

- Use prefetched line items from lookup_attendee() instead of redundant
  select_related() queries in ScanView and LookupView
- Include order_status in scan success response for UI warning display
- Use _get_ticket_type_name() with prefetched iteration instead of filter query
- Normalize except syntax to tuple form for tooling compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 19, 2026 03:17
@JacobCoffee JacobCoffee merged commit ad9f95b into main Mar 19, 2026
13 of 15 checks passed
@JacobCoffee JacobCoffee deleted the phase-17/on-site-checkin-scanner branch March 19, 2026 03:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces end-to-end on-site operations for the registration system: persistent check-in / door-check / redemption models, staff-only JSON endpoints for scanners, and manage-app UI pages for scanning and dashboarding.

Changes:

  • Added CheckIn, DoorCheck, and ProductRedemption models (plus admin) and corresponding migrations.
  • Implemented check-in and redemption business logic services and staff-only JSON endpoints (scan/lookup/redeem/offline preload).
  • Added manage UI pages (scanner + dashboard) and integrated them into manage navigation and routing, with tests covering the new functionality.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/test_registration/test_checkin.py Adds model/service/API/manage-view coverage for check-in and redemption flows.
src/django_program/registration/views_checkin.py New staff-only JSON API endpoints backing the scanner UI and offline preload export.
src/django_program/registration/urls.py Wires new check-in API routes into the registration URLconf.
src/django_program/registration/services/checkin.py Adds CheckInService and RedemptionService business logic (incl. transactional redemption).
src/django_program/registration/models.py Re-exports new check-in/redemption models from the registration models module.
src/django_program/registration/checkin.py Defines new on-site operations models.
src/django_program/registration/admin.py Adds read-only admin views for auditing check-ins/door-checks/redemptions.
src/django_program/registration/migrations/0016_checkin_doorcheck_productredemption.py Creates new DB tables for check-in/door-check/redemption.
src/django_program/registration/migrations/0017_alter_productredemption_unique_together.py Removes uniqueness constraint to allow multi-quantity redemptions.
src/django_program/manage/views_checkin.py Adds manage dashboard + scanner views and dashboard statistics queries.
src/django_program/manage/urls.py Adds manage routes for dashboard and scanner pages.
src/django_program/manage/templates/django_program/manage/checkin_scanner.html Adds scanner UI (vanilla JS + fetch against JSON API).
src/django_program/manage/templates/django_program/manage/checkin_dashboard.html Adds dashboard UI for aggregate check-in and redemption stats.
src/django_program/manage/templates/django_program/manage/base.html Adds “Check-in” entry to manage sidebar navigation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +9 to +24
import json
import logging

from django.db.models import Count, Prefetch
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.views import View

from django_program.conference.models import Conference
from django_program.registration.attendee import Attendee
from django_program.registration.models import Order, OrderLineItem
from django_program.registration.services.checkin import CheckInService, RedemptionService

logger = logging.getLogger(__name__)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in PR #55 (2033cd4) — removed unused logging import and logger variable.

Comment on lines +105 to +109
return json.loads(request.body) # type: ignore[no-any-return]
except (json.JSONDecodeError, ValueError):
return None


Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in PR #55 (2033cd4) — _parse_json_body now validates the parsed value is a dict, returns None for arrays/strings/etc.

context["active_nav"] = "checkin"

total_attendees = Attendee.objects.filter(conference=conference).count()
checked_in_count = Attendee.objects.filter(conference=conference, checkins__isnull=False).distinct().count()
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in PR #55 (2033cd4) — replaced join+distinct with checked_in_at__isnull=False filter since the field is already set on first check-in.

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.

3 participants