Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 22 additions & 32 deletions cms/server/admin/handlers/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,11 @@ def get(self, training_program_id: str, training_day_id: str):
'status_label': status_label,
})

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.render_params_for_training_program(training_program)
self.r_params["training_day"] = training_day
self.r_params["contest"] = contest
self.r_params["shared_ips"] = shared_ips
self.r_params["users_not_finished"] = users_not_finished
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == training_program.managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()
self.render("archive_training_day.html", **self.r_params)

@require_permission(BaseHandler.PERMISSION_ALL)
Expand Down Expand Up @@ -682,8 +675,7 @@ def get(self, training_program_id: str):
key=lambda s: s.participation.user.username if s.participation else ""
)

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.render_params_for_training_program(training_program)
self.r_params["archived_training_days"] = archived_training_days
self.r_params["attendance_data"] = attendance_data
self.r_params["sorted_students"] = sorted_students
Expand All @@ -694,12 +686,24 @@ def get(self, training_program_id: str):
self.r_params["all_training_day_types"] = get_all_training_day_types(
training_program)
self.r_params["all_student_tags"] = get_all_student_tags(training_program)
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == training_program.managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()

# Build training days with pending delays from notification data
training_days_with_pending_delays: list[dict] = []
td_notifications = self.r_params.get("training_day_notifications", {})
for td in training_program.training_days:
if td.contest is None:
continue
td_notif = td_notifications.get(td.id, {})
pending_count = td_notif.get("pending_delay_requests", 0)
if pending_count > 0:
training_days_with_pending_delays.append({
"contest_id": td.contest_id,
"name": td.contest.name,
"pending_count": pending_count,
})
self.r_params["training_days_with_pending_delays"] = \
training_days_with_pending_delays

self.render("training_program_attendance.html", **self.r_params)


Expand Down Expand Up @@ -818,8 +822,7 @@ def get(self, training_program_id: str):
key=lambda s: s.participation.user.username if s.participation else ""
)

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.render_params_for_training_program(training_program)
self.r_params["archived_training_days"] = filtered_training_days
self.r_params["ranking_data"] = ranking_data
self.r_params["sorted_students"] = sorted_students
Expand All @@ -834,12 +837,6 @@ def get(self, training_program_id: str):
self.r_params["all_training_day_types"] = get_all_training_day_types(
training_program)
self.r_params["all_student_tags"] = get_all_student_tags(training_program)
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == training_program.managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()
self.render("training_program_combined_ranking.html", **self.r_params)


