Skip to content

Conversation

@devin-ai-integration
Copy link

@devin-ai-integration devin-ai-integration bot commented Jan 17, 2026

Summary

This PR addresses three issues with the training program sidebar and adds a new feature:

  1. Task sidebar context: When viewing a task that belongs to an active training day, the sidebar now shows the training day's contest menu instead of the training program menu. If the training day is archived (no contest), it falls back to showing the training program sidebar.

  2. Overview and Resource Usage links: The "Overview" and "Resource Usage" links in the training program sidebar now link to the managing contest's overview and resource usage pages, keeping the user within the training program's contest context.

  3. Collapsible Training Days submenu: Replaced the simple "Training Days" menu entry with a collapsible sidebar section (similar to how contests work in the main menu) that lists all active training days and includes an "Add a training day" button for admins.

  4. Managing contest configuration on training program page: The training program general page now displays and allows editing of the managing contest's configuration fields (languages, limits, tokens, times, etc.).

Updates since last revision

  • Added searchable dropdowns for task selection: The task selection dropdowns in "Add task to student's archive" (student_tasks.html) and "Assign task to students" (bulk_assign_task.html) now use the same TomSelect-based searchable component.
  • Training day task selection now shows ALL tasks: When adding a task to a training day, the dropdown now shows all available tasks (not just those in the training program). Tasks not in the training program are marked with "(not in training program)" and a confirmation dialog appears before adding them. If confirmed, the task is automatically added to the training program as well.
  • Added searchable dropdown for "Add a new student": The student selection dropdown now uses the same TomSelect-based searchable component used elsewhere in the system.
  • Fixed redirect loop for overview/resourceslist pages: Added /overview and /resourceslist to the list of paths excluded from the managing contest redirect logic in base.py.
  • Added redirect handlers for /training_program/{id}/overview and /training_program/{id}/resourceslist: These handlers redirect users to the managing contest's overview and resourceslist pages.
  • Refactored contest configuration fields into a reusable template fragment (fragments/contest_config_fields.html)
  • Fixed contest config fields not persisting: TrainingProgramHandler.post() now correctly reads and saves all fields rendered by contest_config_fields.html, including: allowed_localizations, allow_delay_requests, allow_unofficial_submission_before_analysis_mode, block_hidden_participations, allow_password_authentication, allow_registration, ip_restriction, ip_autologin, per_user_time, and min_submission_interval_grace_period.

Review & Testing Checklist for Human

  • Test contest configuration persistence (especially login section): On a training program's general page, modify fields in the "Logging in" section (block hidden participations, allow password authentication, allow registration, IP restriction, IP autologin) and verify changes persist after saving.
  • Test other contest config fields: Modify allowed_localizations, per_user_time, and min_submission_interval_grace_period fields and verify they persist.
  • Test training day task selection with non-program tasks: Add a task to a training day that is NOT in the training program. Verify: (1) confirmation dialog appears, (2) task is added to both the training day AND the training program.
  • Test searchable dropdowns: Verify all three searchable dropdowns work correctly: "Add a new student", "Add task to student's archive", and "Assign task to students by tag".
  • Test redirect loop is fixed: Navigate to a training program and click Overview/Resource Usage links. Verify they redirect without causing an infinite redirect loop.
  • Test regular contest configuration page: Verify the contest configuration page still works correctly after the template refactoring.

Recommended test plan:

  1. Navigate to a training program's general page and modify login section fields (allow_password_authentication, ip_restriction, etc.) - verify they save correctly
  2. Modify allowed_localizations (e.g., "en,he") and per_user_time - verify they persist
  3. Navigate to a training day's tasks page and add a task that's not in the training program - verify confirmation dialog and automatic addition to program
  4. Navigate to a training program's Students page - verify the dropdown is searchable
  5. Click Overview/Resource Usage links in training program sidebar - verify no redirect loop

Notes

  • The selectattr("contest") Jinja2 filter is used to identify active training days (those with a contest, not archived)
  • Overview and Resource Usage links use training_program.managing_contest.id to link to the managing contest's pages
  • The shared fragment expects LANGUAGES, TOKEN_MODE_DISABLED, TOKEN_MODE_FINITE, TOKEN_MODE_INFINITE to be available in the template context
  • The prepare() method in base.py now excludes /overview and /resourceslist from the redirect logic to prevent loops
  • When adding a non-program task to a training day, the handler in contesttask.py automatically adds the task to the managing contest first
  • The contest config field handling in TrainingProgramHandler.post() follows the same patterns as ContestHandler.post() for consistency

