Skip to content

Conversation

@dwang3851
Copy link
Contributor

Description

This changes implements server-side pagination for the manage submissions page to improve loading times.

Motivation and Context

Previously, it would take about 1.5 seconds for each page of the manage submissions page to load since we would render all of them at once. For example, for a submissions page with 400 submissions, it would take about 6 seconds. This change renders only one page at a time, to take a maximum of 1.5 seconds each time.

How Has This Been Tested?

Tested all of the manage submissions functions locally

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have run rubocop and erblint for style check. If you haven't, run overcommit --install && overcommit --sign to use pre-commit hook for linting
  • My change requires a change to the documentation, which is located at Autolab Docs
  • I have updated the documentation accordingly, included in this PR

@dwang3851 dwang3851 marked this pull request as ready for review October 30, 2025 22:22
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

📝 Walkthrough

Walkthrough

Switched the submissions index to DataTables server-side rendering. Controller now serves paginated/searchable/sortable JSON and a new row formatter. Client JS manages selection state, delegated events, dynamic row rendering, and batch-action enablement. View removed server-rendered rows and provides header + select-all UI.

Changes

Cohort / File(s) Summary
Server-side DataTables & Controller
app/controllers/submissions_controller.rb
Added JSON handling for DataTables (paging, search by email, multi-column sort including computed score), computes recordsTotal/recordsFiltered, and introduced format_submission_for_datatable(submission, score = nil); declared action_auth_level for new action; removed prior per-request caching logic.
Client DataTables Initialization & Selection State
app/assets/javascripts/manage_submissions.js
Replaced legacy DataTable init with serverSide/ajax; added selectedStudentCids, selectedSubmissions, submissions_to_cud; implemented columns, createdRow, drawCallback, delegated handlers for dynamic elements; added toggleAllRows, toggleRow, updateSelectAllCheckbox, and updateSelectedCount; removed old pagination/hardcoded-row hacks.
View: Table Header & Template Simplification
app/views/submissions/index.html.erb
Removed server-rendered tbody and per-row markup, selected-count placeholder, and Regrade All block; simplified header generation to include select-all checkbox and sortable headers; retained modals/placeholders for annotations.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Browser as Browser (manage_submissions.js)
    participant Server as Server (SubmissionsController)
    participant DB as Database

    rect rgba(230,245,255,0.8)
    Note over User,Browser: Initial page load (HTML)
    User->>Browser: GET /submissions
    Browser->>Server: GET /submissions (HTML)
    Server->>DB: Query scaffold data (limited)
    DB-->>Server: Return scaffold
    Server-->>Browser: Render index view (DataTables config + headers)
    Browser->>Browser: Initialize DataTables (serverSide)
    end

    rect rgba(245,230,255,0.8)
    Note over Browser,Server: DataTables requests rows
    Browser->>Server: AJAX GET /submissions.json (draw,start,length,search,order)
    Server->>DB: Query submissions with filters/sort/limit (may compute score)
    DB-->>Server: Return rows
    Server->>Server: format_submission_for_datatable per row
    Server-->>Browser: JSON (data, draw, recordsTotal, recordsFiltered)
    Browser->>Browser: createdRow + drawCallback -> render row HTML, checkboxes, actions
    end

    rect rgba(230,255,235,0.8)
    Note over User,Browser: Selection & batch actions
    User->>Browser: Click row checkbox / select-all
    Browser->>Browser: toggleRow / toggleAllRows -> update selections, UI highlight
    Browser->>Browser: updateSelectedCount() -> enable/disable batch actions
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–75 minutes

  • Focus review on:
    • app/controllers/submissions_controller.rb: search/sort correctness (especially computed-score sort), record counts, and format_submission_for_datatable data/HTML.
    • app/assets/javascripts/manage_submissions.js: DataTables columns mapping, createdRow/drawCallback selection persistence, delegated event handlers, and batch-action endpoint wiring.
    • Column ordering and header ↔ DataTables column index alignment in app/views/submissions/index.html.erb.

Possibly related PRs

Suggested reviewers

  • KesterTan
  • coder6583
  • jhs-panda

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title "Manage submissions speed" clearly describes the main objective of the changeset—optimizing the performance of the manage submissions page through server-side pagination.
Description check ✅ Passed The description covers the key required sections: a clear summary of changes (server-side pagination implementation), motivation explaining the performance problem and its solution, and testing notes indicating local validation.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch manage-submissions-speed

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/assets/javascripts/manage_submissions.js (1)

558-562: Reset selections safely when paging.

