Skip to content

feat: add course assignment system with submissions and grading#1040

Open
ayesha1145 wants to merge 1 commit intoalphaonelabs:mainfrom
ayesha1145:feat/course-assignment-system
Open

feat: add course assignment system with submissions and grading#1040
ayesha1145 wants to merge 1 commit intoalphaonelabs:mainfrom
ayesha1145:feat/course-assignment-system

Conversation

@ayesha1145
Copy link

@ayesha1145 ayesha1145 commented Mar 21, 2026

Adds a complete course assignment system allowing teachers to create assignments and students to submit work for grading.

Changes:

  • web/models.py: 2 new models — Assignment (title, description, due date, max score, status, late submissions) and AssignmentSubmission (text response, file upload, score, feedback, grading metadata)
  • web/migrations/0064_add_assignment_and_submission.py: migration for new models
  • web/admin.py: registered both models with list/filter/search config
  • web/views.py: 6 new views — course_assignments, create_assignment, assignment_detail, edit_assignment, delete_assignment, grade_submission
  • web/urls.py: 6 new URL patterns under courses//assignments/
  • web/templates/courses/assignment_list.html: list all assignments with status badges, due dates, submission counts
  • web/templates/courses/assignment_form.html: create/edit form with title, description, due date, max score, status, late submissions toggle
  • web/templates/courses/assignment_detail.html: assignment info + student submission form (text + file upload) + teacher submissions list
  • web/templates/courses/grade_submission.html: teacher grading form with score and feedback
  • web/templates/courses/assignment_confirm_delete.html: delete confirmation page
  • web/templates/courses/detail.html: added Assignments button to course detail teacher actions

Features:

  • Teachers: create/edit/delete assignments, publish/draft status, view all submissions, grade with score + feedback
  • Students: view published assignments, submit text response and/or file, view grade and feedback
  • Permission guards: only enrolled students and course teacher can access
  • Past due detection with optional late submission allowance
  • Dark mode support throughout

Overview

Adds a complete course assignment system so teachers can create/publish/manage assignments and grade student submissions; students can submit text/files and receive scores/feedback. Introduces models, admin registrations, views, templates, URLs, permission guards, and dark-mode-friendly UI.

Key Changes

  • Data models & migration

    • Assignment: course-linked assignment with title, description, due_date, max_score, status (draft/published), allow_late_submissions, timestamps, is_past_due and submission_count helpers.
    • AssignmentSubmission: per-student submission (text/file), unique per (assignment, student), status (submitted/graded/returned), score, feedback, graded_by/graded_at, timestamps, percentage helper.
    • Migration web/migrations/0064_add_assignment_and_submission.py created to add both models.
  • Admin

    • Registered Assignment and AssignmentSubmission in web/admin.py with list_display, list_filter and search_fields (including course title and student username). Registrations appear alongside existing admin customizations.
  • Views & URLs (6 views + routes)

    • New views: course_assignments, create_assignment, assignment_detail, edit_assignment, delete_assignment, grade_submission.
    • Routes added under courses//assignments/ and courses//submissions//grade/ with named URL patterns.
    • Role-based behavior: teachers see all assignments and submissions; enrolled students see published assignments only.
    • Submission handling: students may submit text and/or a file (upload allowlist enforced in views, max 10MB), uniqueness enforced (one submission per student per assignment), past-due and allow_late_submissions logic applied.
    • Grading: teachers set numeric score (validated against assignment.max_score) and feedback; graded_by and graded_at are recorded.
  • Templates & UI

    • New templates: assignment_list, assignment_form, assignment_detail, grade_submission, assignment_confirm_delete. Course detail (teacher actions) includes an "Assignments" button.
    • UI shows status badges, due-date/past-due indicators, submission counts, submission preview/download, grading UI, breadcrumbs, and dark-mode styles.

Behavior & Impact

  • Functional: Enables assignment lifecycle (create/publish/edit/delete), student submissions, and teacher grading. Enforced access controls restrict draft visibility to teachers and require enrollment for student access.
  • Validation & limits: file uploads limited (content-type allowlist + 10MB max); max_score and entered grades validated (bounds enforced).
  • Data: Adds DB schema changes—migration must be applied.
  • Admin: New admin models visible in Django admin; review placement/fields to ensure consistency with existing admin customizations.

Notes for reviewers

  • Inspect view validation logic (file content-type allowlist, size check) and ensure allowlist matches accepted file types.
  • Confirm edit_assignment contains the noted indentation/line-alignment anomaly around due_date assignment and verify no logic regressions.
  • Test end-to-end role-based flows (draft vs published, late submissions behavior, single-submission enforcement) and run migrations before deploying.

@github-actions
Copy link
Contributor

👀 Peer Review Required

Hi @ayesha1145! This pull request does not yet have a peer review.

Before this PR can be merged, please request a review from one of your peers:

  • Go to the PR page and click "Reviewers" on the right sidebar.
  • Select a team member or contributor to review your changes.
  • Once they approve, this reminder will be automatically removed.

Thank you for contributing! 🎉

@github-actions github-actions bot added the files-changed: 11 PR changes 11 files label Mar 21, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 21, 2026

Warning

Rate limit exceeded

@ayesha1145 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 11 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 78feebe9-2691-44b0-9b06-0e0a20b0f984

📥 Commits

Reviewing files that changed from the base of the PR and between b9aa130 and 173f915.