Link to Devin run: https://app.devin.ai/sessions/a9d8ff324e80435a8266decdbff6e58f
Requested by: Ron Ryvchin (@ronryv)

Summary by CodeRabbit

  • New Features

    • Dedicated Training Days sidebar block and redirects for training program Overview and Resources pages
    • Separate "Training Program Participations" section in user view
  • Improvements

    • Searchable dropdowns and clearer placeholder options for task/user selects
    • Client-side confirmation when adding tasks not in the training program
    • Consolidated contest configuration in training program form, multipart form support, Reset button, and updated navigation targets
    • Participation start time now set immediately for training program registrations

✏️ Tip: You can customize this high-level summary in your review settings.

1. Show training day side menu when viewing a task in an active training day
   - Modified TaskHandler.get() to set self.contest to the training day's
     contest when the task has an active training day (not archived)

2. Fix Overview and Resource Usage links to stay within training program context
   - Changed the condition in base.html to link Overview to the training
     program's general page when in training program context
   - Resource usage still links to the main resource usage page

3. Add collapsible Training Days submenu in training program sidebar
   - Replaced the simple 'Training Days' link with a collapsible section
   - Shows all active training days with links to their contest pages
   - Includes 'Add a training day' button for admins with permission_all

Co-Authored-By: Ron Ryvchin <[email protected]>
@devin-ai-integration
Copy link
Author

devin-ai-integration bot commented Jan 17, 2026

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

📝 Walkthrough

Walkthrough

Adds two training-program redirect handlers and routes; centralizes contest configuration via a new fragment and contest_attrs/set_attrs flow with validation; reinitializes proxy after training-program updates; moves tasks into a training program's managing contest when assigned to a training day; updates multiple admin templates and client-side checks.

Changes

