- EmployeeAttendanceSerializer: add meeting_title (source: meeting.title)
fixes attendance.js rendering r.meeting_title as undefined
- MeetingParticipantSerializer: add user_email and user_display_name
so meeting detail participant list shows human-readable identity
- OrganizationProfileListCreateView: add GET handler to list org profiles
(previously POST-only, returning 405 on GET)
- UserProfileSerializer: add org_ids derived from role_bindings
so frontend can resolve which org profile to load for branding
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Move daphne before django.contrib.staticfiles in INSTALLED_APPS so Daphne
system check passes and makemigrations --check can run cleanly
- Generate tracking/0002: rename auto-generated AuditEvent index names to
match Django 5.2 format
- Generate accounts/0005: sync AccountUser managers and ManyToMany field
help_text to match current Django/guardian defaults
The CallSession / CallParticipant / CallEvent migration (training/0004)
already existed from the groupcall backend commit; this commit ensures
makemigrations --check reports no pending migrations across all apps.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Backend:
- Add migration 0002 for Meeting, MeetingParticipant, EmployeeAttendance models
- Add EmployeeAttendanceSerializer with meeting_title helper field
- Add MeetingDetailView (GET /meetings/{id}/ with participants embedded)
- Add EmployeeAttendanceListView (GET /training/attendance/) with
role-based access: own records always visible; other users require
progress:view_all / progress:view_team / users:manage capability
- Register meeting-detail and attendance-list routes in urls.py
Frontend:
- attendance.html + attendance.js: JWT login flow, attendance history
table with status badges, token refresh via sessionStorage only
(no localStorage for access tokens)
- meetings.html + meetings.js: meeting list, create-meeting form,
per-meeting participant management, inline attendance recording
with select + save per row; UUID input validated before submit
- attendance.css: shared stylesheet for both pages
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- AttendanceSession model with open/closed status, org_id, timestamps
- POST /api/v1/attendance/check-in/ – creates session, rejects duplicate open check-ins (409)
- POST /api/v1/attendance/check-out/ – closes open session, validates checkout > checkin
- GET /api/v1/attendance/status/ – current open session for requesting user
- GET /api/v1/attendance/history/ – paginated session history per user
- GET /api/v1/attendance/admin/sessions/ – admin view for managers/admins with filters
- Initial migration with composite indexes on (user, status) and (user, checked_in_at)
- 18 test cases covering core flows and edge cases
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Introduces a production Docker Compose stack that places nginx in front
of gunicorn. nginx serves the frontend SPA and Django static files
directly, and proxies all backend routes (/api, /admin, /healthz, etc.)
to the Django container. SECURE_SSL_REDIRECT is now env-configurable so
plain-HTTP nginx deployments work without separate settings files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Before creating a UserRoleBinding for a specific org_id, check whether
the org's OrganizationProfile.license_user_limit has been reached. New
users attempting to join a full org receive HTTP 403 with a descriptive
message. Re-assigning a user already in the org (role change) is
unaffected, as is assignment to an org with no OrganizationProfile.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- OrganizationProfileListCreateView (admin-only) handles POST
- OrganizationProfileCreateSerializer drops auto UniqueValidator so the
view can return 409 (instead of 400) for duplicate org_id
- Returns 201 on success, 400 on validation errors, 409 on duplicate
- Tests: create success, defaults, duplicate 409, missing field 400, non-admin 403
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replace the choices-constrained field (20/50/100/1000 only) with a plain
PositiveIntegerField so any positive integer is accepted. Adds migration
0004 to apply the schema change. Adds a test asserting arbitrary values
(e.g. 75) are accepted. Updates README with a field table and curl example
that documents common values without enforcing them.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Release stale Django migration advisory locks before migrating
- Run migrate via docker exec (not in container startup command)
- Update container CMD to start gunicorn directly (no migrate)
- This unblocks the 15-min migrate hang caused by leftover pg advisory lock
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Replace single healthz curl (no timeout) with polling loop (30x10s)
- Add --connect-timeout and --max-time to all curl calls
- Print container logs when gunicorn fails to start
- Print container logs in always() step for visibility
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Waitlist deployment uses requirements/waitlist.txt which does not include celery.
Making the import conditional allows the app to start without celery installed.
Co-Authored-By: Paperclip <noreply@paperclip.ing>