Expand Down Expand Up @@ -1099,8 +1096,7 @@ def get_training_day_num(
params["student_tags_mode"] = student_tags_mode
history_url += "?" + urlencode(params)

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.render_params_for_training_program(training_program)
self.r_params["student"] = student
self.r_params["user_id"] = str(student.participation.user_id) if student.participation else "0"
self.r_params["user_count"] = user_count
Expand All @@ -1116,10 +1112,4 @@ def get_training_day_num(
self.r_params["training_day_types"] = training_day_types
self.r_params["student_tags"] = student_tags
self.r_params["student_tags_mode"] = student_tags_mode
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == training_program.managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()
self.render("training_program_combined_ranking_detail.html", **self.r_params)
74 changes: 74 additions & 0 deletions cms/server/admin/handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,8 +509,82 @@ def render_params(self) -> dict:
.order_by(TrainingProgram.name)
.all()
)

return params

def render_params_for_training_program(
self, training_program: "TrainingProgram"
) -> dict:
"""Initialize render params for a training program page.

This is a convenience method that combines render_params(),
setting training_program, contest, unanswered questions count,
and notification counts for training days in the sidebar.

Args:
training_program: The training program being viewed.

Returns:
The initialized r_params dict.
"""
managing_contest = training_program.managing_contest
self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.r_params["contest"] = managing_contest

# Count unanswered questions for the managing contest (used in sidebar)
self.r_params["unanswered"] = (
self.sql_session.query(Question)
.join(Participation)
.filter(Participation.contest_id == managing_contest.id)
.filter(Question.reply_timestamp.is_(None))
.filter(Question.ignored.is_(False))
.count()
)

# Add notification counts for training days
training_day_notifications: dict[int, dict] = {}
total_td_unanswered_questions = 0
total_td_pending_delay_requests = 0

for td in training_program.training_days:
if td.contest is None:
continue

# Count unanswered questions for this training day
td_unanswered = (
self.sql_session.query(Question)
.join(Participation)
.filter(Participation.contest_id == td.contest_id)
.filter(Question.reply_timestamp.is_(None))
.filter(Question.ignored.is_(False))
.count()
)

# Count pending delay requests for this training day
td_pending_delays = (
self.sql_session.query(DelayRequest)
.join(Participation)
.filter(Participation.contest_id == td.contest_id)
.filter(DelayRequest.status == "pending")
.count()
)

training_day_notifications[td.id] = {
"unanswered_questions": td_unanswered,
"pending_delay_requests": td_pending_delays,
}
total_td_unanswered_questions += td_unanswered
total_td_pending_delay_requests += td_pending_delays

self.r_params["training_day_notifications"] = training_day_notifications
self.r_params["total_td_unanswered_questions"] = \
total_td_unanswered_questions
self.r_params["total_td_pending_delay_requests"] = \
total_td_pending_delay_requests

return self.r_params

def write_error(self, status_code, **kwargs):
if "exc_info" in kwargs and kwargs["exc_info"][0] != tornado.web.HTTPError:
exc_info = kwargs["exc_info"]
Expand Down
14 changes: 12 additions & 2 deletions cms/server/admin/handlers/contest.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,12 @@ def get(self, contest_id: str | None = None):
if contest_id is not None:
self.contest = self.safe_get_item(Contest, contest_id)

self.r_params = self.render_params()
# If this contest is a managing contest for a training program,
# use render_params_for_training_program to show training day notifications
if self.contest is not None and self.contest.training_program is not None:
self.render_params_for_training_program(self.contest.training_program)
else:
self.r_params = self.render_params()
self.render("overview.html", **self.r_params)


Expand All @@ -298,7 +303,12 @@ def get(self, contest_id: str | None = None):
if contest_id is not None:
self.contest = self.safe_get_item(Contest, contest_id)

self.r_params = self.render_params()
# If this contest is a managing contest for a training program,
# use render_params_for_training_program to show training day notifications
if self.contest is not None and self.contest.training_program is not None:
self.render_params_for_training_program(self.contest.training_program)
else:
self.r_params = self.render_params()
self.r_params["resource_addresses"] = {}
services = get_service_shards("ResourceService")
for i in range(services):
Expand Down
51 changes: 12 additions & 39 deletions cms/server/admin/handlers/student.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,9 @@ def get(self, training_program_id: str):
training_program = self.safe_get_item(TrainingProgram, training_program_id)
managing_contest = training_program.managing_contest

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.r_params["contest"] = managing_contest
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()

assigned_user_ids_q = self.sql_session.query(Participation.user_id).filter(
self.render_params_for_training_program(training_program)

assigned_user_ids_q= self.sql_session.query(Participation.user_id).filter(
Participation.contest == managing_contest
)

Expand Down Expand Up @@ -191,6 +183,11 @@ def get(self, training_program_id: str, user_id: str):
.filter(Submission.participation == participation)
self.render_params_for_remove_confirmation(submission_query)

# Use the helper to set up training program params
self.render_params_for_training_program(training_program)
self.r_params["unanswered"] = 0 # Override for deletion confirmation page
self.r_params["user"] = user

# Count submissions and participations from training days
training_day_contest_ids = [td.contest_id for td in training_program.training_days]
training_day_contest_ids = [
Expand All @@ -215,10 +212,6 @@ def get(self, training_program_id: str, user_id: str):
training_day_participations = 0
training_day_submissions = 0

self.r_params["user"] = user
self.r_params["training_program"] = training_program
self.r_params["contest"] = managing_contest
self.r_params["unanswered"] = 0
self.r_params["training_day_submissions"] = training_day_submissions
self.r_params["training_day_participations"] = training_day_participations
self.render("training_program_student_remove.html", **self.r_params)
Expand Down Expand Up @@ -308,19 +301,13 @@ def get(self, training_program_id: str, user_id: str):
page = int(self.get_query_argument("page", "0"))
self.render_params_for_submissions(submission_query, page)

# Get all unique student tags from this training program for autocomplete
self.r_params["training_program"] = training_program
# render_params_for_training_program sets training_program, contest, unanswered
self.render_params_for_training_program(training_program)
self.r_params["participation"] = participation
self.r_params["student"] = student
self.r_params["selected_user"] = participation.user
self.r_params["teams"] = self.sql_session.query(Team).all()
self.r_params["all_student_tags"] = get_all_student_tags(training_program)
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()
self.render("student.html", **self.r_params)

@require_permission(BaseHandler.PERMISSION_ALL)
Expand Down Expand Up @@ -535,8 +522,7 @@ def get(self, training_program_id: str, user_id: str):
if task_id_str in archived_ranking.task_scores:
training_scores[st.task_id] = archived_ranking.task_scores[task_id_str]

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.render_params_for_training_program(training_program)
self.r_params["participation"] = participation
self.r_params["student"] = student
self.r_params["selected_user"] = participation.user
Expand All @@ -546,12 +532,6 @@ def get(self, training_program_id: str, user_id: str):
self.r_params["available_tasks"] = available_tasks
self.r_params["home_scores"] = home_scores
self.r_params["training_scores"] = training_scores
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()
self.render("student_tasks.html", **self.r_params)


Expand Down Expand Up @@ -703,16 +683,9 @@ def get(self, training_program_id: str):
# Get all unique student tags
all_student_tags = get_all_student_tags(training_program)

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.render_params_for_training_program(training_program)
self.r_params["all_tasks"] = all_tasks
self.r_params["all_student_tags"] = all_student_tags
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()
self.render("bulk_assign_task.html", **self.r_params)

@require_permission(BaseHandler.PERMISSION_ALL)
Expand Down
27 changes: 4 additions & 23 deletions cms/server/admin/handlers/trainingday.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,8 @@ class TrainingProgramTrainingDaysHandler(BaseHandler):
@require_permission(BaseHandler.AUTHENTICATED)
def get(self, training_program_id: str):
training_program = self.safe_get_item(TrainingProgram, training_program_id)
managing_contest = training_program.managing_contest

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.r_params["contest"] = managing_contest
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()
self.render_params_for_training_program(training_program)
self.r_params["all_training_day_types"] = get_all_training_day_types(
training_program)

Expand Down Expand Up @@ -251,15 +242,7 @@ def get(self, training_program_id: str):
training_program = self.safe_get_item(TrainingProgram, training_program_id)
managing_contest = training_program.managing_contest

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.r_params["contest"] = managing_contest
self.r_params["unanswered"] = self.sql_session.query(Question)\
.join(Participation)\
.filter(Participation.contest_id == managing_contest.id)\
.filter(Question.reply_timestamp.is_(None))\
.filter(Question.ignored.is_(False))\
.count()
self.render_params_for_training_program(training_program)

# Get all student tags for the tagify select dropdown
tags_query = self.sql_session.query(
Expand Down Expand Up @@ -446,11 +429,9 @@ def get(self, training_program_id: str, training_day_id: str):
if training_day.training_program_id != training_program.id:
raise tornado.web.HTTPError(404)

self.r_params = self.render_params()
self.r_params["training_program"] = training_program
self.render_params_for_training_program(training_program)
self.r_params["training_day"] = training_day
self.r_params["contest"] = managing_contest
self.r_params["unanswered"] = 0
self.r_params["unanswered"] = 0 # Override for deletion confirmation page

# Stats for warning message
self.r_params["task_count"] = len(training_day.tasks)
Expand Down
Loading
Loading