selectedSubmissions.map(selectedSubmission => toggleRow(selectedSubmission, false)); mutates selectedSubmissions while you iterate it, so at least one previously selected ID survives. After changing pages, destructive operations (delete, excuse, etc.) still include that hidden submission. Iterate over a copy (or just clear the arrays) before toggling to keep selection state accurate.

-      selectedSubmissions.map(selectedSubmission => toggleRow(selectedSubmission, false));
+      [...selectedSubmissions].forEach(selectedSubmission => toggleRow(selectedSubmission, false));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 674efe9 and cfdbab4.

⛔ Files ignored due to path filters (1)
  • .github/workflows/codeql-analysis.yml is excluded by !**/*.yml
📒 Files selected for processing (3)
  • app/assets/javascripts/manage_submissions.js (3 hunks)
  • app/controllers/submissions_controller.rb (1 hunks)
  • app/views/submissions/index.html.erb (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-09-20T20:46:52.775Z
Learnt from: evanyeyeye
Repo: autolab/Autolab PR: 2209
File: app/controllers/autograders_controller.rb:0-0
Timestamp: 2024-09-20T20:46:52.775Z
Learning: In the `AutogradersController`, ensure that the `download_file` method has `action_auth_level :download_file, :instructor` to restrict access to instructors.

Applied to files:

  • app/controllers/submissions_controller.rb
📚 Learning: 2024-06-10T19:38:42.782Z
Learnt from: jlge
Repo: autolab/Autolab PR: 2011
File: app/controllers/assessments_controller.rb:28-29
Timestamp: 2024-06-10T19:38:42.782Z
Learning: `destroy_no_redirect` in `app/controllers/assessments_controller.rb` is an internal method used within the controller and not a public endpoint. It should not be included in the `before_action :set_assessment` exclusion list.

Applied to files:

  • app/controllers/submissions_controller.rb
🧬 Code graph analysis (1)
app/controllers/submissions_controller.rb (4)
app/controllers/application_controller.rb (1)
  • action_auth_level (53-71)
app/helpers/application_helper.rb (1)
  • computed_score (111-118)
app/models/submission.rb (1)
  • final_score (327-335)
app/models/assessment_user_datum.rb (1)
  • final_score (106-109)

@KesterTan KesterTan requested review from a team and KesterTan and removed request for a team October 30, 2025 22:51
@dwang3851 dwang3851 force-pushed the manage-submissions-speed branch from cfdbab4 to a6f0c1e Compare October 30, 2025 22:54
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/assets/javascripts/manage_submissions.js (2)

527-531: Remove redundant selectedStudentCids filtering.

Lines 528-530 already conditionally remove selectedCid from selectedStudentCids only when no other submissions from that student are selected. Line 531 then unconditionally removes it again with _.without(), which is redundant and could incorrectly remove CIDs that should remain selected.

Apply this diff:

         const hasOtherSelectedSubmissions = selectedSubmissions.some(id => submissions_to_cud[id] === selectedCid);
         if (!hasOtherSelectedSubmissions) {
           selectedStudentCids = selectedStudentCids.filter(cid => cid !== selectedCid);
         }
-        selectedStudentCids = _.without(selectedStudentCids, selectedCid);
       }

558-562: Reconsider clearing selections on pagination.

This handler clears all selected submissions when the user navigates to a different page. Typically, users expect selections to persist across pages so they can select submissions from multiple pages for batch operations (e.g., regrade, delete, download).

Consider removing this handler to allow selections to persist, or add a clear indicator to users that changing pages will clear their selections.

♻️ Duplicate comments (1)
app/controllers/submissions_controller.rb (1)

42-42: SQL injection vulnerability remains unfixed.

The order_direction parameter is still taken directly from params without validation and later interpolated into SQL (line 84). This allows SQL injection attacks.

As noted in the previous review, apply this fix:

-        order_direction = params.dig(:order, '0', :dir) || 'desc'
+        direction_param = params.dig(:order, '0', :dir).to_s.downcase
+        order_direction = %w[asc desc].include?(direction_param) ? direction_param : 'desc'
🧹 Nitpick comments (1)
app/views/submissions/index.html.erb (1)

14-20: Remove unused additional_data array.

With the migration to DataTables server-side processing, this additional_data array appears unused. The JavaScript now builds submissions_to_cud dynamically from the JSON response (see manage_submissions.js lines 243-248). The server-side loop over @submissions here no longer serves a purpose.

Apply this diff to remove the unused code:

-  // additional data for each row
-  additional_data = [
-      <% for s in @submissions do %>
-      {
-          "submission-id": "<%= s.id.to_s %>",
-      },
-      <% end %>
-  ];
-
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cfdbab4 and a6f0c1e.

📒 Files selected for processing (3)
  • app/assets/javascripts/manage_submissions.js (3 hunks)
  • app/controllers/submissions_controller.rb (1 hunks)
  • app/views/submissions/index.html.erb (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-06-10T19:38:42.782Z
Learnt from: najclark
Repo: autolab/Autolab PR: 2011
File: app/controllers/courses_controller.rb:1316-1320
Timestamp: 2024-06-10T19:38:42.782Z
Learning: The user confirmed the concern about potential security risks when setting `flash[:error]` and `flash[:html_safe]` with user-supplied input, specifically mentioning `course_name` derived from a tar file's contents. It's important to ensure proper sanitization of user-supplied input to prevent XSS vulnerabilities, especially when `flash[:html_safe]` is used, which indicates the content should not be escaped.

Applied to files:

  • app/controllers/submissions_controller.rb
📚 Learning: 2024-09-20T20:46:52.775Z
Learnt from: evanyeyeye
Repo: autolab/Autolab PR: 2209
File: app/controllers/autograders_controller.rb:0-0
Timestamp: 2024-09-20T20:46:52.775Z
Learning: In the `AutogradersController`, ensure that the `download_file` method has `action_auth_level :download_file, :instructor` to restrict access to instructors.

Applied to files:

  • app/controllers/submissions_controller.rb
📚 Learning: 2024-06-10T19:38:42.782Z
Learnt from: jlge
Repo: autolab/Autolab PR: 2011
File: app/controllers/assessments_controller.rb:28-29
Timestamp: 2024-06-10T19:38:42.782Z
Learning: `destroy_no_redirect` in `app/controllers/assessments_controller.rb` is an internal method used within the controller and not a public endpoint. It should not be included in the `before_action :set_assessment` exclusion list.

Applied to files:

  • app/controllers/submissions_controller.rb
🧬 Code graph analysis (1)
app/controllers/submissions_controller.rb (3)
app/models/submission.rb (1)
  • final_score (327-335)
app/controllers/application_controller.rb (1)
  • action_auth_level (53-71)
app/helpers/application_helper.rb (1)
  • computed_score (111-118)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (6)
app/views/submissions/index.html.erb (1)

120-147: LGTM: Header structure and DataTables placeholder.

The streamlined header structure with the select-all checkbox, conditional sort icons, and DataTables-driven tbody placeholder is well-implemented and aligns with the server-side processing migration.

app/controllers/submissions_controller.rb (2)

19-34: LGTM: HTML format initialization.

The HTML format branch correctly initializes instance variables and loads a limited set of submissions (100) for the initial page render. The @submissions_to_cud mapping is efficiently created using pluck and to_h.


105-137: LGTM: DataTables formatter method.

The format_submission_for_datatable method is properly authorized at the :instructor level and returns a well-structured array for DataTables consumption. The use of instance variables and helper methods is appropriate.

app/assets/javascripts/manage_submissions.js (3)

76-96: LGTM: Delegated event handler.

The use of $(document).on('click', '.score-details', ...) is the correct pattern for handling clicks on dynamically created DataTables rows. This ensures the event handler works for elements rendered after page load.


236-306: LGTM: DataTables server-side configuration.

The DataTables initialization with server-side processing is well-implemented:

  • AJAX endpoint correctly targets the JSON format
  • dataSrc callback builds the submissions_to_cud mapping from response data
  • Column definitions properly handle rendering, sorting, and visibility
  • createdRow and drawCallback maintain selection state across redraws

315-321: LGTM: Select-all checkbox synchronization.

The updateSelectAllCheckbox function correctly synchronizes the select-all checkbox state with individual row selections.

}
}

$(document).ready(function () {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we remove these double document.ready functions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i don't really know why, but the page does not work without both document.ready functions. I figured whoever made the double document.ready functions in the first place had some reason for doing it.

var submission_info = {}
var submission_info = {};
var selectedStudentCids = [];
var selectedSubmissions = [];
Copy link
Contributor

Choose a reason for hiding this comment

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

should we make these sets, would this be faster when we check for inclusion?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

some functions (e.g requestData) expect arrays, changing to sets would be slower for those

Copy link
Contributor

@KesterTan KesterTan left a comment

Choose a reason for hiding this comment

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

Overall functionality works fine, nice that pagination finally works as expected. There's some delay in making the requests but sorting/searching still seem reasonably fast. Just have a few comments.

@KesterTan
Copy link
Contributor

Regrade all causes the whole page to re-render, the submissions seem to be empty for a while before it is loaded again.

As a result, the tooltip for regrade all also dissapears.

@dwang3851
Copy link
Contributor Author

Regrade all causes the whole page to re-render, the submissions seem to be empty for a while before it is loaded again.

As a result, the tooltip for regrade all also dissapears.

The tooltip for regrade all does not disappear for me, was unable to reproduce this behavior

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: 0

🧹 Nitpick comments (1)
app/controllers/submissions_controller.rb (1)

67-68: Minor typo in comment.

Line 67: "we don't stored" should be "we don't store".

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6f0c1e and e4db7d0.

📒 Files selected for processing (1)
  • app/controllers/submissions_controller.rb (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-06-10T19:38:42.782Z
Learnt from: najclark
Repo: autolab/Autolab PR: 2011
File: app/controllers/courses_controller.rb:1316-1320
Timestamp: 2024-06-10T19:38:42.782Z
Learning: The user confirmed the concern about potential security risks when setting `flash[:error]` and `flash[:html_safe]` with user-supplied input, specifically mentioning `course_name` derived from a tar file's contents. It's important to ensure proper sanitization of user-supplied input to prevent XSS vulnerabilities, especially when `flash[:html_safe]` is used, which indicates the content should not be escaped.

Applied to files:

  • app/controllers/submissions_controller.rb
📚 Learning: 2024-09-20T20:46:52.775Z
Learnt from: evanyeyeye
Repo: autolab/Autolab PR: 2209
File: app/controllers/autograders_controller.rb:0-0
Timestamp: 2024-09-20T20:46:52.775Z
Learning: In the `AutogradersController`, ensure that the `download_file` method has `action_auth_level :download_file, :instructor` to restrict access to instructors.

Applied to files:

  • app/controllers/submissions_controller.rb
📚 Learning: 2024-06-10T19:38:42.782Z
Learnt from: jlge
Repo: autolab/Autolab PR: 2011
File: app/controllers/assessments_controller.rb:28-29
Timestamp: 2024-06-10T19:38:42.782Z
Learning: `destroy_no_redirect` in `app/controllers/assessments_controller.rb` is an internal method used within the controller and not a public endpoint. It should not be included in the `before_action :set_assessment` exclusion list.

Applied to files:

  • app/controllers/submissions_controller.rb
🧬 Code graph analysis (1)
app/controllers/submissions_controller.rb (3)
app/models/submission.rb (1)
  • final_score (327-335)
app/controllers/application_controller.rb (1)
  • action_auth_level (53-71)
app/helpers/application_helper.rb (1)
  • computed_score (111-118)
🪛 RuboCop (1.81.7)
app/controllers/submissions_controller.rb

[convention] 136-136: Put a comma after the last item of a multiline array.

(Style/TrailingCommaInArrayLiteral)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint
  • GitHub Check: test
🔇 Additional comments (3)
app/controllers/submissions_controller.rb (3)

42-43: LGTM: SQL injection vulnerability fixed.

The order direction is now properly sanitized using a whitelist before being used in the SQL query, addressing the critical security concern from the previous review.


106-138: LGTM: Well-structured DataTables formatter.

The method properly declares authorization, handles both pre-computed and dynamically-computed scores, and uses the computed_score helper to safely handle any score computation exceptions. The data structure aligns with DataTables' expected format.

Optional: RuboCop suggests adding a trailing comma after line 136 for consistency with Ruby style guides, but this is purely stylistic.


27-34: No changes needed—@Submissions is actively used in the view.

The search results show that @submissions is used in multiple ways in the view:

  • Line 15 iterates over @submissions to populate initial table data
  • Line 66 uses @submissions.any? to determine button state
  • Line 106 passes @submissions to a download helper

The HTML format correctly loads submissions because they're needed for initial data population, button availability checks, and download functionality. DataTables supplements this with server-side pagination for subsequent interactions, not replacing the initial data requirement.

Note: @submissions_to_cud (created on line 33 of the controller) does not appear to be used in the view and could potentially be removed if it's not needed elsewhere.

Likely an incorrect or invalid review comment.

@KesterTan
Copy link
Contributor

screen-recording-autolab-25nov.mov

Check recording above, there should be a tooltip that says x number of submissions regraded but none is shown.

Screenshot 2025-11-25 at 5 10 41 AM

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants