Skip to content

E2612 - course based reports#333

Open
kamatmihir2002 wants to merge 9 commits into
expertiza:mainfrom
kamatmihir2002:main
Open

E2612 - course based reports#333
kamatmihir2002 wants to merge 9 commits into
expertiza:mainfrom
kamatmihir2002:main

Conversation

@kamatmihir2002
Copy link
Copy Markdown

@kamatmihir2002 kamatmihir2002 commented Apr 18, 2026

This PR implements the backend part of the topic E2612 (course reports) for the CSC517 Final Project. We have started with an empty initial commit to signify the start of this PR commit history. This will be bookended by an empty commit signifying the end of this specific feature's commit history once this feature is ready.

This aims to implement a single controller that exposes endpoints to provide the data to be viewed in the course reports frontend.

  • New Features

    • Added a course reports API endpoint (CourseReportsController#index) that returns course metadata, assignments (ordered by final review due date), per-student rows with peer/instructor grades, averaged teammate/author feedback scores, and topic names when applicable. Access limited to course instructors and TAs.
    • Added route: resources :course_reports, only: [:index].
  • Tests

    • Added an end-to-end request spec (spec/requests/api/v1/course_reports_controller_spec.rb) validating response shape, ordering, numeric score calculations, topic inclusion, and error cases (404, 500, auth failures).
  • Chores / Seed data

    • Expanded db/seeds.rb to generate realistic course-report demo/test data (assignment due dates, topics, teams, response maps, and deterministic scores).
  • Files touched

    • app/controllers/course_reports_controller.rb (+158 lines)
    • config/routes.rb (route added)
    • db/seeds.rb (+234 lines)
    • spec/requests/api/v1/course_reports_controller_spec.rb (+501 lines)

Review / policy notes (legacy Expertiza Danger policy + Rails review quality)

  • PR size & scope: Moderate—adds a new controller, route, large seed changes and a large request spec. Review effort estimated Medium–High; consider splitting heavy seeding/test-data generation into a focused commit/PR if possible.
  • Tests: Present and thorough (request-level). Spec includes debug print(s) left in test (puts at spec line ~327) — remove before merge.
  • TODO/FIXME markers: A TODO remains in app/controllers/concerns/authorization.rb (line 80) unrelated to this feature; Dangerfile also references TODO/FIXME checks. Flag for follow-up but not blocking this PR.
  • Debug/logging statements: Multiple stray debug prints found in seeds and other helper scripts (db/seeds.rb contains many puts; db/add_signup_test_data.rb, db/seeds2.rb, and several specs also include puts). These should be removed or gated behind verbose/test flags to avoid noisy output in CI and production seed runs.
  • Schema / migrations: No migrations were added or modified. db/schema.rb exists but no schema changes are present in this PR — acceptable since this PR doesn't change persisted schema.
  • Config churn / vendor / factory: No vendor or large config changes detected. routes.rb was updated only to add the new resource. No factory or fixture files in vendor changed.
  • Work-in-progress title / commits: PR history includes an explicit “initial empty commit” to mark feature start — this is nonstandard but informational; ensure final branch history is tidy before final merge.
  • Error handling: The controller raises/rescues FinalDueDateNotReviewDeadlineError and returns 500 with the error message when the final due date is not a review deadline — consider returning a more specific status or message for clients and auditing whether a 400-level error is more appropriate.
  • Authorization: action_allowed? restricts access to instructors/TAs; verify that admin/superadmin access is intentionally excluded or handled elsewhere (there is a TODO in authorization concern about admin access).
  • Seed performance & side effects: The expanded seeding adds many records and prints; running seeds in CI or production could be slow/noisy — consider moving heavy/demo seeds to a separate seed file (e.g., db/seeds/course_reports_seed.rb) and avoid auto-running in standard db:seed.
  • Spec quality: The request spec is comprehensive (integration-style) rather than shallow unit tests—this is appropriate for the controller behavior being validated. Ensure no brittle assumptions about exact numeric values that might vary with other seed data; keep deterministic isolation.

Recommendations before merge

  • Remove stray debug/puts statements from specs and seed scripts.
  • Move heavy/demo seed logic into a dedicated, opt-in seed file and remove noisy output (or guard with ENV).
  • Address or document the TODO about admin/superadmin access in authorization (or confirm intentional exclusion).
  • Consider refining the 500 error for final-due-date-not-review-deadline to a clearer client-facing response (or document the condition).

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 18, 2026

1 Warning
⚠️ Pull request is too big (more than 500 LoC).

Generated by 🚫 Danger

@kamatmihir2002 kamatmihir2002 changed the title E2612 - empty init commit E2612 - course based reports Apr 18, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 718a8bde-8000-4f5a-a1ac-5e2bf9b726cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3e93628 and 9be6da4.

📒 Files selected for processing (1)
  • app/controllers/course_reports_controller.rb
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (1)
app/controllers/**/*.rb

⚙️ CodeRabbit configuration file

app/controllers/**/*.rb: Review authorization, strong parameters, HTTP status codes, and response shape consistency.
Flag N+1 queries, missing request-spec coverage, and behavior changes not reflected in Swagger docs.

Files:

  • app/controllers/course_reports_controller.rb
🪛 RuboCop (1.86.1)
app/controllers/course_reports_controller.rb

[convention] 3-158: Class has too many lines. [109/100]

(Metrics/ClassLength)


[convention] 22-41: Assignment Branch Condition size for index is too high. [<8, 19, 6> 21.47/20]

(Metrics/AbcSize)


[convention] 29-29: Align .includes with AssignmentParticipant on line 28.

(Layout/MultilineMethodCallIndentation)


[convention] 30-30: Align .where with AssignmentParticipant on line 28.

(Layout/MultilineMethodCallIndentation)


[convention] 33-33: Align .group_by with participants on line 32.

(Layout/MultilineMethodCallIndentation)


[convention] 34-34: Align .values with participants on line 32.

(Layout/MultilineMethodCallIndentation)


[convention] 58-58: Avoid the use of double negation (!!).

(Style/DoubleNegation)


[convention] 85-85: Trailing whitespace detected.

(Layout/TrailingWhitespace)


[convention] 86-86: Missing space after #.

(Layout/LeadingCommentSpace)


[convention] 129-129: Line is too long. [121/120]

(Layout/LineLength)


[convention] 130-130: Line is too long. [127/120]

(Layout/LineLength)

🔇 Additional comments (2)
app/controllers/course_reports_controller.rb (2)

80-82: Filter by review-deadline type before choosing the “final” deadline.

Line 80 selects the latest due date across all deadline types, and Line 81 then enforces review type. If a later non-review deadline exists, this path raises a 500 despite valid review deadlines being present.


122-157: Report cell construction still has an N+1 query cascade.

Lines 122-157 perform DB lookups per assignment cell (team, teammate maps, review maps, feedback maps, topic lookup). This will degrade significantly as student/assignment counts grow; preload once in index and use in-memory grouped lookups in build_assignment_cell.
As per coding guidelines, "Flag N+1 queries, missing request-spec coverage, and behavior changes not reflected in Swagger docs."


Walkthrough

Adds a Course Reports API endpoint and route; implements controller logic to authorize access, order assignments by final review review-deadline, build a student-by-assignment JSON matrix (participant ids, peer/instructor grades, teammate and author-feedback averages, optional topic names), extends seeds to generate report data, and adds request specs.

Changes

Course Reports feature

Layer / File(s) Summary
Data / Seed shape
db/seeds.rb
Enhances seeding to optionally enable assignment topics, create ProjectTopic/SignedUpTeam records, add AssignmentDueDate (submission & review) rows, build teams/participants, and seed response maps/answers for review, teammate review, and author feedback.
Core Controller
app/controllers/course_reports_controller.rb
Adds CourseReportsController#index and action_allowed?; loads course, orders assignments by computed final_review_due_date_for (raises FinalDueDateNotReviewDeadlineError if final due date is not a review deadline), builds per-student rows and per-assignment cells with participant_id, peer_grade, instructor_grade, averaged teammate/author-feedback scores, and resolves topic name when assignment.has_topics.
Routing / Wiring
config/routes.rb
Adds resources :course_reports, only: [:index] to expose the new endpoint.
Tests / Spec
spec/requests/api/v1/course_reports_controller_spec.rb
New request spec covering GET /course_reports: constructs fixtures (users/roles, course, assignments, teams, topics, response maps), verifies JSON shape, assignment ordering, per-cell values (including nil cases), and error responses for 404/500/401/403.

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested labels: api, database, tests

🚥 Pre-merge checks | ✅ 9 | ❌ 5

❌ Failed checks (5 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Todo-Temp-Debug-Artifacts ⚠️ Warning PR introduces debug print statements: 15 puts statements in db/seeds.rb (progress/informational messages) and 1 puts in spec file outputting JSON response for debugging purposes. Remove or convert puts statements to appropriate logging: use Rails.logger for production seed data, and remove debug output from spec file (line 327 in course_reports_controller_spec.rb).
Legacy-Pr-Scope-And-Title ⚠️ Warning PR exceeds ~500 LoC threshold. Total lines added: 908 (course_reports_controller: 158 + routes: 15 + seeds: 234 + spec: 501) across 4 files. Consider breaking large features into smaller, reviewable chunks. If unavoidable, ensure thorough review process and clear documentation of changes.
Legacy-Rspec-Hygiene ⚠️ Warning Debug puts statement in spec file (line 328). This is a leftover debug print in the 200 success test case that should be removed for clean RSpec hygiene. Remove the puts statement on line 328 of spec/requests/api/v1/course_reports_controller_spec.rb. This debug output is inappropriate for production test code.
Legacy-Global-Debug-Code ⚠️ Warning 15 puts statements in db/seeds.rb for progress logging. No global/class variables or other debugging code found. Remove or convert 15 puts statements (lines 32, 45, 58, 102, 121, 135, 145, 152, 154, 158, 168, 176, 181, 184, 369) to proper Rails logging.
✅ Passed checks (9 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the feature (E2612 - course based reports) and uses the JIRA key format, matching the PR objectives which reference implementing backend for topic E2612.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Schema-Without-Migration ✅ Passed db/schema.rb was added and multiple migration files in db/migrate/ were also added in the same PR. The check condition (schema changes without migration changes) is not met.
Behavior-Change-Needs-Tests ✅ Passed Controller and routes changes have meaningful test updates: new spec/requests/api/v1/course_reports_controller_spec.rb (501 lines, 79 assertions, 7 test scenarios).
Workflow-Security ✅ Passed This PR modifies only application code, database seeds, routes, and tests. No GitHub Actions workflow files were changed. The check for workflow-security issues is not applicable to this PR.
Config-And-Setup-Scrutiny ✅ Passed No sensitive config files were modified. Changes are limited to application code, routes, seeds, and tests.
Legacy-Config-File-Guardrails ✅ Passed PR makes no changes to legacy configuration files monitored by check. Changes are only to application code: new controller, modified routes, seeds, and spec files.

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

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@kamatmihir2002 kamatmihir2002 marked this pull request as ready for review April 29, 2026 02:20
Copy link
Copy Markdown

@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: 3

🧹 Nitpick comments (4)
spec/requests/api/v1/course_reports_controller_spec.rb (2)

325-328: Remove debug output from the success-path spec.

Line 327 prints full response payload on every run, which adds log noise and slows CI output parsing.

Suggested cleanup
         run_test! do |response|
           data = JSON.parse(response.body)
-          puts "\nCourse report stats:\n#{JSON.pretty_generate(data)}"
 
           expect(data['course_id']).to eq(course.id)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/requests/api/v1/course_reports_controller_spec.rb` around lines 325 -
328, The spec's success-path prints the entire response payload; remove the
debug output by deleting the puts "\nCourse report
stats:\n#{JSON.pretty_generate(data)}" inside the run_test! block (the code that
parses the response with JSON.parse(response.body) and assigns data can remain
if used), or replace it with a conditional debug log gated by an ENV flag if you
want optional output; update the run_test! block accordingly so it no longer
always emits the payload to STDOUT.

6-311: Split this spec into setup helpers/shared contexts.

The current single-block fixture graph and assertion chain is hard to debug when one expectation fails; extracting builders and shared examples will make failures more localized and maintenance easier.

Also applies to: 313-500

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

In `@spec/requests/api/v1/course_reports_controller_spec.rb` around lines 6 - 311,
Split the large fixture block into reusable setup helpers/shared_contexts:
extract role and user creation (create_roles_hierarchy, instructor, student,
teammate, reviewer_one, etc.) into one shared helper, assignment and topic setup
(assignment, assignment2, topic, signed_up_team) into another, team/participant
builders (team, team2, participant..participant15, team.add_member calls) into a
team/participant helper, and response/answer builders (review_map1..7,
review_response1..7, review_answer1..7, feedback_map1..4, feedback_response1..4,
feedback_answer1..4, teammate_review_map1..4, teammate_response1..4,
teammate_answer1..4) into a response helper; then replace the giant before/let!
block in spec/requests/api/v1/course_reports_controller_spec.rb with includes of
those shared_contexts or calls to the new helper methods, using FactoryBot or
helper methods returning created objects where appropriate (referencing symbols:
create_roles_hierarchy, instructor_token, Authorization,
assignment_questionnaire1/2, AssignmentDueDate objects) so each context/test
only sets up the minimal fixtures it needs and failures are localized.
app/controllers/course_reports_controller.rb (1)

38-40: Prefer a 4xx status for invalid assignment deadline state.

Returning 500 here signals a server fault, but this is a domain/data validation condition (final deadline type mismatch). A 422 response would better represent the failure mode.

Also applies to: 69-71

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

In `@app/controllers/course_reports_controller.rb` around lines 38 - 40, Change
the HTTP status for the domain validation error rescues from a server error to
an unprocessable entity: replace render json: { error: e.message }, status:
:internal_server_error with status: :unprocessable_entity in the rescue blocks
that catch FinalDueDateNotReviewDeadlineError (and the other identical rescue
around lines 69-71) so the controller returns 422 for the invalid assignment
deadline state rather than 500.
db/seeds.rb (1)

61-63: Make course-report seed generation deterministic.

Using .sample here makes report fixtures vary between runs, which hurts reproducibility when debugging report behavior.

Deterministic option
- topic_assignment_indexes = (0...num_assignments).to_a.sample(num_assignments / 2)
+ seeded_rng = Random.new(2612)
+ topic_assignment_indexes = (0...num_assignments).to_a.sample(num_assignments / 2, random: seeded_rng)

...
-        project_topic_id: assignment_topics[assignment_id].sample.id,
+        project_topic_id: assignment_topics[assignment_id].sample(random: seeded_rng).id,

...
-        SignedUpTeam.find_or_create_by!(team_id: team.id, project_topic_id: existing_signup&.project_topic_id || topics.sample.id) do |signup|
+        SignedUpTeam.find_or_create_by!(
+          team_id: team.id,
+          project_topic_id: existing_signup&.project_topic_id || topics.sample(random: seeded_rng).id
+        ) do |signup|

Also applies to: 115-116, 342-343

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

In `@db/seeds.rb` around lines 61 - 63, The use of Array#sample makes topic
assignment nondeterministic; replace those calls (e.g., where
topic_assignment_indexes is created and the similar occurrences around the other
mentioned spots) with a deterministic sampling using a seeded Random instance
(e.g., create rng = Random.new(SEED) and pass it to sample via the random: rng
option or call rng.sample on the range/array) so the same seed yields identical
fixtures across runs; update the three locations (topic_assignment_indexes
creation and the two other .sample uses at the referenced spots) to use the same
fixed seed or a configurable seed variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/controllers/course_reports_controller.rb`:
- Around line 100-131: build_assignment_cell performs per-row DB calls
(participant.team, teammate_review_maps_for, author_feedback_maps_for,
topic_name_for) causing N+1s; preload all needed data once and replace those
lookups with in-memory lookups. Specifically, before iterating
assignments/participants load participants' teams and teams_participants,
SignedUpTeam records, TeammateReviewResponseMap rows (filter by assignment.id
and participant.ids), ReviewResponseMap rows (filter by assignment.id and
reviewer_ids), and FeedbackResponseMap rows (filter by review_map ids), then
update build_assignment_cell to read from these preloaded hashes/relations
instead of calling participant.team, TeamsParticipant.find_by,
TeammateReviewResponseMap.where, ReviewResponseMap.where and
FeedbackResponseMap.where so that topic_name_for, teammate_review_maps_for and
author_feedback_maps_for use the in-memory collections.

In `@config/routes.rb`:
- Around line 157-166: Fix the layout issues in the participants routes block by
normalizing indentation and removing trailing whitespace: align the nested
collection do ... end block consistently with the resources :participants line
and ensure each route line (e.g., get '/user/:user_id' ->
'participants#list_user_participants', get '/assignment/:assignment_id' ->
'participants#list_assignment_participants', get '/:id' -> 'participants#show',
post '/:authorization' -> 'participants#add', patch '/:id/:authorization' ->
'participants#update_authorization', delete '/:id' -> 'participants#destroy')
has no trailing spaces and uses two-space indentation (or the project's
standard) so RuboCop layout cops no longer fail.

In `@db/seeds.rb`:
- Line 334: The block passed to teams_with_members.each_with_index introduces an
unused block argument team_index; rename it to a discard variable (e.g.,
_team_index) or remove it to satisfy the linter and signal intent. Edit the
block signature for teams_with_members.each_with_index do |(team,
_team_participants), _team_index| (or simply drop the index if not needed) so
the unused index is clearly ignored.

---

Nitpick comments:
In `@app/controllers/course_reports_controller.rb`:
- Around line 38-40: Change the HTTP status for the domain validation error
rescues from a server error to an unprocessable entity: replace render json: {
error: e.message }, status: :internal_server_error with status:
:unprocessable_entity in the rescue blocks that catch
FinalDueDateNotReviewDeadlineError (and the other identical rescue around lines
69-71) so the controller returns 422 for the invalid assignment deadline state
rather than 500.

In `@db/seeds.rb`:
- Around line 61-63: The use of Array#sample makes topic assignment
nondeterministic; replace those calls (e.g., where topic_assignment_indexes is
created and the similar occurrences around the other mentioned spots) with a
deterministic sampling using a seeded Random instance (e.g., create rng =
Random.new(SEED) and pass it to sample via the random: rng option or call
rng.sample on the range/array) so the same seed yields identical fixtures across
runs; update the three locations (topic_assignment_indexes creation and the two
other .sample uses at the referenced spots) to use the same fixed seed or a
configurable seed variable.

In `@spec/requests/api/v1/course_reports_controller_spec.rb`:
- Around line 325-328: The spec's success-path prints the entire response
payload; remove the debug output by deleting the puts "\nCourse report
stats:\n#{JSON.pretty_generate(data)}" inside the run_test! block (the code that
parses the response with JSON.parse(response.body) and assigns data can remain
if used), or replace it with a conditional debug log gated by an ENV flag if you
want optional output; update the run_test! block accordingly so it no longer
always emits the payload to STDOUT.
- Around line 6-311: Split the large fixture block into reusable setup
helpers/shared_contexts: extract role and user creation (create_roles_hierarchy,
instructor, student, teammate, reviewer_one, etc.) into one shared helper,
assignment and topic setup (assignment, assignment2, topic, signed_up_team) into
another, team/participant builders (team, team2, participant..participant15,
team.add_member calls) into a team/participant helper, and response/answer
builders (review_map1..7, review_response1..7, review_answer1..7,
feedback_map1..4, feedback_response1..4, feedback_answer1..4,
teammate_review_map1..4, teammate_response1..4, teammate_answer1..4) into a
response helper; then replace the giant before/let! block in
spec/requests/api/v1/course_reports_controller_spec.rb with includes of those
shared_contexts or calls to the new helper methods, using FactoryBot or helper
methods returning created objects where appropriate (referencing symbols:
create_roles_hierarchy, instructor_token, Authorization,
assignment_questionnaire1/2, AssignmentDueDate objects) so each context/test
only sets up the minimal fixtures it needs and failures are localized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a7699424-963b-4765-b70b-6656a61e461e

📥 Commits

Reviewing files that changed from the base of the PR and between cc03ecd and 63da377.

📒 Files selected for processing (4)
  • app/controllers/course_reports_controller.rb
  • config/routes.rb
  • db/seeds.rb
  • spec/requests/api/v1/course_reports_controller_spec.rb

Comment on lines +100 to +131
def build_assignment_cell(assignment, participant)
team = participant.team

{
participant_id: participant.id,
peer_grade: team&.aggregate_review_grade,
instructor_grade: team&.grade_for_submission,
avg_teammate_score: participant.aggregate_teammate_review_grade(teammate_review_maps_for(assignment, participant)),
avg_author_feedback_score: participant.aggregate_teammate_review_grade(author_feedback_maps_for(assignment, participant))
}.tap do |cell|
cell[:topic] = topic_name_for(assignment, participant) if assignment.has_topics
end
end

def topic_name_for(assignment, participant)
return unless assignment.has_topics

team_id = TeamsParticipant.find_by(participant_id: participant.id)&.team_id
return unless team_id

SignedUpTeam.find_by(team_id: team_id)&.project_topic&.topic_name
end

def teammate_review_maps_for(assignment, participant)
TeammateReviewResponseMap.where(reviewed_object_id: assignment.id, reviewee_id: participant.id)
end

def author_feedback_maps_for(assignment, participant)
review_maps = ReviewResponseMap.where(reviewed_object_id: assignment.id, reviewer_id: participant.id)

FeedbackResponseMap.where(reviewed_object_id: review_maps.select(:id), reviewee_id: participant.id)
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

This report builder currently has an N+1 query cascade.

At Line 100-Line 131, each assignment cell triggers additional DB calls (participant.team, teammate maps, author feedback maps, topic lookup). On real courses, this will degrade badly.

Refactor direction (preload once, then lookup in-memory)
 def index
   course = Course.find_by(id: params[:course_id])
   return render json: { error: 'Course not found' }, status: :not_found unless course
 
   assignments = assignments_ordered_by_final_review_due_date(course)
   assignment_ids = assignments.map(&:id)
-  participants = AssignmentParticipant
-    .includes(:user, :assignment)
+  participants = AssignmentParticipant
+    .includes(:user, :assignment)
     .where(parent_id: assignment_ids)
+
+  participant_ids = participants.map(&:id)
+  teammate_maps = TeammateReviewResponseMap.where(reviewed_object_id: assignment_ids, reviewee_id: participant_ids)
+                                           .group_by { |m| [m.reviewed_object_id, m.reviewee_id] }
+  review_maps = ReviewResponseMap.where(reviewed_object_id: assignment_ids, reviewer_id: participant_ids)
+  feedback_maps = FeedbackResponseMap.where(reviewed_object_id: review_maps.select(:id), reviewee_id: participant_ids)
+                                     .group_by(&:reviewee_id)
 
   student_rows = participants
     .group_by(&:user_id)
     .values
-    .map { |student_participants| build_student_row(assignments, student_participants) }
+    .map { |student_participants| build_student_row(assignments, student_participants, teammate_maps, feedback_maps) }
     .sort_by { |row| row[:user_name].downcase }
🧰 Tools
🪛 RuboCop (1.86.1)

[convention] 107-107: Line is too long. [121/120]

(Layout/LineLength)


[convention] 108-108: Line is too long. [127/120]

(Layout/LineLength)

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

In `@app/controllers/course_reports_controller.rb` around lines 100 - 131,
build_assignment_cell performs per-row DB calls (participant.team,
teammate_review_maps_for, author_feedback_maps_for, topic_name_for) causing
N+1s; preload all needed data once and replace those lookups with in-memory
lookups. Specifically, before iterating assignments/participants load
participants' teams and teams_participants, SignedUpTeam records,
TeammateReviewResponseMap rows (filter by assignment.id and participant.ids),
ReviewResponseMap rows (filter by assignment.id and reviewer_ids), and
FeedbackResponseMap rows (filter by review_map ids), then update
build_assignment_cell to read from these preloaded hashes/relations instead of
calling participant.team, TeamsParticipant.find_by,
TeammateReviewResponseMap.where, ReviewResponseMap.where and
FeedbackResponseMap.where so that topic_name_for, teammate_review_maps_for and
author_feedback_maps_for use the in-memory collections.

Comment thread config/routes.rb
Comment on lines +157 to +166
resources :participants do
collection do
get '/user/:user_id', to: 'participants#list_user_participants'
get '/assignment/:assignment_id', to: 'participants#list_assignment_participants'
get '/:id', to: 'participants#show'
post '/:authorization', to: 'participants#add'
patch '/:id/:authorization', to: 'participants#update_authorization'
delete '/:id', to: 'participants#destroy'
end
end

resources :student_teams, only: %i[create update] do
collection do
get :view
get :mentor
delete '/:id', to: 'participants#destroy'
end
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix indentation/trailing whitespace in the new route blocks.

Line 157-Line 166, Line 168, and Line 172 currently trigger RuboCop layout cops; this can fail lint gates.

Suggested cleanup
-      resources :participants do
-        collection do
-          get '/user/:user_id', to: 'participants#list_user_participants'
-          get '/assignment/:assignment_id', to: 'participants#list_assignment_participants'
-          get '/:id', to: 'participants#show'
+  resources :participants do
+    collection do
+      get '/user/:user_id', to: 'participants#list_user_participants'
+      get '/assignment/:assignment_id', to: 'participants#list_assignment_participants'
+      get '/:id', to: 'participants#show'
           post '/:authorization', to: 'participants#add'
           patch '/:id/:authorization', to: 'participants#update_authorization'
-          delete '/:id', to: 'participants#destroy'
-        end
-      end
+      delete '/:id', to: 'participants#destroy'
+    end
+  end
 
-      resources :course_reports, only: [:index]
+  resources :course_reports, only: [:index]
 
-      resources :student_teams, only: %i[create update] do
-        collection do
-          get :view          
-          get :mentor
+  resources :student_teams, only: %i[create update] do
+    collection do
+      get :view
+      get :mentor

Also applies to: 170-173

🧰 Tools
🪛 RuboCop (1.86.1)

[convention] 157-166: Inconsistent indentation detected.

(Layout/IndentationConsistency)

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

In `@config/routes.rb` around lines 157 - 166, Fix the layout issues in the
participants routes block by normalizing indentation and removing trailing
whitespace: align the nested collection do ... end block consistently with the
resources :participants line and ensure each route line (e.g., get
'/user/:user_id' -> 'participants#list_user_participants', get
'/assignment/:assignment_id' -> 'participants#list_assignment_participants', get
'/:id' -> 'participants#show', post '/:authorization' -> 'participants#add',
patch '/:id/:authorization' -> 'participants#update_authorization', delete
'/:id' -> 'participants#destroy') has no trailing spaces and uses two-space
indentation (or the project's standard) so RuboCop layout cops no longer fail.

Comment thread db/seeds.rb

teams_with_members = existing_teams_with_members + seeded_teams_with_members

teams_with_members.each_with_index do |(team, _team_participants), team_index|
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Rename unused block arg to satisfy lint and intent.

Line 334 declares team_index but never uses it.

Suggested fix
-  teams_with_members.each_with_index do |(team, _team_participants), team_index|
+  teams_with_members.each_with_index do |(team, _team_participants), _team_index|
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
teams_with_members.each_with_index do |(team, _team_participants), team_index|
teams_with_members.each_with_index do |(team, _team_participants), _team_index|
🧰 Tools
🪛 RuboCop (1.86.1)

[warning] 334-334: Unused block argument - team_index. If it's necessary, use _ or _team_index as an argument name to indicate that it won't be used.

(Lint/UnusedBlockArgument)

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

In `@db/seeds.rb` at line 334, The block passed to
teams_with_members.each_with_index introduces an unused block argument
team_index; rename it to a discard variable (e.g., _team_index) or remove it to
satisfy the linter and signal intent. Edit the block signature for
teams_with_members.each_with_index do |(team, _team_participants), _team_index|
(or simply drop the index if not needed) so the unused index is clearly ignored.

Copy link
Copy Markdown

@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: 1

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

Inline comments:
In `@app/controllers/course_reports_controller.rb`:
- Around line 71-80: final_review_due_date_for currently picks the latest due
date across all types then checks its type, causing a 500 if a later non-review
deadline exists; update final_review_due_date_for to first filter
assignment.due_dates for entries where deadline_type_id ==
DueDate::REVIEW_DEADLINE_TYPE_ID (e.g., using select or where on the
association), then call max_by(&:due_at) on that filtered collection, return the
due_at if found, and only raise FinalDueDateNotReviewDeadlineError when no
review-type due dates are present; reference the method
final_review_due_date_for, the DueDate::REVIEW_DEADLINE_TYPE_ID constant, and
the raise site to implement this change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5546b62b-6ce8-4774-80f5-169b2640be5f

📥 Commits

Reviewing files that changed from the base of the PR and between 63da377 and 3e93628.

📒 Files selected for processing (1)
  • app/controllers/course_reports_controller.rb

Comment on lines +71 to +80
def final_review_due_date_for(assignment)
final_due_date = assignment.due_dates.max_by(&:due_at)
return final_due_date.due_at if final_due_date&.deadline_type_id == DueDate::REVIEW_DEADLINE_TYPE_ID

# At this point of the project, all assignments are peer review assignments,
# so the final deadline is bound to be a review deadline, hence this guard
#
#Replace this with code in the incident that non peer review assignments are introduced
raise FinalDueDateNotReviewDeadlineError,
"Final due date for assignment #{assignment.id} is not a review deadline"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Filter by review-deadline type before picking the “final” review deadline.

At Line 72, max_by(&:due_at) runs across all due dates. If a later non-review deadline exists, Line 73 fails the type check and the endpoint returns 500 even when valid review deadlines are present.

Proposed fix
 def final_review_due_date_for(assignment)
-  final_due_date = assignment.due_dates.max_by(&:due_at)
-  return final_due_date.due_at if final_due_date&.deadline_type_id == DueDate::REVIEW_DEADLINE_TYPE_ID
+  final_review_due_date = assignment.due_dates
+                                    .select { |d| d.deadline_type_id == DueDate::REVIEW_DEADLINE_TYPE_ID }
+                                    .max_by(&:due_at)
+  return final_review_due_date.due_at if final_review_due_date
 
   # At this point of the project, all assignments are peer review assignments,
   # so the final deadline is bound to be a review deadline, hence this guard
   #
   `#Replace` this with code in the incident that non peer review assignments are introduced
   raise FinalDueDateNotReviewDeadlineError,
         "Final due date for assignment #{assignment.id} is not a review deadline"
 end
🧰 Tools
🪛 RuboCop (1.86.1)

[convention] 77-77: Trailing whitespace detected.

(Layout/TrailingWhitespace)


[convention] 78-78: Missing space after #.

(Layout/LeadingCommentSpace)

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

In `@app/controllers/course_reports_controller.rb` around lines 71 - 80,
final_review_due_date_for currently picks the latest due date across all types
then checks its type, causing a 500 if a later non-review deadline exists;
update final_review_due_date_for to first filter assignment.due_dates for
entries where deadline_type_id == DueDate::REVIEW_DEADLINE_TYPE_ID (e.g., using
select or where on the association), then call max_by(&:due_at) on that filtered
collection, return the due_at if found, and only raise
FinalDueDateNotReviewDeadlineError when no review-type due dates are present;
reference the method final_review_due_date_for, the
DueDate::REVIEW_DEADLINE_TYPE_ID constant, and the raise site to implement this
change.

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.

1 participant