Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
3321d0e
Add test factories for assignments, parts, and submissions
espadonne Jun 27, 2026
5939e83
Fix AssignmentViewSet.list N+1 fan-out (Phase 1a)
espadonne Jun 27, 2026
1462b6c
Fix TeacherSubmissionViewSet.recent N+1 fan-out (Phase 1a)
espadonne Jun 27, 2026
b4406d5
Prefetch list querysets for enrollment/pieceplan/activity/grade views…
espadonne Jun 27, 2026
24fad3d
Add Phase 1 remodel campaign directive; mark 1a hot-path fixes done
espadonne Jun 27, 2026
823c1cc
Fix dashboard N+1s: select_related forward FKs, prefetch reverse, pag…
espadonne Jun 27, 2026
091f5f3
Prefetch roster GET enrollments (Phase 1a #7)
espadonne Jun 27, 2026
a0ec1b1
Collapse duplicate course/enrollment lookups in AssignmentViewSet.get…
espadonne Jun 27, 2026
5f624e6
Remove dead PiecePlan.assign; collapse Part.for_activity double query…
espadonne Jun 27, 2026
94c0386
Add composite indexes for latest-submission and attachment ordering (…
espadonne Jun 27, 2026
8724000
Black-format new query-count test files
espadonne Jun 27, 2026
815ae92
Update campaign: mark 1a #7-#14, #22, #25, #26 complete
espadonne Jun 27, 2026
78c5838
Fix UserViewSet update scoping bug + N+1 (Phase 1a #13)
espadonne Jun 27, 2026
a2198f4
Bulk-create assignments instead of per-student update_or_create (Phas…
espadonne Jun 27, 2026
d60bab6
Collapse change_piece_instrument save loop into one UPDATE (Phase 1b …
espadonne Jun 27, 2026
d3dd29c
Update campaign: 1b #15-#17/#20 + #13 done; note ATOMIC_REQUESTS + mi…
espadonne Jun 27, 2026
51444b9
Format test_change_instrument.py for CI black (latest)
espadonne Jun 27, 2026
5b227f8
Bulk-create telephone_fixed groups and assignments (Phase 1b #18, #21)
espadonne Jun 27, 2026
105b3e0
Bulk-create roster enrollments instead of get()+create() per user (Ph…
espadonne Jun 27, 2026
e9601d8
Update campaign: 1b #18, #19, #21 done
espadonne Jun 27, 2026
fd509f8
Annotate assignment list with planned order via subquery (Phase 1c #2…
espadonne Jun 27, 2026
b93ba91
Update campaign: 1c complete (#23, #24)
espadonne Jun 27, 2026
68c2098
Add Phase 2 design scoping (CourseAssignment): options, landmines, se…
espadonne Jun 27, 2026
200a34d
Neutralize migration 0033's live-helper call (Phase 2 prerequisite)
espadonne Jun 27, 2026
fed0363
Record Phase 2 decision (Option B) and prerequisite-done in design doc
espadonne Jun 27, 2026
16e0c19
Add CourseAssignment + GroupAssignment models (Phase 2 step 2)
espadonne Jun 27, 2026
a46d24b
Dual-write CourseAssignment/GroupAssignment from assign helpers (Phas…
espadonne Jun 27, 2026
78d19c2
Mark Phase 2 steps 2-3 done; expand remaining sequence in design doc
espadonne Jun 27, 2026
c57a671
Backfill CourseAssignment/GroupAssignment from existing Assignment ro…
espadonne Jun 27, 2026
0781d99
Add Submission course_assignment/enrollment/instrument/part + backfil…
espadonne Jun 27, 2026
8b69a9d
Re-key ActivityProgress to (course_assignment, enrollment) + backfill…
espadonne Jun 27, 2026
1b0a3f3
Mark Phase 2 steps 4-6 done in design doc
espadonne Jun 27, 2026
c3f54f3
Update Phase 2 doc header: Option B, steps 1-6 built
espadonne Jun 27, 2026
299570f
Add CourseAssignment read-resolution serializer with response-equival…
espadonne Jun 27, 2026
40057e3
Flip student assignment list to resolve from CourseAssignment
espadonne Jun 27, 2026
a0413e9
Make Submission/ActivityProgress assignment FK nullable for Phase 2 t…
espadonne Jun 27, 2026
cfccbe3
Make UserFactory default username unique to fix enrollment collisions
espadonne Jun 27, 2026
ee32876
Flip submission/activity-progress routes to resolve CourseAssignment …
espadonne Jun 27, 2026
1891829
Make CourseFactory start_date/end_date real dates not datetimes
espadonne Jun 27, 2026
9d54429
Flip student assignment retrieve to resolve from CourseAssignment
espadonne Jun 27, 2026
b518660
Update Phase 2 design: step 7 student read-path flip complete, teache…
espadonne Jun 27, 2026
c2a30b2
Format repo with black (latest) to satisfy CI format-check
espadonne Jun 27, 2026
960c5b2
Flip teacher assignment list to resolve from CourseAssignment
espadonne Jun 28, 2026
aeae54e
Update Phase 2 design: step 7 complete (teacher list flipped)
espadonne Jun 28, 2026
7dc62f7
Repoint GroupSerializer.get_members from Assignment to GroupAssignment
espadonne Jun 28, 2026
3853b78
Repoint TeacherSubmissionViewSet.recent off the Assignment FK
espadonne Jun 28, 2026
5ea3870
Repoint ActivityViewSet distinct-activity list to CourseAssignment
espadonne Jun 28, 2026
0dc288e
Move per-piece instrument override to CourseAssignment.instrument
espadonne Jun 28, 2026
e0b2718
Stop writing per-student Assignment rows in assign helpers
espadonne Jun 28, 2026
d7e1a86
Update Phase 2 design: step 8 reads/writes off Assignment done, drops…
espadonne Jun 28, 2026
6cb3e9c
Add unique(course_assignment, enrollment) constraint to ActivityProgress
espadonne Jun 28, 2026
19022e0
Repoint unassign to CourseAssignment; drop legacy assignment linkage …
espadonne Jun 28, 2026
f627690
Rebuild dashboards CSV/list export from CourseAssignment x enrollments
espadonne Jun 28, 2026
78b7d43
Remove dead Assignment-based serializers and viewset paths
espadonne Jun 28, 2026
53fffa7
Drop Assignment model and its FK columns on Submission/ActivityProgress
espadonne Jun 28, 2026
db0f9c8
Repoint admin off dropped Assignment to course_assignment/enrollment
espadonne Jun 28, 2026
edd8a33
Expose course_assignment/enrollment on ActivityProgressSerializer
espadonne Jun 28, 2026
b529b90
Drop AssignmentFactory; key SubmissionFactory by course_assignment/en…
espadonne Jun 28, 2026
9a870d8
Migrate test fixtures off Assignment to CourseAssignment read path
espadonne Jun 28, 2026
0c8e28b
Mark Phase 2 step 8 (drop Assignment) complete in design doc
espadonne Jun 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion config/api_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@

assignments_router = nested_cls(courses_router, "assignments", lookup="assignment")
assignments_router.register("submissions", SubmissionViewSet)
assignments_router.register("activity-progress", ActivityProgressViewSet, basename="activity-progress")
assignments_router.register(
"activity-progress", ActivityProgressViewSet, basename="activity-progress"
)

attachments_router = nested_cls(assignments_router, "submissions", lookup="submission")
attachments_router.register("attachments", AttachmentViewSet)
Expand Down
27 changes: 10 additions & 17 deletions config/settings/railway.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@

# Allow Railway's domain and custom domains
ALLOWED_HOSTS = env.list(
"DJANGO_ALLOWED_HOSTS",
default=["localhost", ".railway.app", ".up.railway.app"]
"DJANGO_ALLOWED_HOSTS", default=["localhost", ".railway.app", ".up.railway.app"]
)

# DATABASES
Expand Down Expand Up @@ -62,7 +61,9 @@
# STATIC FILES (whitenoise)
# ------------------------------------------------------------------------------
INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405
MIDDLEWARE.insert(1, "whitenoise.middleware.WhiteNoiseMiddleware") # After SecurityMiddleware
MIDDLEWARE.insert(
1, "whitenoise.middleware.WhiteNoiseMiddleware"
) # After SecurityMiddleware

STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
STATIC_URL = "/static/"
Expand All @@ -79,12 +80,10 @@
# ------------------------------------------------------------------------------
# Use console backend for development/testing (emails print to console)
EMAIL_BACKEND = env(
"DJANGO_EMAIL_BACKEND",
default="django.core.mail.backends.console.EmailBackend"
"DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend"
)
DEFAULT_FROM_EMAIL = env(
"DJANGO_DEFAULT_FROM_EMAIL",
default="MusicCPR <noreply@musiccpr.org>"
"DJANGO_DEFAULT_FROM_EMAIL", default="MusicCPR <noreply@musiccpr.org>"
)

# ADMIN
Expand All @@ -97,9 +96,7 @@
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "%(levelname)s %(asctime)s %(name)s %(message)s"
}
"verbose": {"format": "%(levelname)s %(asctime)s %(name)s %(message)s"}
},
"handlers": {
"console": {
Expand Down Expand Up @@ -133,17 +130,13 @@
r"^https://.*\.railway\.app$",
r"^http://localhost:\d+$",
r"^http://127\.0\.0\.1:\d+$",
]
],
)

# Also allow specific origins if set
CORS_ALLOWED_ORIGINS = env.list(
"CORS_ALLOWED_ORIGINS",
default=[]
)
CORS_ALLOWED_ORIGINS = env.list("CORS_ALLOWED_ORIGINS", default=[])

# CSRF trusted origins (needed for admin)
CSRF_TRUSTED_ORIGINS = env.list(
"CSRF_TRUSTED_ORIGINS",
default=["https://*.railway.app", "https://*.vercel.app"]
"CSRF_TRUSTED_ORIGINS", default=["https://*.railway.app", "https://*.vercel.app"]
)
16 changes: 12 additions & 4 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
def debug_media(request):
"""Diagnostic endpoint to check media files."""
import subprocess

result = {
"MEDIA_ROOT": str(settings.MEDIA_ROOT),
"MEDIA_ROOT_exists": os.path.exists(settings.MEDIA_ROOT),
Expand All @@ -29,7 +30,9 @@ def debug_media(request):
for f in filenames[:20]: # Limit to first 20
files.append(os.path.join(root, f).replace(settings.MEDIA_ROOT, ""))
result["files"] = files
result["file_count"] = sum(len(f) for _, _, f in os.walk(settings.MEDIA_ROOT))
result["file_count"] = sum(
len(f) for _, _, f in os.walk(settings.MEDIA_ROOT)
)
except Exception as e:
result["error"] = str(e)
else:
Expand All @@ -39,10 +42,13 @@ def debug_media(request):
teleband_media = os.path.join(os.getcwd(), "teleband", "media")
result["teleband_media_exists"] = os.path.exists(teleband_media)
if os.path.exists(teleband_media):
result["teleband_media_count"] = sum(len(f) for _, _, f in os.walk(teleband_media))
result["teleband_media_count"] = sum(
len(f) for _, _, f in os.walk(teleband_media)
)

return JsonResponse(result)


urlpatterns = [
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
path("debug-media/", debug_media, name="debug-media"),
Expand All @@ -68,9 +74,11 @@ def debug_media(request):
# Serve media files - in production with S3 this is handled by S3,
# but for Railway/local deployments we serve from filesystem
# Note: static() only works with DEBUG=True, so we use serve() directly for non-S3 deployments
if not hasattr(settings, 'DEFAULT_FILE_STORAGE') or 'S3' not in getattr(settings, 'DEFAULT_FILE_STORAGE', ''):
if not hasattr(settings, "DEFAULT_FILE_STORAGE") or "S3" not in getattr(
settings, "DEFAULT_FILE_STORAGE", ""
):
urlpatterns += [
re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
re_path(r"^media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}),
]

if settings.DEBUG:
Expand Down
Loading
Loading