@@ -154,35 +154,16 @@ def api_scoreboard_config():
154154 )
155155
156156
157- @refbp .route ("/api/scoreboard/submissions" , methods = ("GET" ,))
158- @limiter .limit ("20 per minute" )
159- def api_scoreboard_submissions ():
160- """Team-grouped submission scores with per-task breakdown.
161-
162- Response shape::
157+ def _collect_submissions (* , include_admins : bool ) -> dict :
158+ """Build the challenge -> team -> entries mapping.
163159
164- {
165- "<short_name>": {
166- "<team label>": [
167- {
168- "ts": "DD/MM/YYYY HH:MM:SS",
169- "score": <float>,
170- "tasks": {"<task_name>": <float | null>, ...}
171- },
172- ...
173- ]
174- }
175- }
176-
177- ``tasks`` values are ``null`` for tasks whose underlying raw score was
178- ``None`` (bool-returning tests). Such tasks contribute 0 to ``score``.
160+ When *include_admins* is ``False``, submissions by admin users are
161+ silently skipped and submissions before the assignment start time
162+ are excluded so the public scoreboard only shows student work
163+ within the configured window.
179164 """
180- _scoreboard_enabled_or_abort ()
181-
182165 scores : dict [str , dict [str , list [dict ]]] = defaultdict (lambda : defaultdict (list ))
183166
184- # Eager-load test results; we always read them now that multi-task
185- # submissions are supported.
186167 submissions = Submission .query .options (
187168 selectinload (Submission .submission_test_results )
188169 ).all ()
@@ -191,25 +172,32 @@ def api_scoreboard_submissions():
191172 instance = submission .origin_instance
192173 if instance is None :
193174 continue
175+ user = instance .user
176+ if user is None :
177+ continue
178+ if not include_admins and user .is_admin :
179+ continue
194180 exercise = instance .exercise
195181 if exercise is None :
196182 continue
197183 cfg = exercise .config
198184 if cfg is None or cfg .category is None :
199185 continue
200186
187+ if not include_admins and cfg .submission_deadline_start :
188+ if submission .submission_ts < cfg .submission_deadline_start :
189+ continue
190+
201191 if not submission .submission_test_results :
202- # Nothing was tested — no meaningful score to plot.
203192 continue
204193
205194 total , breakdown = score_submission (
206195 submission .submission_test_results ,
207196 cfg .per_task_scoring_policies ,
208197 )
209198 if not breakdown :
210- # Every task was discarded; nothing to show for this submission.
211199 continue
212- team = team_identity (instance . user )
200+ team = team_identity (user )
213201 scores [exercise .short_name ][team ].append (
214202 {
215203 "ts" : datetime_to_string (submission .submission_ts ),
@@ -222,4 +210,51 @@ def api_scoreboard_submissions():
222210 for entries in challenge .values ():
223211 entries .sort (key = lambda e : e ["ts" ])
224212
225- return jsonify (scores )
213+ return scores
214+
215+
216+ @refbp .route ("/api/scoreboard/submissions" , methods = ("GET" ,))
217+ @limiter .limit ("120 per minute" )
218+ def api_scoreboard_submissions ():
219+ """Team-grouped submission scores with per-task breakdown.
220+
221+ Response shape::
222+
223+ {
224+ "<short_name>": {
225+ "<team label>": [
226+ {
227+ "ts": "DD/MM/YYYY HH:MM:SS",
228+ "score": <float>,
229+ "tasks": {"<task_name>": <float | null>, ...}
230+ },
231+ ...
232+ ]
233+ }
234+ }
235+
236+ ``tasks`` values are ``null`` for tasks whose underlying raw score was
237+ ``None`` (bool-returning tests). Such tasks contribute 0 to ``score``.
238+
239+ Submissions by admin users are excluded from the public scoreboard.
240+ """
241+ _scoreboard_enabled_or_abort ()
242+ return jsonify (_collect_submissions (include_admins = False ))
243+
244+
245+ @refbp .route ("/api/scoreboard/submissions/admin" , methods = ("GET" ,))
246+ @limiter .limit ("120 per minute" )
247+ def api_scoreboard_submissions_admin ():
248+ """Admin variant: includes submissions by admin users.
249+
250+ Requires an authenticated admin session; returns 403 otherwise.
251+ Same response shape as the public endpoint.
252+ """
253+ from flask_login import current_user
254+
255+ _scoreboard_enabled_or_abort ()
256+
257+ if not current_user .is_authenticated or not current_user .is_admin :
258+ abort (403 )
259+
260+ return jsonify (_collect_submissions (include_admins = True ))
0 commit comments