Cohort / File(s) Summary
Handler exports & routing
cms/server/admin/handlers/__init__.py
Import and register TrainingProgramOverviewRedirectHandler and TrainingProgramResourcesListRedirectHandler with routes /training_program/([0-9]+)/overview and /training_program/([0-9]+)/resourceslist.
Redirect control
cms/server/admin/handlers/base.py
Extend redirect-blocking logic to also block targets ending with /overview and /resourceslist.
TrainingProgram handler & redirects
cms/server/admin/handlers/trainingprogram.py
Replace direct field syncs with building contest_attrs and calling contest.set_attrs(...); validate contest start/stop; call proxy_service.reinitialize() after commit; add two redirect handlers for overview/resourceslist; adjust various input handling and participation start-time semantics.
Contest task assignment logic
cms/server/admin/handlers/contesttask.py
For training days, unassigned tasks = program tasks then other tasks; when assigning a task to a training day, move it into the training program’s managing contest (update task.contest and task.num) if not already present before assigning training_day_num.
Task rendering selection
cms/server/admin/handlers/task.py
When rendering a task page prefer contest from task.training_day.contest if present, otherwise use task.contest.
Contest config UI refactor
cms/server/admin/templates/contest.html, cms/server/admin/templates/fragments/contest_config_fields.html
Replace inline contest form in contest.html with included fragment fragments/contest_config_fields.html that centralizes contest configuration fields.
Training program template & sidebar
cms/server/admin/templates/training_program.html, cms/server/admin/templates/base.html
Change form to multipart/form-data, use textarea for description, include contest fragment, remove inline start/stop inputs and scripts; add a Training Days sidebar block and adjust menu link logic to reference the managing contest.
Contest tasks UI & client checks
cms/server/admin/templates/contest_tasks.html
Add id="add-task-form", expose program_task_ids, mark non-program options with data-not-in-program, and prompt for confirmation if adding a task not already in the training program.
Searchable selects & placeholders
cms/server/admin/templates/bulk_assign_task.html, .../student_tasks.html, .../training_program_students.html
Add searchable-select class to select elements and replace placeholder option value="null" with value="" selected disabled hidden.
User participations & registration flows
cms/server/admin/handlers/user.py, cms/server/contest/handlers/main.py
Split participations into training-program vs regular sets for admin UI; on registration for training programs set Participation.starting_time to now, create Student and add Participation entries for each training day.
Small template trims
cms/server/admin/templates/*
Remove inline Students/Tasks blocks from training program page and remove Training Days link from training program main menu; update several template UI texts and confirmation behaviors.

Sequence Diagram(s)

sequenceDiagram
  participant AdminUI as Admin UI
  participant Handler as TrainingProgramHandler
  participant DB as Database
  participant Proxy as ProxyService
  AdminUI->>Handler: POST /training_program/{id} (form with contest attrs)
  Handler->>DB: load training_program and managing_contest
  Handler->>Handler: build contest_attrs, validate start/stop
  Handler->>DB: contest.set_attrs(contest_attrs) and commit
  Handler->>Proxy: proxy_service.reinitialize()
  Proxy-->>Handler: ack
  Handler->>AdminUI: redirect back to training_program page
Loading
sequenceDiagram
  participant AdminUI as Admin UI
  participant AddHandler as AddContestTaskHandler
  participant DB as Database
  participant TaskModel as Task model
  AdminUI->>AddHandler: POST add task to training day (task_id)
  AddHandler->>DB: load task, training_program.managing_contest
  alt Task not in managing contest
    AddHandler->>TaskModel: update task.contest and task.num (move to managing contest)
    AddHandler->>DB: save task move
  end
  AddHandler->>TaskModel: set task.training_day_num, save
  AddHandler->>AdminUI: redirect to contest tasks page
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • Daniel-Aga
  • Command-Master

Poem

🐰
I hop through routes and tidy fields,
Move tasks to nests where order yields.
I nudge the proxy, wake the race,
Rearrange menus, tidy space.
A carrot commit—everything heals! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% 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 summarizes the two main objectives: fixing training program sidebar issues and adding contest configuration editing capabilities.

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

✨ Finishing touches
  • 📝 Generate docstrings

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

@devin-ai-integration devin-ai-integration bot changed the title Fix training program sidebar issues Fix training program sidebar issues and add contest configuration Jan 17, 2026
devin-ai-integration bot and others added 5 commits January 17, 2026 13:22
- Create fragments/contest_config_fields.html with all contest configuration fields
- Update contest.html to use the shared fragment
- Update training_program.html to use the shared fragment
- Remove tasks and students sections from training program general page (they have their own sections)

Co-Authored-By: Ron Ryvchin <[email protected]>
… URLs

When users navigate to /training_program/{id}/overview or
/training_program/{id}/resourceslist, these handlers redirect them to
the managing contest's overview and resourceslist pages respectively.

This fixes 404 errors that occur when users access these URLs directly
or from cached pages.

Co-Authored-By: Ron Ryvchin <[email protected]>
Add /overview and /resourceslist to the list of paths that should not be
redirected from managing contest URLs. This prevents the infinite redirect
loop between /training_program/{id}/overview and /contest/{id}/overview.

Co-Authored-By: Ron Ryvchin <[email protected]>
Update the 'Add a new student' form to use the same TomSelect-based
searchable dropdown that is used elsewhere in the system (e.g., adding
users to contests, adding tasks).

Co-Authored-By: Ron Ryvchin <[email protected]>
- Add searchable-select to 'Select a task to add to this student's task archive'
- Add searchable-select to 'Select a task to assign to students'
- Update training day task selection to show all available tasks
- Tasks not in the training program are marked with '(not in training program)'
- Show confirmation dialog when selecting a task not in the training program
- Automatically add the task to the training program when confirmed

Co-Authored-By: Ron Ryvchin <[email protected]>
@ronryv
Copy link
Collaborator

ronryv commented Jan 17, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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

🤖 Fix all issues with AI agents
In `@cms/server/admin/templates/fragments/contest_config_fields.html`:
- Line 187: Update the documentation link in the template fragment so it uses
HTTPS instead of HTTP: in the tokens parameters header line where the anchor
href currently begins with "http://cms.readthedocs.org/en/{{ rtd_version
}}/Configuring%20a%20contest.html#feedback-to-contestants", change the scheme to
"https://" while keeping the existing rtd_version interpolation and target
attribute intact.

In `@cms/server/admin/templates/training_program.html`:
- Around line 27-32: The post handler is not persisting contest config inputs
rendered by contest_config_fields.html; update TrainingProgramHandler.post() to
read the posted values for allowed_localizations, allow_delay_requests,
allow_unofficial_submission_before_analysis_mode, block_hidden_participations,
allow_password_authentication, allow_registration, ip_restriction, ip_autologin,
per_user_time, and min_submission_interval_grace_period from the request
(respecting their types: lists/booleans/ints/strings), apply them to
training_program.managing_contest (the Contest object manipulated in the
handler) and save/update the contest (persist changes) so user edits in the
login section and other rendered fields are not lost. Ensure you use the same
field names as in the fragment and validate/coerce inputs consistently with
existing Contest handling in TrainingProgramHandler.post().

ronryv and others added 2 commits January 17, 2026 16:59
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Add handling for the following fields that were rendered by
contest_config_fields.html but not being persisted:
- allowed_localizations (comma-separated list)
- allow_delay_requests (boolean)
- allow_unofficial_submission_before_analysis_mode (boolean)
- block_hidden_participations (boolean)
- allow_password_authentication (boolean)
- allow_registration (boolean)
- ip_restriction (boolean)
- ip_autologin (boolean)
- per_user_time (timedelta in seconds)
- min_submission_interval_grace_period (timedelta in seconds)

These fields are now read from the request and applied to the
managing_contest using the same patterns as ContestHandler.post().

Co-Authored-By: Ron Ryvchin <[email protected]>
@ronryv
Copy link
Collaborator

ronryv commented Jan 17, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

✅ Actions performed

Full review triggered.

Copy link

@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

🤖 Fix all issues with AI agents
In `@cms/server/admin/templates/base.html`:
- Around line 496-520: The new sidebar section uses
data-section-key="training-days" but the client-side collapsed/expanded
whitelist (variable/array named validSectionKeys or similar used when hydrating
collapsed state) doesn't include "training-days", causing a flash; update the
validSectionKeys array (or the whitelist used in the hydration logic/function
that reads data-section-key) to include the string "training-days" so the
server-rendered collapsed state is honored on first paint (search for
validSectionKeys, collapsedSections, or the hydration function that references
section keys to locate the exact spot).

In `@cms/server/admin/templates/bulk_assign_task.html`:
- Around line 53-55: The form submits task_id="" which the template now
produces, but BulkAssignTaskHandler.post only rejects the string "null", causing
safe_get_item to potentially raise a 404; update BulkAssignTaskHandler.post to
treat both an empty string ("") and the literal "null" as invalid task
selections (return the same validation error/early return), or alternatively
change the select's placeholder value to "null"—locate the handler method named
BulkAssignTaskHandler.post and add a check like if task_id in ("", "null") to
short-circuit with the validation response before calling safe_get_item.

In `@cms/server/admin/templates/fragments/contest_config_fields.html`:
- Line 187: The external documentation anchor in the template fragment (the <a>
tag linking to "https://cms.readthedocs.io/en/{{ rtd_version
}}/Configuring%20a%20contest.html#feedback-to-contestants") should include
rel="noopener noreferrer" in addition to target="_blank" to prevent tabnabbing;
update that <a> element in contest_config_fields.html to add the rel attribute
while preserving the existing href, target, and surrounding conditional.

- Auto-set starting_time when adding student to training program
  (so they can see everything immediately without a start button)
- Auto-set starting_time on registration to managing contest
  (training programs don't have a start button)
- Add 'training-days' to validSectionKeys in base.html to prevent
  flash of expanded menu on page load
- Fix BulkAssignTaskHandler.post to reject empty string task_id
  (in addition to 'null')
- Add rel='noopener noreferrer' to documentation link in
  contest_config_fields.html to prevent tabnabbing

Co-Authored-By: Ron Ryvchin <[email protected]>
@ronryv
Copy link
Collaborator

ronryv commented Jan 17, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

✅ Actions performed

Full review triggered.

Copy link

@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

🤖 Fix all issues with AI agents
In `@cms/server/admin/templates/student_tasks.html`:
- Around line 23-25: The AddStudentTaskHandler currently only rejects the string
"null" so empty selections from the template (which submit value="") bypass
validation and cause safe_get_item() to 404; update the validation in
AddStudentTaskHandler to treat both empty string and "null" as invalid (i.e.,
check if task_id in ("", "null")) before calling safe_get_item(), mirroring the
validation used elsewhere in the file.

In `@cms/server/admin/templates/training_program_students.html`:
- Around line 21-23: The form sends an empty string for the placeholder option
but the validation in trainingprogram.py checks for the literal "null"; update
the validation to check for an empty string instead. Locate the handler/function
in trainingprogram.py that reads user_id (the code using assert user_id !=
"null") and change the assertion to assert user_id != "" (or equivalently check
if user_id), mirroring the validation used in contestuser.py to prevent empty
submissions from passing. Ensure any related error messages or control flow
still behave correctly after this change.
♻️ Duplicate comments (3)
cms/server/admin/templates/bulk_assign_task.html (1)

53-55: Server-side validation may still reject only "null", not empty values.

If BulkAssignTaskHandler.post still checks only "null", the new empty-string placeholder can fall through and end in a 404 instead of a clean validation error.

cms/server/admin/templates/training_program.html (1)

27-32: Verify all contest config fields are processed by the handler.

The fragment is included with show_login_section=true, which renders login-related fields. I've verified that TrainingProgramHandler.post() now processes these fields (lines 153-158 in trainingprogram.py), including block_hidden_participations, allow_password_authentication, allow_registration, ip_restriction, and ip_autologin.

The previous review concern about missing field processing has been addressed.

cms/server/admin/templates/base.html (1)

9-9: Past review concern addressed.

The 'training-days' key has been added to validSectionKeys, preventing the flash of expanded menus during hydration.

🧹 Nitpick comments (1)
cms/server/admin/templates/contest_tasks.html (1)

15-34: LGTM! Client-side confirmation for non-program tasks is well-implemented.

The confirmation logic correctly:

  1. Parses the task ID and checks against programTaskIds
  2. Guards against empty selection with selectedTaskId && (NaN is falsy)
  3. Prevents form submission if user cancels

Consider using Array.includes() instead of indexOf() for slightly cleaner code:

Optional modernization
-        if (selectedTaskId && programTaskIds.indexOf(selectedTaskId) === -1) {
+        if (selectedTaskId && !programTaskIds.includes(selectedTaskId)) {

devin-ai-integration bot and others added 2 commits January 17, 2026 16:30
- Fix AddStudentTaskHandler to reject empty string task_id
- Fix AddTrainingProgramStudentHandler to check for empty string user_id
- Modernize indexOf to includes in contest_tasks.html

Co-Authored-By: Ron Ryvchin <[email protected]>
- Create Student record when user registers to training program's managing contest
  (mirrors AddTrainingProgramStudentHandler behavior)
- Also add participations to all existing training days on registration
- Hide training day contests from 'Add a new participation' dropdown
- Hide managing contests (training programs) from 'Add a new participation' dropdown
- Separate training program participations into their own section in user page
- Hide training day participations from user page (managed via training program)
- Add confirmation dialog for removing regular participations
- Training program removal links to 'remove from training program' flow

Co-Authored-By: Ron Ryvchin <[email protected]>
@ronryv
Copy link
Collaborator

ronryv commented Jan 17, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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

🤖 Fix all issues with AI agents
In `@cms/server/admin/templates/user.html`:
- Line 26: The onclick confirm call directly injects p.user.username and
p.contest.training_program.name into a JS string; change those interpolations to
use the Jinja2 |tojson filter so the values are safely escaped for JavaScript
(e.g., replace p.user.username and p.contest.training_program.name with their
|tojson forms in the onclick string), matching the safe usage already present on
lines ~100/104 in this template.
🧹 Nitpick comments (3)
cms/server/admin/handlers/user.py (1)

51-76: Participation categorization logic is clear and well-documented.

The separation into training program, training day, and regular participations aligns with the PR objectives.

One consideration: accessing p.contest.training_program and p.contest.training_day for each participation may trigger N+1 queries if the relationships aren't eagerly loaded. If user pages become slow with many participations, consider using joinedload or selectinload on the initial query.

cms/server/admin/handlers/trainingprogram.py (2)

143-145: Guard against clearing contest languages unintentionally.

If the form submits no languages, this will persist an empty list; please confirm that’s valid for a contest. If not, preserve the existing list or require at least one language.

🔧 Possible guard
-            # Programming languages
-            contest_attrs["languages"] = self.get_arguments("languages")
+            # Programming languages
+            selected_languages = self.get_arguments("languages")
+            if not selected_languages:
+                raise ValueError("Please select at least one language")
+            contest_attrs["languages"] = selected_languages

489-506: Avoid assert for request validation.

assert can be stripped in optimized Python, which would bypass the check. Prefer an explicit guard.

🔧 Suggested change
-            user_id: str = self.get_argument("user_id")
-            assert user_id != "", "Please select a valid user"
+            user_id: str = self.get_argument("user_id")
+            if user_id == "":
+                raise ValueError("Please select a valid user")

@ronryv ronryv merged commit 9da7db4 into training_program Jan 17, 2026
5 checks passed
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.

2 participants