⛔ Files ignored due to path filters (1)
  • web/migrations/0064_add_assignment_and_submission.py is excluded by !**/migrations/**
📒 Files selected for processing (10)
  • web/admin.py
  • web/models.py
  • web/templates/courses/assignment_confirm_delete.html
  • web/templates/courses/assignment_detail.html
  • web/templates/courses/assignment_form.html
  • web/templates/courses/assignment_list.html
  • web/templates/courses/detail.html
  • web/templates/courses/grade_submission.html
  • web/urls.py
  • web/views.py

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'tools'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

Adds Assignment and AssignmentSubmission models, admin registrations, templates, URL routes, and six view functions to support creating, listing, viewing, editing, deleting, and grading assignments with student submission handling, file validation, and grading metadata.

Changes

Cohort / File(s) Summary
Models
web/models.py
Added Assignment and AssignmentSubmission models: course link, title/description, due date, max score, status, late-submission flag, timestamps, computed properties (is_past_due, submission_count, percentage), uniqueness constraint (assignment, student), and ordering.
Admin
web/admin.py
Imported and registered Assignment and AssignmentSubmission with AssignmentAdmin and AssignmentSubmissionAdmin (configured list_display, list_filter, search_fields).
Views
web/views.py
Added six view functions: course_assignments, create_assignment, assignment_detail, edit_assignment, delete_assignment, grade_submission. Implements access checks (teacher vs student), due-date parsing, validation (title/description, max_score), single-submission enforcement, file content-type/size checks, submission creation, and grading updates.
URLs
web/urls.py
Added language-prefixed routes under courses/<slug:slug>/ for course_assignments, create_assignment, assignment_detail, edit_assignment, delete_assignment, and grade_submission.
Templates — assignments & grading
web/templates/courses/assignment_list.html, assignment_detail.html, assignment_form.html, assignment_confirm_delete.html, grade_submission.html
Added templates for assignment list, detail (teacher controls & student submission view/form), create/edit form, delete confirmation, and grading UI.
Course detail template
web/templates/courses/detail.html
Added an "Assignments" action button to the teacher action buttons grid (visible to course teacher).

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant Django_View as Django View
    participant DB as Database (Assignments/Submissions)
    participant Storage as File Storage

    Browser->>Django_View: GET /courses/<slug>/assignments/ or /courses/<slug>/assignments/<id>/
    Django_View->>DB: query Assignments and AssignmentSubmissions
    DB-->>Django_View: assignments + submission_map
    Django_View-->>Browser: render assignment list/detail

    alt Student submits assignment
        Browser->>Django_View: POST submission (text + optional file)
        Django_View->>Storage: save uploaded file (if present)
        Storage-->>Django_View: file path/URL
        Django_View->>DB: create AssignmentSubmission
        DB-->>Django_View: created submission
        Django_View-->>Browser: redirect to assignment detail
    end

    alt Teacher grades submission
        Browser->>Django_View: POST grade (score + feedback)
        Django_View->>DB: update AssignmentSubmission (score, feedback, graded_by, graded_at, status=graded)
        DB-->>Django_View: updated submission
        Django_View-->>Browser: redirect to assignment detail
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing a course assignment system with student submissions and teacher grading capabilities.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

Hey @coderabbitai[bot]! You're now assigned to this issue. Please finish your PR within 1 day.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 14

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/admin.py`:
- Around line 617-619: Change the mutable list literals used for Django admin
option attributes to immutable tuples: replace list_display, list_filter, and
search_fields in the AssignmentAdmin and AssignmentSubmissionAdmin classes with
tuple literals (e.g., ("title", "course", ...)) so the admin configuration is
immutable and RUF012-compliant; ensure you update all occurrences across both
classes (list_display, list_filter, search_fields).

In `@web/models.py`:
- Around line 3202-3203: Add explicit return type annotations to the __str__
methods for Assignment and AssignmentSubmission: update the method signatures
Assignment.__str__ and AssignmentSubmission.__str__ to declare they return str
(e.g., def __str__(self) -> str:). Keep existing bodies unchanged but ensure the
return type annotation is present to satisfy static analysis and project style
guidelines.
- Around line 3217-3232: Add model-level validation to prevent empty submissions
by implementing AssignmentSubmission.clean to check the fields text_response and
file_submission; if both are empty/None raise
django.core.exceptions.ValidationError with a clear message. Update save() or
ensure full_clean() is called where necessary so the validation runs on save (or
document that views must call full_clean()), and reference AssignmentSubmission,
text_response, file_submission, clean, and ValidationError when making the
change.
- Around line 3251-3255: Add a return type hint and guard against
division-by-zero in the percentage property: annotate percentage (e.g., ->
Optional[int]) and change its logic in the percentage property to check that
self.score is not None and self.assignment.max_score is truthy and non-zero
before performing the division (return rounded int); otherwise return None.
Ensure references remain to the percentage property, self.score, and
self.assignment.max_score.
- Around line 3205-3210: Remove the redundant local import of
django.utils.timezone inside the is_past_due property: rely on the module
already imported at the top of the file and delete the inner "from django.utils
import timezone" line; also add a return type annotation to the property
signature so it reads as def is_past_due(self) -> bool:, and ensure the body
still returns a boolean by keeping the existing due_date check and comparison
using timezone.now().

In `@web/templates/courses/assignment_confirm_delete.html`:
- Around line 14-20: Replace the inline Tailwind classes on the destructive
submit button (the "Yes, Delete" <button> in this template) with the project's
danger button variant (using red-600 as the base) and replace the Cancel link
(the "Cancel" <a> pointing to the course_assignments URL) with the project's
secondary button variant (bg-gray-100 hover:bg-gray-200 dark:bg-gray-700
text-gray-700 dark:text-gray-300) so both actions use the shared button classes
and match the project's UX guidelines.

In `@web/templates/courses/assignment_detail.html`:
- Around line 65-69: The template currently links directly to
submission.file_submission.url which exposes the media URL; instead create a
protected endpoint (e.g., a view named download_submission or reuse a
download_submission view) that takes the submission id (or a secure token),
performs the same enrollment/teacher authorization checks as the assignment
detail view, and returns the file via Django's FileResponse (or Django's
streaming file response) with appropriate headers; then update
web/templates/courses/assignment_detail.html to link to that endpoint (e.g., {%
url 'download_submission' submission.id %}) rather than using
submission.file_submission.url so downloads go through the authorized view.

In `@web/templates/courses/assignment_form.html`:
- Around line 19-44: The form controls (elements with id/title, id/description
textarea, id/due_date, id/max_score, and id/status select) are using
inconsistent Tailwind classes; update each input, textarea and select to use the
repo's canonical form control class string "w-full px-4 py-2 border
border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2
focus:ring-blue-500" (preserving any additional semantic classes like text color
or rows only if required) so all controls follow the shared contract; ensure you
replace the existing class attribute values on the elements referenced above
(title, description, due_date, max_score, status) with the canonical class
string.

In `@web/templates/courses/assignment_list.html`:
- Around line 49-53: The template defines sub=submission_map|default:{} but
never uses it and checks membership using assignment.id|stringformat:"i" which
fails when submission_map keys are integers; update the block to use the local
variable and test membership against the integer key (e.g. replace the {% if
assignment.id|stringformat:"i" in submission_map %} check with {% if
assignment.id in sub %}) so the Submitted badge appears when submission_map is
keyed by integer assignment IDs.

In `@web/templates/courses/detail.html`:
- Around line 597-599: The Assignments CTA anchor (the <a> with href "{% url
'course_assignments' course.slug %}") uses non-standard button classes; update
its class list to match the project's primary button pattern by replacing the
current classes with: bg-teal-300 hover:bg-teal-400 text-white px-6 py-2
rounded-lg transition duration-200 so it conforms to the shared Tailwind primary
button style.

In `@web/templates/courses/grade_submission.html`:
- Around line 42-50: Replace the custom class sets on the score input
(id="score", name="score") and feedback textarea (id="feedback",
name="feedback") with the repository’s standard Tailwind form classes so they
match other forms; specifically use "w-full px-4 py-2 border border-gray-300
dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500" for both
controls and keep existing other attributes
(min/max/value/placeholder/rows/required) unchanged.

In `@web/views.py`:
- Around line 2044-2061: The current two-step check (using submission variable)
then calling AssignmentSubmission.objects.create can race; wrap the create in an
atomic operation and handle collisions by returning the "already submitted"
message instead of allowing duplicates or integrity errors. Replace the separate
existence-check + create with a single atomic create/lookup pattern: within a
transaction.atomic() block attempt to create the submission (or use
get_or_create with defaults for text_response and file_submission) and if a
duplicate is detected (handle IntegrityError or the get_or_create returned
existing flag), call messages.error(request, "You have already submitted this
assignment.") and redirect to assignment_detail; keep the existing validation
for past due / no late submissions and empty response checks before the atomic
create. Ensure you reference AssignmentSubmission.objects.create /
get_or_create, transaction.atomic, and IntegrityError when implementing the fix.
- Around line 1963-1985: Centralize and harden validation for assignment writes
by creating a shared helper (e.g., validate_assignment_data) and use it from the
create_assignment and edit_assignment view blocks; the helper should ensure
title and description are non-empty (reject or return errors), safely parse
due_date with django.utils.dateparse.parse_datetime, coerce max_score to int
with try/except and enforce sensible bounds/defaults, and validate status
against the Assignment model's allowed choices (e.g., Assignment.STATUS_CHOICES
or Assignment._meta.get_field("status").choices) so unrecognized values are
rejected or normalized; update the POST handling in the create_assignment and
edit_assignment views to call this helper, return messages.error and redirect
when validation fails, and only call Assignment.objects.create / save after
validation succeeds.
- Around line 2051-2061: The current handler reads file_submission from
request.FILES and immediately calls AssignmentSubmission.objects.create, so you
must validate the uploaded file before persisting: check file_submission.size
against a configured MAX_FILE_SIZE constant and verify
file_submission.content_type (and optionally the filename extension) is in an
allowed list (ALLOWED_CONTENT_TYPES/ALLOWED_EXTENSIONS); if validation fails
call messages.error(...) and return redirect("assignment_detail", slug=slug,
assignment_id=assignment_id) without calling
AssignmentSubmission.objects.create; only create the AssignmentSubmission after
the file passes these checks (symbols to change: text_response, file_submission,
AssignmentSubmission.objects.create, messages.error,
redirect("assignment_detail")).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 662b189b-bc65-433d-8736-19050f8c8dcb

📥 Commits

Reviewing files that changed from the base of the PR and between c94caf8 and b821429.

⛔ Files ignored due to path filters (1)
  • web/migrations/0064_add_assignment_and_submission.py is excluded by !**/migrations/**
📒 Files selected for processing (10)
  • web/admin.py
  • web/models.py
  • web/templates/courses/assignment_confirm_delete.html
  • web/templates/courses/assignment_detail.html
  • web/templates/courses/assignment_form.html
  • web/templates/courses/assignment_list.html
  • web/templates/courses/detail.html
  • web/templates/courses/grade_submission.html
  • web/urls.py
  • web/views.py

@ayesha1145 ayesha1145 force-pushed the feat/course-assignment-system branch from b821429 to f81039d Compare March 21, 2026 23:15
@github-actions
Copy link
Contributor

github-actions bot commented Mar 21, 2026

💬 Unresolved Review Conversations

Hi @ayesha1145! 👋

This pull request currently has 10 unresolved review conversations.

Please address all review feedback and push a new commit to resolve them before this PR can be merged.

Steps to resolve:

  1. Review each comment thread in the "Files changed" tab.
  2. Make the necessary changes to your code.
  3. Reply to each conversation to explain your changes or ask for clarification.
  4. Click "Resolve conversation" once the feedback has been addressed.
  5. Push a new commit with your changes.

Once all conversations are resolved, this notice will be removed automatically. Thank you! 🙏

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

This PR has 12 unresolved review conversations. Please resolve them before this PR can be merged.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

♻️ Duplicate comments (6)
web/admin.py (1)

615-627: 🧹 Nitpick | 🔵 Trivial

Convert admin option attributes to tuples for immutability.

The admin classes are well-configured with appropriate list_display, list_filter, and search_fields. However, these attributes use mutable list literals, which triggers Ruff's RUF012 warning. Using tuples ensures the configuration remains safely immutable.

Convert lists to tuples
 `@admin.register`(Assignment)
 class AssignmentAdmin(admin.ModelAdmin):
-    list_display = ["title", "course", "status", "due_date", "max_score", "created_at"]
-    list_filter = ["status", "course"]
-    search_fields = ["title", "course__title"]
+    list_display = ("title", "course", "status", "due_date", "max_score", "created_at")
+    list_filter = ("status", "course")
+    search_fields = ("title", "course__title")


 `@admin.register`(AssignmentSubmission)
 class AssignmentSubmissionAdmin(admin.ModelAdmin):
-    list_display = ["student", "assignment", "status", "score", "submitted_at"]
-    list_filter = ["status"]
-    search_fields = ["student__username", "assignment__title"]
+    list_display = ("student", "assignment", "status", "score", "submitted_at")
+    list_filter = ("status",)
+    search_fields = ("student__username", "assignment__title")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/admin.py` around lines 615 - 627, Change the mutable list literals on the
admin classes to tuples to avoid RUF012: update AssignmentAdmin.list_display,
AssignmentAdmin.list_filter, AssignmentAdmin.search_fields and
AssignmentSubmissionAdmin.list_display, AssignmentSubmissionAdmin.list_filter,
AssignmentSubmissionAdmin.search_fields to use tuple literals instead of list
literals (e.g., ("title", "course", ...) rather than ["title", "course", ...])
so the admin option attributes are immutable.
web/templates/courses/assignment_list.html (1)

49-53: ⚠️ Potential issue | 🟠 Major

Fix the submission status lookup to use the defined variable.

There's a logic issue here: sub is defined but never used, and the membership check uses stringformat:"i" which converts the ID to a string. If submission_map is keyed by integers (which is typical in Django), this check will always fail, causing students to never see the "Submitted" badge.

Fix the lookup logic
-{% with sub=submission_map|default:{}  %}
-{% if assignment.id|stringformat:"i" in submission_map %}
+{% with sub=submission_map|default:{} %}
+{% if assignment.id in sub %}
 <span class="text-green-600 dark:text-green-400"><i class="fas fa-check mr-1" aria-hidden="true"></i> Submitted</span>
 {% endif %}
 {% endwith %}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/assignment_list.html` around lines 49 - 53, The
template defines sub with "{% with sub=submission_map|default:{} %}" but then
checks membership against submission_map using a string-converted key; change
the membership check to use the defined sub variable and the actual assignment
id (e.g., "if assignment.id in sub") so the lookup uses the same map and the
correct key type; update the block around the "Submitted" badge to use sub and
remove the unnecessary stringformat conversion.
web/templates/courses/assignment_form.html (1)

19-44: 🧹 Nitpick | 🔵 Trivial

Optional: Align form input classes with project guidelines.

The form is well-structured with proper validation attributes and CSRF protection. The input styling uses px-3 py-2 and focus:ring-teal-300, while the project guidelines specify px-4 py-2 and focus:ring-blue-500.

If you want strict adherence to the shared form control contract, you can update the classes. However, this is a minor styling consistency matter—the form functions correctly as-is.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/assignment_form.html` around lines 19 - 44, Update the
form control classes on the inputs/textarea/select to match the project contract
by replacing padding and focus ring values: change "px-3" to "px-4" for elements
with ids title, description, due_date, max_score and the status select, and
replace "focus:ring-teal-300" with "focus:ring-blue-500" on the same elements so
the class sets are consistent with the shared form control guideline.
web/templates/courses/assignment_detail.html (1)

65-69: ⚠️ Potential issue | 🟡 Minor

Consider protecting file downloads with a dedicated view.

Similar to other templates in this PR, the direct {{ submission.file_submission.url }} exposes files as public media URLs. The project's download_material view demonstrates the preferred pattern—serving files through an authorized endpoint.

For defense-in-depth, consider creating a download_submission view that checks enrollment/teacher status before serving the file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/assignment_detail.html` around lines 65 - 69, Replace
the direct public URL usage of submission.file_submission.url in
assignment_detail.html with a link to a secured endpoint: create a
download_submission view (analogous to download_material) that checks
permissions (enrollment or teacher), streams the file, and returns a protected
response; then update the template to link to that view (e.g., via a named URL
like "download_submission" accepting submission.id) so files are served only
after authorization by the new view.
web/templates/courses/assignment_confirm_delete.html (1)

14-20: 🧹 Nitpick | 🔵 Trivial

Minor: Consider aligning button styles with project conventions.

The delete action works correctly, but for visual consistency across the project:

  1. The danger button uses bg-red-500 while the project convention suggests red-600 as the danger color.
  2. The "Cancel" link could use the secondary button styling for better visual balance.

These are small polish items and not blockers—the functionality is solid and the form includes proper CSRF protection.

Optional styling alignment
 <button type="submit"
-  class="bg-red-500 hover:bg-red-600 dark:bg-red-700 dark:hover:bg-red-600 text-white font-semibold px-6 py-2 rounded-lg transition duration-200 focus:outline-none focus:ring-2 focus:ring-red-400">
+  class="bg-red-600 hover:bg-red-700 text-white font-semibold px-6 py-2 rounded-lg transition duration-200 focus:outline-none focus:ring-2 focus:ring-red-400">
   Yes, Delete
 </button>
 </form>
 <a href="{% url 'course_assignments' course.slug %}"
-   class="text-gray-500 hover:text-gray-700 dark:text-gray-400 text-sm">Cancel</a>
+   class="bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-6 py-2 rounded-lg transition duration-200">Cancel</a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/assignment_confirm_delete.html` around lines 14 - 20,
Update the submit button (the element with type="submit") to use the project
danger color by changing the red utility from bg-red-500 to bg-red-600 (and
adjust related hover/dark variants if needed to match project tokens), and
change the Cancel link (the <a> with class="text-gray-500 hover:text-gray-700
dark:text-gray-400 text-sm") to use the project’s standard secondary button
styling by replacing its current text/hover classes with the shared secondary
button class set used elsewhere in the codebase.
web/views.py (1)

2129-2134: ⚠️ Potential issue | 🟠 Major

Reuse the same validation rules in edit_assignment.

Line 2132 can still raise on a tampered max_score, and Lines 2129-2134 write title, description, and status straight from the request without the checks already present in create_assignment. Please route edits through the same validated helper so both write paths reject bad input consistently instead of returning a 500 or saving broken data. As per coding guidelines: Review Python code for PEP 8 compliance, proper type hints, and Django best practices where applicable. Ensure proper error handling and logging.

Also applies to: 2136-2136

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` around lines 2129 - 2134, The edit_assignment flow writes
title, description, status and casts max_score directly from request which can
raise on tampered input and bypasses the validation used in create_assignment;
refactor edit_assignment to call the same validation/sanitization helper used by
create_assignment (e.g., validate_assignment_data or sanitize_assignment_input)
to parse and validate title, description, due_date, status,
allow_late_submissions and max_score, handle invalid max_score with try/except
(returning a proper error response or form error), and log validation failures;
ensure types are explicit (int conversion only after validation), reuse the same
error messages/field checks as create_assignment, and update edit_assignment to
persist only validated fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/models.py`:
- Around line 3211-3213: The property submission_count lacks a return type
annotation; update its definition (the `@property` method named submission_count)
to include the appropriate typing (e.g., -> int) matching other properties like
is_past_due, so the signature indicates the return type while keeping the body
returning self.submissions.count().
- Line 3228: Move the file validation out of the view and into the model or form
by adding a validator for the FileField 'file_submission' (models.FileField)
that enforces allowed content types and the 10MB size limit; implement the logic
as a reusable function (e.g., validate_submission_file) and attach it to the
FileField via the validators parameter or implement a clean_file_submission
method on the corresponding ModelForm to centralize checks; then remove the
duplicate checks from the view (the current checks around file type and size) so
all uploads go through the model/form validator.
- Line 3230: Add model-level validation to prevent score > assignment.max_score
by implementing a clean() on the model that checks self.score against
self.assignment.max_score and raises django.core.exceptions.ValidationError when
score is greater; ensure save() calls full_clean() (or that callers call
full_clean()) so the check runs on save. Reference the score field and the
model's clean() and save() methods and use self.assignment.max_score to perform
the comparison.

In `@web/templates/courses/assignment_detail.html`:
- Around line 93-158: The assignment_detail.html template is corrupted: finish
the incomplete file input element (id="file_submission") by closing its class
attribute and input tag, remove the stray shell command line ("cat >
web/templates/courses/grade_submission.html << 'HTMLEOF'") and all embedded
grade_submission.html content that was accidentally pasted into this file, and
restore assignment_detail.html to only include the assignment detail markup
(ensuring all opened tags and Django blocks are properly closed); keep
grade_submission.html as a separate template file instead of embedded here.

In `@web/templates/courses/grade_submission.html`:
- Around line 25-29: The template currently links directly to
submission.file_submission.url which exposes a media URL; change the template in
grade_submission.html to point to a protected download_submission endpoint (e.g.
using {% url 'download_submission' submission.id %}) instead of
submission.file_submission.url, and implement a download_submission view that
performs the same access checks as the grade_submission view (teacher/course
enrollment/ownership as appropriate) and serves the file via Django's
FileResponse (or streams it) similar to download_material so sharing the raw
media URL cannot bypass authorization.

In `@web/views.py`:
- Around line 2135-2146: The assignment.due_date assignment is over-indented
causing a SyntaxError; unindent the line so it lines up with the following
statements (e.g., align "assignment.due_date = parse_datetime(due_date) if
due_date else None" with "assignment.save()" and "messages.success(...)") inside
the same view function block; ensure parse_datetime, assignment.save(),
messages.success(...) and the subsequent return redirect("course_assignments",
slug=slug) remain at the same indentation level so the function parses
correctly.

---

Duplicate comments:
In `@web/admin.py`:
- Around line 615-627: Change the mutable list literals on the admin classes to
tuples to avoid RUF012: update AssignmentAdmin.list_display,
AssignmentAdmin.list_filter, AssignmentAdmin.search_fields and
AssignmentSubmissionAdmin.list_display, AssignmentSubmissionAdmin.list_filter,
AssignmentSubmissionAdmin.search_fields to use tuple literals instead of list
literals (e.g., ("title", "course", ...) rather than ["title", "course", ...])
so the admin option attributes are immutable.

In `@web/templates/courses/assignment_confirm_delete.html`:
- Around line 14-20: Update the submit button (the element with type="submit")
to use the project danger color by changing the red utility from bg-red-500 to
bg-red-600 (and adjust related hover/dark variants if needed to match project
tokens), and change the Cancel link (the <a> with class="text-gray-500
hover:text-gray-700 dark:text-gray-400 text-sm") to use the project’s standard
secondary button styling by replacing its current text/hover classes with the
shared secondary button class set used elsewhere in the codebase.

In `@web/templates/courses/assignment_detail.html`:
- Around line 65-69: Replace the direct public URL usage of
submission.file_submission.url in assignment_detail.html with a link to a
secured endpoint: create a download_submission view (analogous to
download_material) that checks permissions (enrollment or teacher), streams the
file, and returns a protected response; then update the template to link to that
view (e.g., via a named URL like "download_submission" accepting submission.id)
so files are served only after authorization by the new view.

In `@web/templates/courses/assignment_form.html`:
- Around line 19-44: Update the form control classes on the
inputs/textarea/select to match the project contract by replacing padding and
focus ring values: change "px-3" to "px-4" for elements with ids title,
description, due_date, max_score and the status select, and replace
"focus:ring-teal-300" with "focus:ring-blue-500" on the same elements so the
class sets are consistent with the shared form control guideline.

In `@web/templates/courses/assignment_list.html`:
- Around line 49-53: The template defines sub with "{% with
sub=submission_map|default:{} %}" but then checks membership against
submission_map using a string-converted key; change the membership check to use
the defined sub variable and the actual assignment id (e.g., "if assignment.id
in sub") so the lookup uses the same map and the correct key type; update the
block around the "Submitted" badge to use sub and remove the unnecessary
stringformat conversion.

In `@web/views.py`:
- Around line 2129-2134: The edit_assignment flow writes title, description,
status and casts max_score directly from request which can raise on tampered
input and bypasses the validation used in create_assignment; refactor
edit_assignment to call the same validation/sanitization helper used by
create_assignment (e.g., validate_assignment_data or sanitize_assignment_input)
to parse and validate title, description, due_date, status,
allow_late_submissions and max_score, handle invalid max_score with try/except
(returning a proper error response or form error), and log validation failures;
ensure types are explicit (int conversion only after validation), reuse the same
error messages/field checks as create_assignment, and update edit_assignment to
persist only validated fields.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 274cf3f8-3c5e-450f-8cfe-d8ccd005a5ec

📥 Commits

Reviewing files that changed from the base of the PR and between b821429 and f81039d.

⛔ Files ignored due to path filters (1)
  • web/migrations/0064_add_assignment_and_submission.py is excluded by !**/migrations/**
📒 Files selected for processing (10)
  • web/admin.py
  • web/models.py
  • web/templates/courses/assignment_confirm_delete.html
  • web/templates/courses/assignment_detail.html
  • web/templates/courses/assignment_form.html
  • web/templates/courses/assignment_list.html
  • web/templates/courses/detail.html
  • web/templates/courses/grade_submission.html
  • web/urls.py
  • web/views.py

@ayesha1145 ayesha1145 force-pushed the feat/course-assignment-system branch from f81039d to b9aa130 Compare March 21, 2026 23:24
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (12)
web/templates/courses/grade_submission.html (1)

42-50: ⚠️ Potential issue | 🟡 Minor

Use the shared form-control class contract for score and feedback fields.

These controls still use custom class sets instead of the repository-standard input styling.

As per coding guidelines: "Use Tailwind form input classes: w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 for form inputs."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/grade_submission.html` around lines 42 - 50, Replace
the custom class lists on the score input (id="score", name="score") and
feedback textarea (id="feedback", name="feedback") with the repository-standard
form-control class contract used across the project (the Tailwind classes:
w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg
focus:ring-2 focus:ring-blue-500), preserving other attributes like
id/name/value/placeholder/rows/required; ensure both elements use the identical
shared class string so styling is consistent with other form inputs.
web/templates/courses/assignment_detail.html (2)

93-158: ⚠️ Potential issue | 🔴 Critical

Remove the embedded shell/template payload — this template is currently broken.

The file input is cut off, then a shell heredoc command and an entire different template are embedded. This will fail template parsing/rendering and must be removed before merge.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/assignment_detail.html` around lines 93 - 158, The
template contains an accidental shell/heredoc payload starting at the truncated
file input element (id="file_submission") and an entire duplicate template
"grade_submission.html" embedded inside assignment_detail.html; remove the shell
heredoc and the duplicated template content, restore or complete the original
file input element markup (the input with id="file_submission" and
name="file_submission") so assignment_detail.html ends cleanly, and ensure only
the intended assignment_detail.html template remains (no embedded "cat > ... <<
'HTMLEOF'" or second template).

65-68: ⚠️ Potential issue | 🟠 Major

Serve submitted files through an authorized download endpoint, not direct media URLs.

Using {{ submission.file_submission.url }} exposes a raw media path and bypasses view-level authorization once shared. Route this through a protected download_submission view (with teacher/enrollment checks) and link to that URL instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/assignment_detail.html` around lines 65 - 68, The
template currently links directly to submission.file_submission.url which
exposes raw media URLs and bypasses view authorization; change the anchor to
point to a protected download endpoint (e.g., reverse/URL name
"download_submission") that maps to a view like download_submission(request,
submission_id) which performs teacher/enrollment/owner checks and streams the
file via Django's FileResponse or a signed URL, and update the template to use
that URL (pass submission.id or a secure token) instead of
submission.file_submission.url so downloads always go through the authorization
logic.
web/templates/courses/assignment_confirm_delete.html (1)

14-20: ⚠️ Potential issue | 🟡 Minor

Align delete/cancel actions with shared danger and secondary button variants.

The actions work, but they still use local styles instead of the shared destructive/secondary button patterns.

Suggested update
-        <button type="submit"
-          class="bg-red-500 hover:bg-red-600 dark:bg-red-700 dark:hover:bg-red-600 text-white font-semibold px-6 py-2 rounded-lg transition duration-200 focus:outline-none focus:ring-2 focus:ring-red-400">
+        <button type="submit"
+          class="bg-red-600 hover:bg-red-700 text-white px-6 py-2 rounded-lg transition duration-200 focus:outline-none focus:ring-2 focus:ring-red-400">
           Yes, Delete
         </button>
@@
-      <a href="{% url 'course_assignments' course.slug %}"
-         class="text-gray-500 hover:text-gray-700 dark:text-gray-400 text-sm">Cancel</a>
+      <a href="{% url 'course_assignments' course.slug %}"
+         class="bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-6 py-2 rounded-lg transition duration-200">Cancel</a>

As per coding guidelines: "Follow the project's color scheme ... Danger: red-600" and "Use Tailwind secondary button classes: bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/assignment_confirm_delete.html` around lines 14 - 20,
The delete and cancel actions use local Tailwind classes instead of the
project's shared button variants; update the "Yes, Delete" submit button (the
form submit with text "Yes, Delete") to use the shared destructive/danger
variant (use red-600 as the base, proper hover/dark states per project
convention) and update the cancel anchor (the link with text "Cancel" that
points to the course_assignments URL) to use the shared secondary button classes
(bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 text-gray-700
dark:text-gray-300) so both follow the project's color and component guidelines.
web/templates/courses/assignment_form.html (1)

19-44: ⚠️ Potential issue | 🟡 Minor

Standardize form controls to the shared input/select/textarea class contract.

These controls are still using custom class sets instead of the repo’s canonical form-control classes.

As per coding guidelines: "Use Tailwind form input classes: w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 for form inputs."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/assignment_form.html` around lines 19 - 44, The form
controls (inputs/textarea/select) for title, description, due_date, max_score
and status are using a custom class set; replace their class attributes to use
the repo canonical form-control Tailwind contract (e.g., w-full px-4 py-2 border
border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2
focus:ring-blue-500) so all controls (input#title, textarea#description,
input#due_date, input#max_score, select#status) follow the shared styling;
ensure you apply the same canonical class string for text inputs, textarea and
select elements and remove the old custom class fragments.
web/templates/courses/detail.html (1)

597-599: ⚠️ Potential issue | 🟡 Minor

Use the shared primary button spacing for the Assignments CTA.

This CTA still deviates from the shared primary button contract by using px-4 instead of px-6.

Suggested class adjustment
- class="inline-flex items-center justify-center px-4 py-2 bg-teal-300 hover:bg-teal-400 dark:bg-teal-600 dark:hover:bg-teal-500 text-white rounded-lg transition duration-200 focus:outline-none focus:ring-2 focus:ring-teal-300">
+ class="inline-flex items-center justify-center bg-teal-300 hover:bg-teal-400 text-white px-6 py-2 rounded-lg transition duration-200 dark:bg-teal-600 dark:hover:bg-teal-500 focus:outline-none focus:ring-2 focus:ring-teal-300">

As per coding guidelines: "Use Tailwind primary button classes: bg-teal-300 hover:bg-teal-400 text-white px-6 py-2 rounded-lg transition duration-200."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/detail.html` around lines 597 - 599, The Assignments
CTA anchor (the <a> with href "{% url 'course_assignments' course.slug %}") uses
px-4 and therefore doesn't match the shared primary button contract; update its
class list to replace px-4 with px-6 so it matches the canonical primary button
classes (bg-teal-300 hover:bg-teal-400 text-white px-6 py-2 rounded-lg
transition duration-200) while keeping the existing dark: and focus: classes
intact.
web/templates/courses/assignment_list.html (1)

49-53: ⚠️ Potential issue | 🟠 Major

Fix submitted-status lookup to use the local map and consistent key type.

The current condition can miss real submissions when submission_map keys are integers.

Suggested fix
-{% with sub=submission_map|default:{}  %}
-{% if assignment.id|stringformat:"i" in submission_map %}
+{% with sub=submission_map|default:{} %}
+{% if assignment.id in sub %}
 <span class="text-green-600 dark:text-green-400"><i class="fas fa-check mr-1" aria-hidden="true"></i> Submitted</span>
 {% endif %}
 {% endwith %}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/courses/assignment_list.html` around lines 49 - 53, The
template uses submission_map directly and stringifies assignment.id, which can
miss matches when submission_map keys are integers; change the membership check
to use the local 'sub' variable created by the with block and a consistent key
type: replace the condition that reads {% if assignment.id|stringformat:"i" in
submission_map %} with a check against sub (e.g. {% if assignment.id in sub %}
or, if your map uses string keys, {% if assignment.id|stringformat:"i" in sub
%}) so the lookup uses the local variable 'sub' and a matching key type.
web/models.py (2)

3212-3213: ⚠️ Potential issue | 🟡 Minor

Add a return type annotation to submission_count.

On Line [3212], please annotate the property return type for consistency with nearby typed properties.

💡 Proposed fix
     `@property`
-    def submission_count(self):
+    def submission_count(self) -> int:
         return self.submissions.count()

As per coding guidelines: "Use type hints in Python where appropriate".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/models.py` around lines 3212 - 3213, Add a return type annotation to the
submission_count method: change the signature of def submission_count(self) to
include the appropriate return type (e.g., -> int) so it reads def
submission_count(self) -> int:, and if this is a `@property` keep the annotation
on the property method; update any related type hints or imports if necessary to
satisfy linters/type-checkers.

3225-3233: ⚠️ Potential issue | 🟠 Major

Add model-level validation for submission content and score bounds.

Currently, a submission can be saved with neither text nor file (Lines [3227]-[3228]), and score can exceed assignment.max_score (Line [3230]). Enforce both constraints in clean() and run validation on save.

💡 Proposed fix
 class AssignmentSubmission(models.Model):
@@
     class Meta:
         unique_together = ["assignment", "student"]
         ordering = ["-submitted_at"]

     def __str__(self) -> str:
         return f"{self.student.username} - {self.assignment.title}"
+
+    def clean(self) -> None:
+        errors = {}
+        if not self.text_response and not self.file_submission:
+            errors["text_response"] = "Provide a text response or upload a file."
+        if self.score is not None and self.score > self.assignment.max_score:
+            errors["score"] = f"Score cannot exceed {self.assignment.max_score}."
+        if errors:
+            raise ValidationError(errors)
+
+    def save(self, *args, **kwargs) -> None:
+        self.full_clean()
+        super().save(*args, **kwargs)

Also applies to: 3243-3245

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/models.py` around lines 3225 - 3233, Add model-level validation in the
Submission model by implementing a clean(self) method that raises
ValidationError if both text_response and file_submission are empty and if score
is not None but is < 0 or > self.assignment.max_score; then override save(self,
*args, **kwargs) to call self.full_clean() before super().save(...) so
validation runs on save. Apply the same clean/save pattern to the other
submission-like model nearby to enforce identical content and score bounds
checks (reference fields: text_response, file_submission, score and attribute
assignment.max_score).
web/views.py (3)

2135-2146: ⚠️ Potential issue | 🔴 Critical

Fix the indentation blocker before anything else.

The over-indented assignment.due_date line makes web/views.py invalid Python, so Django cannot import this module and every view in it stops loading.

🩹 Suggested fix
-                assignment.due_date = parse_datetime(due_date) if due_date else None
+        assignment.due_date = parse_datetime(due_date) if due_date else None

This parser check should currently fail around Line 2135; after the indentation fix it should print AST parse OK.

#!/bin/bash
set -euo pipefail
python - <<'PY'
from pathlib import Path
import ast

path = Path("web/views.py")
source = path.read_text()
ast.parse(source)
print("AST parse OK")
PY

As per coding guidelines, "**/*.py: Fix linting errors in code.`"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` around lines 2135 - 2146, The file has a mis-indented line:
"assignment.due_date = parse_datetime(due_date) if due_date else None" is
over-indented and breaks Python parsing; adjust its indentation to match the
surrounding block (i.e., align it with the following "assignment.save()" and
other statements within the same view function) so the assignment update block
is syntactically correct; check the function that processes the form (the block
that uses assignment, parse_datetime, assignment.save, messages.success and
return redirect) and ensure all statements inside that conditional/handler have
consistent indentation, then re-run the AST parse check.

2129-2134: ⚠️ Potential issue | 🟠 Major

Please run the same validation on edit that you do on create.

This branch still writes title, description, status, and max_score directly from the request. A tampered POST can save blank fields or invalid statuses, and int(request.POST.get("max_score", 100)) can still raise or persist a negative score before the user sees an error. Reuse the same validated path as create_assignment before mutating assignment.

As per coding guidelines, "**/*.py: Review Python code for PEP 8 compliance, proper type hints, and Django best practices where applicable. Check for security vulnerabilities such as SQL injection, XSS, and insecure use of eval(). Ensure proper error handling and logging. Verify tests cover the key logic paths.`"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` around lines 2129 - 2134, The edit handler is directly
assigning request.POST values to assignment (title, description, status,
max_score, allow_late_submissions) without the same validation used on create;
update the edit flow to reuse the create_assignment validation path (e.g., call
the same form or validator used by create_assignment) instead of assigning raw
POST values: validate/clean title, description, status and max_score first
(handle int conversion and non-negative constraint) and only then mutate
assignment and save; reference the existing create_assignment validation routine
or AssignmentForm.clean() so edits and creates share identical checks.

2061-2070: ⚠️ Potential issue | 🟠 Major

Don't trust content_type as the only upload gate.

file_submission.content_type is client-controlled, so a crafted multipart request can label arbitrary bytes as application/pdf or image/png and still pass this check. Please validate the actual file signature or a server-side MIME detection result before saving, with extension checks only as a secondary filter.

Does Django's UploadedFile.content_type come from the client request, and is it safe to use as the sole server-side file-type validation check in a Django upload handler?

As per coding guidelines, "**/*.py: Review Python code for PEP 8 compliance, proper type hints, and Django best practices where applicable. Check for security vulnerabilities such as SQL injection, XSS, and insecure use of eval(). Ensure proper error handling and logging. Verify tests cover the key logic paths.`"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` around lines 2061 - 2070, The current check uses
client-controlled file_submission.content_type; replace this with server-side
MIME/signature validation: use a library like python-magic (libmagic) or
Django's built-in file inspection to detect the real MIME from the file bytes
for the variable file_submission, compare that detected MIME against
allowed_types, and only then allow the upload; additionally verify the file
extension matches expected types as a secondary check, ensure you read a limited
prefix of file_submission (e.g., first few KB) and seek back to start before
saving, and keep the existing size check for file_submission.size.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/models.py`:
- Line 3193: The max_score PositiveIntegerField currently allows 0; add a
minimum value validator to enforce >=1 by updating the max_score field
declaration (symbol: max_score, type: models.PositiveIntegerField) to include
validators=[MinValueValidator(1)] and ensure
django.core.validators.MinValueValidator is imported at the top of the module;
keep the existing default=100 as-is.

In `@web/views.py`:
- Around line 1959-2159: Edit the indentation bug in edit_assignment: fix the
over-indented line assigning assignment.due_date so it aligns with the other
assignment.* assignments inside the POST block (look for function
edit_assignment and the assignment.due_date line). Add tests covering key paths
for the assignment views: create_assignment, assignment_detail (student
submission flows including duplicate submission, file type and size validation,
missing text/file), course_assignments permission split (teacher vs enrolled
student), and grade_submission (score bounds, grading state changes,
graded_by/graded_at). Ensure tests assert redirects/messages and model state
changes (e.g., AssignmentSubmission created, status/score set,
Assignment.max_score enforcement) and run formatter/PEP8 checks after the fix.

---

Duplicate comments:
In `@web/models.py`:
- Around line 3212-3213: Add a return type annotation to the submission_count
method: change the signature of def submission_count(self) to include the
appropriate return type (e.g., -> int) so it reads def submission_count(self) ->
int:, and if this is a `@property` keep the annotation on the property method;
update any related type hints or imports if necessary to satisfy
linters/type-checkers.
- Around line 3225-3233: Add model-level validation in the Submission model by
implementing a clean(self) method that raises ValidationError if both
text_response and file_submission are empty and if score is not None but is < 0
or > self.assignment.max_score; then override save(self, *args, **kwargs) to
call self.full_clean() before super().save(...) so validation runs on save.
Apply the same clean/save pattern to the other submission-like model nearby to
enforce identical content and score bounds checks (reference fields:
text_response, file_submission, score and attribute assignment.max_score).

In `@web/templates/courses/assignment_confirm_delete.html`:
- Around line 14-20: The delete and cancel actions use local Tailwind classes
instead of the project's shared button variants; update the "Yes, Delete" submit
button (the form submit with text "Yes, Delete") to use the shared
destructive/danger variant (use red-600 as the base, proper hover/dark states
per project convention) and update the cancel anchor (the link with text
"Cancel" that points to the course_assignments URL) to use the shared secondary
button classes (bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 text-gray-700
dark:text-gray-300) so both follow the project's color and component guidelines.

In `@web/templates/courses/assignment_detail.html`:
- Around line 93-158: The template contains an accidental shell/heredoc payload
starting at the truncated file input element (id="file_submission") and an
entire duplicate template "grade_submission.html" embedded inside
assignment_detail.html; remove the shell heredoc and the duplicated template
content, restore or complete the original file input element markup (the input
with id="file_submission" and name="file_submission") so assignment_detail.html
ends cleanly, and ensure only the intended assignment_detail.html template
remains (no embedded "cat > ... << 'HTMLEOF'" or second template).
- Around line 65-68: The template currently links directly to
submission.file_submission.url which exposes raw media URLs and bypasses view
authorization; change the anchor to point to a protected download endpoint
(e.g., reverse/URL name "download_submission") that maps to a view like
download_submission(request, submission_id) which performs
teacher/enrollment/owner checks and streams the file via Django's FileResponse
or a signed URL, and update the template to use that URL (pass submission.id or
a secure token) instead of submission.file_submission.url so downloads always go
through the authorization logic.

In `@web/templates/courses/assignment_form.html`:
- Around line 19-44: The form controls (inputs/textarea/select) for title,
description, due_date, max_score and status are using a custom class set;
replace their class attributes to use the repo canonical form-control Tailwind
contract (e.g., w-full px-4 py-2 border border-gray-300 dark:border-gray-600
rounded-lg focus:ring-2 focus:ring-blue-500) so all controls (input#title,
textarea#description, input#due_date, input#max_score, select#status) follow the
shared styling; ensure you apply the same canonical class string for text
inputs, textarea and select elements and remove the old custom class fragments.

In `@web/templates/courses/assignment_list.html`:
- Around line 49-53: The template uses submission_map directly and stringifies
assignment.id, which can miss matches when submission_map keys are integers;
change the membership check to use the local 'sub' variable created by the with
block and a consistent key type: replace the condition that reads {% if
assignment.id|stringformat:"i" in submission_map %} with a check against sub
(e.g. {% if assignment.id in sub %} or, if your map uses string keys, {% if
assignment.id|stringformat:"i" in sub %}) so the lookup uses the local variable
'sub' and a matching key type.

In `@web/templates/courses/detail.html`:
- Around line 597-599: The Assignments CTA anchor (the <a> with href "{% url
'course_assignments' course.slug %}") uses px-4 and therefore doesn't match the
shared primary button contract; update its class list to replace px-4 with px-6
so it matches the canonical primary button classes (bg-teal-300
hover:bg-teal-400 text-white px-6 py-2 rounded-lg transition duration-200) while
keeping the existing dark: and focus: classes intact.

In `@web/templates/courses/grade_submission.html`:
- Around line 42-50: Replace the custom class lists on the score input
(id="score", name="score") and feedback textarea (id="feedback",
name="feedback") with the repository-standard form-control class contract used
across the project (the Tailwind classes: w-full px-4 py-2 border
border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2
focus:ring-blue-500), preserving other attributes like
id/name/value/placeholder/rows/required; ensure both elements use the identical
shared class string so styling is consistent with other form inputs.

In `@web/views.py`:
- Around line 2135-2146: The file has a mis-indented line: "assignment.due_date
= parse_datetime(due_date) if due_date else None" is over-indented and breaks
Python parsing; adjust its indentation to match the surrounding block (i.e.,
align it with the following "assignment.save()" and other statements within the
same view function) so the assignment update block is syntactically correct;
check the function that processes the form (the block that uses assignment,
parse_datetime, assignment.save, messages.success and return redirect) and
ensure all statements inside that conditional/handler have consistent
indentation, then re-run the AST parse check.
- Around line 2129-2134: The edit handler is directly assigning request.POST
values to assignment (title, description, status, max_score,
allow_late_submissions) without the same validation used on create; update the
edit flow to reuse the create_assignment validation path (e.g., call the same
form or validator used by create_assignment) instead of assigning raw POST
values: validate/clean title, description, status and max_score first (handle
int conversion and non-negative constraint) and only then mutate assignment and
save; reference the existing create_assignment validation routine or
AssignmentForm.clean() so edits and creates share identical checks.
- Around line 2061-2070: The current check uses client-controlled
file_submission.content_type; replace this with server-side MIME/signature
validation: use a library like python-magic (libmagic) or Django's built-in file
inspection to detect the real MIME from the file bytes for the variable
file_submission, compare that detected MIME against allowed_types, and only then
allow the upload; additionally verify the file extension matches expected types
as a secondary check, ensure you read a limited prefix of file_submission (e.g.,
first few KB) and seek back to start before saving, and keep the existing size
check for file_submission.size.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9f0a1926-e419-4815-b113-8b79cbe101f4

📥 Commits

Reviewing files that changed from the base of the PR and between f81039d and b9aa130.

⛔ Files ignored due to path filters (1)
  • web/migrations/0064_add_assignment_and_submission.py is excluded by !**/migrations/**
📒 Files selected for processing (10)
  • web/admin.py
  • web/models.py
  • web/templates/courses/assignment_confirm_delete.html
  • web/templates/courses/assignment_detail.html
  • web/templates/courses/assignment_form.html
  • web/templates/courses/assignment_list.html
  • web/templates/courses/detail.html
  • web/templates/courses/grade_submission.html
  • web/urls.py
  • web/views.py

@ayesha1145 ayesha1145 force-pushed the feat/course-assignment-system branch from b9aa130 to 42152b1 Compare March 21, 2026 23:38
@ayesha1145 ayesha1145 force-pushed the feat/course-assignment-system branch from 42152b1 to 173f915 Compare March 21, 2026 23:44
@ayesha1145
Copy link
Author

Addressed remaining feedback:

  • Added MinValueValidator(1) to Assignment.max_score field to prevent zero-point assignments
  • assignment_detail.html corruption: fixed — template fully rewritten, clean 163 lines, no embedded shell commands
  • edit_assignment indentation: fixed in previous push — assignment.due_date line is correctly indented at 8 spaces
  • Tests: acknowledged — unit tests are out of scope for this PR; the project does not currently require test coverage for contributor PRs
  • File serving via protected endpoint: acknowledged as a future improvement — direct media URL is acceptable for now given the view-level access control already in place

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant