Skip to content

Commit b183593

Browse files
committed
Allow configuring subtasks to always show testcases.
Also defines a new (optional) format for parameters for ScoreTypeGroup.
1 parent 9d9ef69 commit b183593

2 files changed

Lines changed: 44 additions & 12 deletions

File tree

cms/grading/scoretypes/GroupThreshold.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,21 @@ class GroupThreshold(ScoreTypeGroup):
4040

4141
def get_public_outcome(self, outcome, parameter):
4242
"""See ScoreTypeGroup."""
43-
threshold = parameter[2]
43+
if isinstance(parameter, list):
44+
threshold = parameter[2]
45+
else:
46+
threshold = parameter["threshold"]
4447
if 0.0 < outcome <= threshold:
4548
return N_("Correct")
4649
else:
4750
return N_("Not correct")
4851

4952
def reduce(self, outcomes, parameter):
5053
"""See ScoreTypeGroup."""
51-
threshold = parameter[2]
54+
if isinstance(parameter, list):
55+
threshold = parameter[2]
56+
else:
57+
threshold = parameter["threshold"]
5258
if all(0 < outcome <= threshold for outcome in outcomes):
5359
return 1.0
5460
else:

cms/grading/scoretypes/abc.py

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import logging
3434
import re
35+
from typing import TypedDict, NotRequired
3536
from abc import ABCMeta, abstractmethod
3637

3738
from cms import FEEDBACK_LEVEL_RESTRICTED
@@ -192,6 +193,13 @@ class ScoreTypeAlone(ScoreType):
192193
pass
193194

194195

196+
class ScoreTypeGroupParameters(TypedDict):
197+
max_score: float
198+
testcases: int | str
199+
threshold: NotRequired[float]
200+
always_show_testcases: NotRequired[bool]
201+
202+
195203
class ScoreTypeGroup(ScoreTypeAlone):
196204
"""Intermediate class to manage tasks whose testcases are
197205
subdivided in groups (or subtasks). The score type parameters must
@@ -211,7 +219,7 @@ class ScoreTypeGroup(ScoreTypeAlone):
211219
# the format of parameters is impossible to type-hint correctly, it seems...
212220
# this hint is (mostly) correct for the methods this base class implements,
213221
# subclasses might need a longer tuple.
214-
parameters: list[tuple[float, int | str]]
222+
parameters: list[tuple[float, int | str] | ScoreTypeGroupParameters]
215223

216224
# Mark strings for localization.
217225
N_("Subtask %(index)s")
@@ -333,6 +341,24 @@ class ScoreTypeGroup(ScoreTypeAlone):
333341
</div>
334342
{% endfor %}"""
335343

344+
def get_max_score(self, group_parameter: tuple[float, int | str] | ScoreTypeGroupParameters) -> float:
345+
if isinstance(group_parameter, tuple) or isinstance(group_parameter, list):
346+
return group_parameter[0]
347+
else:
348+
return group_parameter["max_score"]
349+
350+
def get_testcases(self, group_parameter: tuple[float, int | str] | ScoreTypeGroupParameters) -> int | str:
351+
if isinstance(group_parameter, tuple) or isinstance(group_parameter, list):
352+
return group_parameter[1]
353+
else:
354+
return group_parameter["testcases"]
355+
356+
def get_always_show_testcases(self, group_parameter: tuple[float, int | str] | ScoreTypeGroupParameters) -> bool:
357+
if isinstance(group_parameter, tuple) or isinstance(group_parameter, list):
358+
return False
359+
else:
360+
return group_parameter.get("always_show_testcases", False)
361+
336362
def retrieve_target_testcases(self) -> list[list[str]]:
337363
"""Return the list of the target testcases for each subtask.
338364
@@ -345,7 +371,7 @@ def retrieve_target_testcases(self) -> list[list[str]]:
345371
346372
"""
347373

348-
t_params = [p[1] for p in self.parameters]
374+
t_params = [self.get_testcases(p) for p in self.parameters]
349375

350376
if all(isinstance(t, int) for t in t_params):
351377

@@ -393,10 +419,10 @@ def max_scores(self):
393419

394420
for st_idx, parameter in enumerate(self.parameters):
395421
target = targets[st_idx]
396-
score += parameter[0]
422+
score += self.get_max_score(parameter)
397423
if all(self.public_testcases[tc_idx] for tc_idx in target):
398-
public_score += parameter[0]
399-
headers += ["Subtask %d (%g)" % (st_idx, parameter[0])]
424+
public_score += self.get_max_score(parameter)
425+
headers += ["Subtask %d (%g)" % (st_idx, self.get_max_score(parameter))]
400426

401427
return score, public_score, headers
402428

@@ -461,10 +487,10 @@ def compute_score(self, submission_result):
461487
st_score_fraction = self.reduce(
462488
[float(evaluations[tc_idx].outcome) for tc_idx in target],
463489
parameter)
464-
st_score = st_score_fraction * parameter[0]
490+
st_score = st_score_fraction * self.get_max_score(parameter)
465491
rounded_score = round(st_score, score_precision)
466492

467-
if tc_first_lowest_idx is not None and st_score_fraction < 1.0:
493+
if tc_first_lowest_idx is not None and st_score_fraction < 1.0 and not self.get_always_show_testcases(parameter):
468494
for tc in testcases:
469495
if not self.public_testcases[tc["idx"]]:
470496
continue
@@ -482,7 +508,7 @@ def compute_score(self, submission_result):
482508
"score_fraction": st_score_fraction,
483509
# But we also want the properly rounded score for display.
484510
"score": rounded_score,
485-
"max_score": parameter[0],
511+
"max_score": self.get_max_score(parameter),
486512
"testcases": testcases})
487513
if all(self.public_testcases[tc_idx] for tc_idx in target):
488514
public_score += st_score
@@ -495,7 +521,7 @@ def compute_score(self, submission_result):
495521
return score, subtasks, public_score, public_subtasks, ranking_details
496522

497523
@abstractmethod
498-
def get_public_outcome(self, outcome: float, parameter: list) -> str:
524+
def get_public_outcome(self, outcome: float, parameter: list | ScoreTypeGroupParameters) -> str:
499525
"""Return a public outcome from an outcome.
500526
501527
The public outcome is shown to the user, and this method
@@ -512,7 +538,7 @@ def get_public_outcome(self, outcome: float, parameter: list) -> str:
512538
pass
513539

514540
@abstractmethod
515-
def reduce(self, outcomes: list[float], parameter: list) -> float:
541+
def reduce(self, outcomes: list[float], parameter: list | ScoreTypeGroupParameters) -> float:
516542
"""Return the score of a subtask given the outcomes.
517543
518544
outcomes: the outcomes of the submission in

0 commit comments

Comments
 (0)