@@ -109,12 +109,19 @@ def compare(a, b):
109109
110110# Computing global scores (for ranking).
111111
112- def task_score (participation , task ):
112+ def task_score (participation , task , public = False , only_tokened = False ):
113113 """Return the score of a contest's user on a task.
114114
115115 participation (Participation): the user and contest for which to
116116 compute the score.
117117 task (Task): the task for which to compute the score.
118+ public (bool): if True, compute the public score (that is, the one
119+ discoverable looking only at the results of public testcases) instead
120+ of the full score.
121+ only_tokened (bool): if True, compute the score discoverable only looking
122+ at the results of tokened submissions (that is, the score that the user
123+ would obtain if all non-tokened submissions scored 0.0, or equivalently
124+ had not been scored yet).
118125
119126 return ((float, bool)): the score of user on task, and True if not
120127 all submissions of the participation in the task have been scored.
@@ -128,6 +135,12 @@ def task_score(participation, task):
128135 # submission_results table. Doing so means that this function should incur
129136 # no exta database queries.
130137
138+ if public and only_tokened :
139+ raise ValueError (
140+ "Requested public task score restricted to tokened submissions. "
141+ "This is a programming error: users have access to all public "
142+ "scores regardless of token status." )
143+
131144 submissions = [s for s in participation .submissions
132145 if s .task is task and s .official ]
133146 if len (submissions ) == 0 :
@@ -137,54 +150,67 @@ def task_score(participation, task):
137150 (s , s .get_result (task .active_dataset ))
138151 for s in sorted (submissions , key = lambda s : s .timestamp )]
139152
153+ score_details_tokened = []
154+ partial = False
155+ for s , sr in submissions_and_results :
156+ if sr is None or not sr .scored ():
157+ partial = True
158+ score , score_details = None , None
159+ elif public :
160+ score , score_details = sr .public_score , sr .public_score_details
161+ elif only_tokened and not s .tokened ():
162+ # If the caller wants the only_tokened score and this submission is
163+ # not tokened, the score mode should ignore its score. To do so, we
164+ # send to the score mode what we would send if it wasn't already
165+ # scored.
166+ score , score_details = None , None
167+ else :
168+ score , score_details = sr .score , sr .score_details
169+ score_details_tokened .append ((score , score_details , s .tokened ()))
170+
140171 if task .score_mode == SCORE_MODE_MAX :
141- return _task_score_max (submissions_and_results )
172+ return _task_score_max (score_details_tokened ), partial
142173 if task .score_mode == SCORE_MODE_MAX_SUBTASK :
143- return _task_score_max_subtask (submissions_and_results )
174+ return _task_score_max_subtask (score_details_tokened ), partial
144175 elif task .score_mode == SCORE_MODE_MAX_TOKENED_LAST :
145- return _task_score_max_tokened_last (submissions_and_results )
176+ return _task_score_max_tokened_last (score_details_tokened ), partial
146177 else :
147178 raise ValueError ("Unknown score mode '%s'" % task .score_mode )
148179
149180
150- def _task_score_max_tokened_last (submissions_and_results ):
181+ def _task_score_max_tokened_last (score_details_tokened ):
151182 """Compute score using the "max tokened last" score mode.
152183
153184 This was used in IOI 2010-2012. The score of a participant on a task is
154185 the maximum score amongst all tokened submissions and the last submission
155186 (not yet computed scores count as 0.0).
156187
157- submissions_and_results ([(Submission, SubmissionResult|None)]): list of
158- all submissions and their results for the participant on the task (on
159- the dataset of interest); result is None if not available (that is,
160- if the submission has not been compiled).
188+ score_details_tokened ([(float|None, object|None, bool)]): a tuple for each
189+ submission of the user in the task, containing score, score details
190+ (each None if not scored yet) and if the submission was tokened.
161191
162- return (( float, bool)): (score, partial), same as task_score() .
192+ return (float): the score .
163193
164194 """
165- partial = False
166195
167196 # The score of the last submission (if computed, otherwise 0.0). Note that
168197 # partial will be set to True in the next loop.
169- last_score = 0.0
170- _ , last_sr = submissions_and_results [- 1 ]
171- if last_sr is not None and last_sr .scored ():
172- last_score = last_sr .score
198+ last_score , _ , _ = score_details_tokened [- 1 ]
199+ if last_score is None :
200+ last_score = 0.0
173201
174202 # The maximum score amongst the tokened submissions (not yet computed
175203 # scores count as 0.0).
176204 max_tokened_score = 0.0
177- for s , sr in submissions_and_results :
178- if sr is not None and sr .scored ():
179- if s .tokened ():
180- max_tokened_score = max (max_tokened_score , sr .score )
181- else :
182- partial = True
205+ for score , _ , tokened in score_details_tokened :
206+ if score is not None :
207+ if tokened :
208+ max_tokened_score = max (max_tokened_score , score )
183209
184- return max (last_score , max_tokened_score ), partial
210+ return max (last_score , max_tokened_score )
185211
186212
187- def _task_score_max_subtask (submissions_and_results ):
213+ def _task_score_max_subtask (score_details_tokened ):
188214 """Compute score using the "max subtask" score mode.
189215
190216 This has been used in IOI since 2017. The score of a participant on a
@@ -196,68 +222,60 @@ def _task_score_max_subtask(submissions_and_results):
196222 this is not true, the score mode will work as if the task had a single
197223 subtask.
198224
199- submissions_and_results ([(Submission, SubmissionResult|None)]): list of
200- all submissions and their results for the participant on the task (on
201- the dataset of interest); result is None if not available (that is,
202- if the submission has not been compiled).
225+ score_details_tokened ([(float|None, object|None, bool)]): a tuple for each
226+ submission of the user in the task, containing score, score details
227+ (each None if not scored yet) and if the submission was tokened.
203228
204- return (( float, bool)): (score, partial), same as task_score() .
229+ return (float): the score .
205230
206231 """
207232 # Maximum score for each subtask (not yet computed scores count as 0.0).
208233 max_scores = {}
209234
210- partial = False
211- for _ , sr in submissions_and_results :
212- if sr is None or not sr .scored ():
213- partial = True
235+ for score , details , _ in score_details_tokened :
236+ if score is None :
214237 continue
215238
216- if sr . score_details == [] and sr . score == 0.0 :
239+ if details == [] and score == 0.0 :
217240 # Submission did not compile, ignore it.
218241 continue
219242
220243 try :
221244 subtask_scores = dict (
222245 (subtask ["idx" ],
223246 subtask ["score_fraction" ] * subtask ["max_score" ])
224- for subtask in sr .score_details
225- )
247+ for subtask in details )
226248 except Exception :
227249 subtask_scores = None
228250
229251 if subtask_scores is None or len (subtask_scores ) == 0 :
230252 # Task's score type is not group, assume a single subtask.
231- subtask_scores = {1 : sr . score }
253+ subtask_scores = {1 : score }
232254
233255 for idx , score in iteritems (subtask_scores ):
234256 max_scores [idx ] = max (max_scores .get (idx , 0.0 ), score )
235257
236- return sum (itervalues (max_scores )), partial
258+ return sum (itervalues (max_scores ))
237259
238260
239- def _task_score_max (submissions_and_results ):
261+ def _task_score_max (score_details_tokened ):
240262 """Compute score using the "max" score mode.
241263
242264 This was used in IOI 2013-2016. The score of a participant on a task is
243265 the maximum score amongst all submissions (not yet computed scores count
244266 as 0.0).
245267
246- submissions_and_results ([(Submission, SubmissionResult|None)]): list of
247- all submissions and their results for the participant on the task (on
248- the dataset of interest); result is None if not available (that is,
249- if the submission has not been compiled).
268+ score_details_tokened ([(float|None, object|None, bool)]): a tuple for each
269+ submission of the user in the task, containing score, score details
270+ (each None if not scored yet) and if the submission was tokened.
250271
251- return (( float, bool)): (score, partial), same as task_score() .
272+ return (float): the score .
252273
253274 """
254- partial = False
255- score = 0.0
275+ max_score = 0.0
256276
257- for _ , sr in submissions_and_results :
258- if sr is not None and sr .scored ():
259- score = max (score , sr .score )
260- else :
261- partial = True
277+ for score , _ , _ in score_details_tokened :
278+ if score is not None :
279+ max_score = max (max_score , score )
262280
263- return score , partial
281+ return max_score
0 commit comments