diff --git a/cms/grading/scoretypes/GroupSum.py b/cms/grading/scoretypes/GroupSum.py new file mode 100644 index 0000000000..1797c845d0 --- /dev/null +++ b/cms/grading/scoretypes/GroupSum.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +# Contest Management System - http://cms-dev.github.io/ +# Copyright © 2010-2012 Giovanni Mascellani +# Copyright © 2010-2018 Stefano Maggiolo +# Copyright © 2010-2012 Matteo Boscariol +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from . import ScoreTypeGroup + + +# Dummy function to mark translatable string. +def N_(message): + return message + + +class GroupSum(ScoreTypeGroup): + """The score of a submission is the sum of group scores, + and each group score is the sum of testcase scores in the group. + + Parameters are [[m, t], ... ] (see ScoreTypeGroup). + + """ + + def get_public_outcome(self, outcome, unused_parameter): + """See ScoreTypeGroup.""" + if outcome <= 0.0: + return N_("Not correct") + elif outcome >= 1.0: + return N_("Correct") + else: + return N_("Partially correct") + + def reduce(self, outcomes, unused_parameter): + """See ScoreTypeGroup.""" + return sum(outcomes) / len(outcomes) diff --git a/cms/grading/scoretypes/GroupSumCheck.py b/cms/grading/scoretypes/GroupSumCheck.py new file mode 100644 index 0000000000..5e660da06a --- /dev/null +++ b/cms/grading/scoretypes/GroupSumCheck.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +# Contest Management System - http://cms-dev.github.io/ +# Copyright © 2010-2012 Giovanni Mascellani +# Copyright © 2010-2018 Stefano Maggiolo +# Copyright © 2010-2012 Matteo Boscariol +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from cms.grading.scoretypes.GroupSum import GroupSum + +class GroupSumCheck(GroupSum): + """The score of a submission is the sum of group scores, + and each group score is the sum of testcase scores in the group, + except when any testcase scores is negative, the total for the + whole group is zero. + """ + + def reduce(self, outcomes, parameter): + """See ScoreTypeGroup.""" + if min(outcomes) < 0: return 0.0 + return sum(outcomes) / len(outcomes) + diff --git a/cms/grading/scoretypes/GroupSumCond.py b/cms/grading/scoretypes/GroupSumCond.py new file mode 100644 index 0000000000..001717df8d --- /dev/null +++ b/cms/grading/scoretypes/GroupSumCond.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +# Contest Management System - http://cms-dev.github.io/ +# Copyright © 2010-2012 Giovanni Mascellani +# Copyright © 2010-2018 Stefano Maggiolo +# Copyright © 2010-2012 Matteo Boscariol +# Copyright © 2018 Ahto Truu +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from cms.grading.scoretypes.GroupSum import GroupSum + +class GroupSumCond(GroupSum): + """The score of a submission is the sum of group scores, + and each group score is the sum of testcase scores in the group, + except the scores for "conditional" groups are only given if the + solution gets at least some points for the "unconditional" groups. + Parameters are [[m, t, f], ... ]. See ScoreTypeGroup for m and t. + The flag f must be one of "U", "C", "E": + "U" for unconditional group whose score is always counted, + "C" for conditional group whose score is only counted + if the total for unconditional scores is non-zero, + "E" for examples (these typically don't score points, but even + if they do, they do not affect the conditional groups). + + """ + + def max_scores(self): + score, public_score, headers = GroupSum.max_scores(self) + for st_idx, parameter in enumerate(self.parameters): + if parameter[2] == "C": + headers[st_idx] += " ***" + return score, public_score, headers + + def compute_score(self, submission_result): + score, subtasks, public_score, public_subtasks, ranking_details = GroupSum.compute_score(self, submission_result) + if len(subtasks) == len(self.parameters): + u_score = 0 + for st_idx, parameter in enumerate(self.parameters): + if parameter[2] == "U": + u_score += subtasks[st_idx]["score_fraction"] * parameter[0] + if u_score == 0: + for st_idx, parameter in enumerate(self.parameters): + if parameter[2] == "C": + st_score = subtasks[st_idx]["score_fraction"] * parameter[0] + score -= st_score + if public_subtasks[st_idx] == subtasks[st_idx]: + public_score -= st_score + ranking_details[st_idx] = "0" + subtasks[st_idx]["score_ignore"] = True + return score, subtasks, public_score, public_subtasks, ranking_details diff --git a/cms/grading/scoretypes/abc.py b/cms/grading/scoretypes/abc.py index f0fe3e3dd2..6d4f78825d 100644 --- a/cms/grading/scoretypes/abc.py +++ b/cms/grading/scoretypes/abc.py @@ -205,7 +205,7 @@ class ScoreTypeGroup(ScoreTypeAlone): N_("N/A") TEMPLATE = """\ {% for st in details %} - {% if "score_fraction" in st %} + {% if "score_ignore" not in st and "score_fraction" in st %} {% if st["score_fraction"] >= 1.0 %}
{% elif st["score_fraction"] <= 0.0 %} @@ -220,7 +220,7 @@ class ScoreTypeGroup(ScoreTypeAlone): {% trans index=st["idx"] %}Subtask {{ index }}{% endtrans %} - {% if "score_fraction" in st and "max_score" in st %} + {% if "score_ignore" not in st and "score_fraction" in st and "max_score" in st %} {% set score = st["score_fraction"] * st["max_score"] %} ({{ score|round(2)|format_decimal }} diff --git a/setup.py b/setup.py index 15c0a7d5a0..999214df0b 100755 --- a/setup.py +++ b/setup.py @@ -179,6 +179,9 @@ def run(self): "Sum=cms.grading.scoretypes.Sum:Sum", "GroupMin=cms.grading.scoretypes.GroupMin:GroupMin", "GroupMul=cms.grading.scoretypes.GroupMul:GroupMul", + "GroupSum=cms.grading.scoretypes.GroupSum:GroupSum", + "GroupSumCheck=cms.grading.scoretypes.GroupSumCheck:GroupSumCheck", + "GroupSumCond=cms.grading.scoretypes.GroupSumCond:GroupSumCond", "GroupThreshold=cms.grading.scoretypes.GroupThreshold:GroupThreshold", ], "cms.grading.languages": [