diff --git a/mathrace_interaction/data/2015/disfida.journal b/mathrace_interaction/data/2015/disfida.journal index f35c903..a3aa081 100644 --- a/mathrace_interaction/data/2015/disfida.journal +++ b/mathrace_interaction/data/2015/disfida.journal @@ -558,7 +558,7 @@ 4848 110 18 13 0 PROT:449 squadra 18, quesito 13: sbagliato 4852 101 aggiorna punteggio esercizi, orologio: 81 4852 110 27 22 0 PROT:450 squadra 27, quesito 22: sbagliato -4858 130 1 44 squadra 1 bonus 44 motivazione: conflitto di ordine con scelta jolly +# 4858 130 1 44 squadra 1 bonus 44 motivazione: conflitto di ordine con scelta jolly 4866 110 17 11 1 PROT:451 squadra 17, quesito 11: giusto 4872 110 20 10 0 PROT:452 squadra 20, quesito 10: sbagliato 4873 110 10 19 1 PROT:453 squadra 10, quesito 19: giusto diff --git a/mathrace_interaction/data/2015/disfida.score b/mathrace_interaction/data/2015/disfida.score index 7136f73..6cdae23 100644 --- a/mathrace_interaction/data/2015/disfida.score +++ b/mathrace_interaction/data/2015/disfida.score @@ -9,8 +9,7 @@ 911, 792, 486, -# il risultato ufficiale è 402, dato da 352 + 50 di bonus per errore di immissione, che non consideriamo in turing: -352, +402, 843, 400, 519, diff --git a/mathrace_interaction/data/2015/kangourou.score b/mathrace_interaction/data/2015/kangourou.score index d97ab23..cf0cdf9 100644 --- a/mathrace_interaction/data/2015/kangourou.score +++ b/mathrace_interaction/data/2015/kangourou.score @@ -22,8 +22,7 @@ 329, 229, 293, -# il risultato ufficiale è 209, dato da 199 + 10 di bonus per errore di immissione, che non consideriamo in turing: -199, +209, 181, 521, 265, diff --git a/mathrace_interaction/data/2017/disfida.score b/mathrace_interaction/data/2017/disfida.score index 89d097d..8d42f7f 100644 --- a/mathrace_interaction/data/2017/disfida.score +++ b/mathrace_interaction/data/2017/disfida.score @@ -19,8 +19,7 @@ 369, 240, 608, -# il risultato ufficiale è 1811, dato da 1801 + 10 di bonus per errore di immissione, che non consideriamo in turing: -1801, +1811, 601, 372, 517, diff --git a/mathrace_interaction/data/2018/disfida.score b/mathrace_interaction/data/2018/disfida.score index 3f70d5b..d1fa6e4 100644 --- a/mathrace_interaction/data/2018/disfida.score +++ b/mathrace_interaction/data/2018/disfida.score @@ -14,8 +14,7 @@ 585, 344, 1184, -# il risultato ufficiale è 1624, dato da 1614 + 10 di bonus per errore di immissione, che non consideriamo in turing: -1614, +1624, 496, 540, 477, diff --git a/mathrace_interaction/data/2023/disfida_legacy_format.score b/mathrace_interaction/data/2023/disfida_legacy_format.score index 0464a36..07c4549 100644 --- a/mathrace_interaction/data/2023/disfida_legacy_format.score +++ b/mathrace_interaction/data/2023/disfida_legacy_format.score @@ -1,7 +1,6 @@ # http://disfida.it/2023/moviola/index_alt.php [531, -# il risultato ufficiale è 725, dato da 715 + 10 di bonus per errore di immissione, che non consideriamo in turing: -715, +725, 535, 542, 689, diff --git a/mathrace_interaction/data/2023/disfida_new_format.score b/mathrace_interaction/data/2023/disfida_new_format.score index 0464a36..07c4549 100644 --- a/mathrace_interaction/data/2023/disfida_new_format.score +++ b/mathrace_interaction/data/2023/disfida_new_format.score @@ -1,7 +1,6 @@ # http://disfida.it/2023/moviola/index_alt.php [531, -# il risultato ufficiale è 725, dato da 715 + 10 di bonus per errore di immissione, che non consideriamo in turing: -715, +725, 535, 542, 689, diff --git a/mathrace_interaction/mathrace_interaction/filter/reorder_lists_in_imported_turing.py b/mathrace_interaction/mathrace_interaction/filter/reorder_lists_in_imported_turing.py index d7e77ed..149ef72 100644 --- a/mathrace_interaction/mathrace_interaction/filter/reorder_lists_in_imported_turing.py +++ b/mathrace_interaction/mathrace_interaction/filter/reorder_lists_in_imported_turing.py @@ -26,7 +26,7 @@ def reorder_lists_in_imported_turing(imported_dict: TuringDict) -> None: The turing dictionary representing the race, imported from a mathrace journal. """ imported_dict["eventi"] = list(sorted(imported_dict["eventi"], key=lambda e: ( - e["orario"], e["subclass"], e["squadra_id"], e["problema"]))) + e["orario"], e["subclass"], e["squadra_id"], e["problema"] if "problema" in e else None))) imported_dict["soluzioni"] = list(sorted(imported_dict["soluzioni"], key=lambda e: e["problema"])) imported_dict["squadre"] = list(sorted(imported_dict["squadre"], key=lambda e: e["num"])) diff --git a/mathrace_interaction/mathrace_interaction/filter/strip_comments_and_unhandled_events_from_journal.py b/mathrace_interaction/mathrace_interaction/filter/strip_comments_and_unhandled_events_from_journal.py index 6631648..2337ad8 100644 --- a/mathrace_interaction/mathrace_interaction/filter/strip_comments_and_unhandled_events_from_journal.py +++ b/mathrace_interaction/mathrace_interaction/filter/strip_comments_and_unhandled_events_from_journal.py @@ -33,8 +33,8 @@ def strip_comments_and_unhandled_events_from_journal(journal_stream: typing.Text def _is_handled_event(timestamp: str, event_type: str, journal_reader_class: type[AbstractJournalReader]) -> bool: """Determine if a line is associated to an handled event.""" blacklist = [ - journal_reader_class.JOLLY_TIMEOUT, journal_reader_class.TIMER_UPDATE, journal_reader_class.MANUAL_BONUS, - journal_reader_class.RACE_SUSPENDED, journal_reader_class.RACE_RESUMED, + journal_reader_class.JOLLY_TIMEOUT, journal_reader_class.TIMER_UPDATE, + journal_reader_class.RACE_SUSPENDED, journal_reader_class.RACE_RESUMED ] if hasattr(journal_reader_class, "TIMER_UPDATE_OTHER_TIMER"): blacklist.append(journal_reader_class.TIMER_UPDATE_OTHER_TIMER) diff --git a/mathrace_interaction/mathrace_interaction/journal_reader.py b/mathrace_interaction/mathrace_interaction/journal_reader.py index 7715de4..46fca17 100644 --- a/mathrace_interaction/mathrace_interaction/journal_reader.py +++ b/mathrace_interaction/mathrace_interaction/journal_reader.py @@ -275,8 +275,7 @@ def _initialize_teams_definition_with_default_definition(self, turing_dict: Turi def _read_race_events_section(self, turing_dict: TuringDict) -> None: """Read all race events.""" - # Allocate a mathrace only storage for manual corrections and timestamp offset - turing_dict["mathrace_only"]["manual_bonuses"] = list() + # Allocate a mathrace only storage for timestamp offset turing_dict["mathrace_only"]["timestamp_offset"] = "" # Process the remaining race events until the race end one turing_dict["eventi"] = list() @@ -400,10 +399,19 @@ def _process_race_end_event(self, timestamp_str: str, event_content: str, turing def _process_manual_bonus_event(self, timestamp_str: str, event_content: str, turing_dict: TuringDict) -> None: """Process a manual bonus event.""" - event_datetime = self._convert_timestamp_to_datetime( - timestamp_str, self.strict_timestamp_race_events, turing_dict) - turing_dict["mathrace_only"]["manual_bonuses"].append({ - "orario": event_datetime.isoformat(), "motivazione": event_content}) + # Allow manual bonus to be assigned even before the offset is computed, since setting it with + # a slightly wrong timestamp does not affect the overall score of the race + event_datetime = self._convert_timestamp_to_datetime(timestamp_str, False, turing_dict) + # Process the event content + team_id, bonus_points, _ = event_content.split(" ", maxsplit=2) + if int(team_id) <= 0: + raise RuntimeError(f"Invalid event content {event_content}: invalid team number {team_id}") + # Append to output dictionary. Note that manual bonus events do not have a mathrace event ID, + # hence it is not stored here. + turing_dict["eventi"].append({ + "subclass": "Bonus", "orario": event_datetime.isoformat(), + "squadra_id" : int(team_id), "punteggio" : int(bonus_points) + }) def _convert_timestamp_to_datetime( self, timestamp_str: str, strict: bool, turing_dict: TuringDict diff --git a/mathrace_interaction/mathrace_interaction/journal_writer.py b/mathrace_interaction/mathrace_interaction/journal_writer.py index 78a4b06..c1209b4 100644 --- a/mathrace_interaction/mathrace_interaction/journal_writer.py +++ b/mathrace_interaction/mathrace_interaction/journal_writer.py @@ -38,6 +38,7 @@ class JournalWriterR5539(AbstractJournalWriter): JOLLY_SELECTION = JournalReaderR5539.JOLLY_SELECTION ANSWER_SUBMISSION = JournalReaderR5539.ANSWER_SUBMISSION RACE_END = JournalReaderR5539.RACE_END + MANUAL_BONUS = JournalReaderR5539.MANUAL_BONUS def _write_race_definition_section(self, turing_dict: TuringDict) -> None: """Write the race definition section.""" @@ -195,6 +196,8 @@ def _store_race_event_line(self, line: str, turing_dict: TuringDict, event_id: i return self._store_jolly_selection_event(line, turing_dict, event_id) elif event["subclass"] == "Consegna": return self._store_answer_submission_event(line, turing_dict, event_id) + elif event["subclass"] == "Bonus": + return self._store_manual_bonus_event(line, turing_dict, event_id) else: raise RuntimeError(f'Unhandled event type {event["subclass"]}') @@ -231,6 +234,16 @@ def _store_answer_submission_event(self, line: str, turing_dict: TuringDict, eve line = f"{line} PROT:{event_mathrace_id}" return f'{line} squadra {team_id}, quesito {question_id}: {"giusto" if correct_answer else "sbagliato"}' + def _store_manual_bonus_event(self, line: str, turing_dict: TuringDict, event_id: int) -> str: + """Store a manual bonus event.""" + assert line == "" + event = turing_dict["eventi"][event_id] + event_timestamp = self._convert_datetime_to_timestamp(event["orario"], turing_dict) + team_id = event["squadra_id"] + bonus_points = event["punteggio"] + line = f"{event_timestamp} {self.MANUAL_BONUS} {team_id} {bonus_points}" + return f"{line} squadra {team_id} bonus {bonus_points}" + def _store_race_end_event(self, line: str, turing_dict: TuringDict) -> str: """Store the race end event.""" assert line == "" @@ -272,6 +285,7 @@ class JournalWriterR11167(JournalWriterR5539): JOLLY_SELECTION = JournalReaderR11167.JOLLY_SELECTION ANSWER_SUBMISSION = JournalReaderR11167.ANSWER_SUBMISSION RACE_END = JournalReaderR11167.RACE_END + MANUAL_BONUS = JournalReaderR11167.MANUAL_BONUS class JournalWriterR11184(JournalWriterR11167): @@ -287,9 +301,18 @@ class JournalWriterR11184(JournalWriterR11167): The I/O stream is typically generated by open(). """ + def __init__(self, journal_stream: typing.TextIO) -> None: + super().__init__(journal_stream) + self._last_mathrace_event_id = 0 + + def _write_race_events_section(self, turing_dict: TuringDict) -> None: + self._last_mathrace_event_id = 0 + super()._write_race_events_section(turing_dict) + def _determine_mathrace_event_id(self, event_id: int) -> str: - """Set mathrace event ID equal to turing's one.""" - return str(event_id + 1) + """Increment the last event ID in mathrace and return the current ID.""" + self._last_mathrace_event_id += 1 + return str(self._last_mathrace_event_id) class JournalWriterR11189(JournalWriterR11184): diff --git a/mathrace_interaction/mathrace_interaction/live_journal_to_live_turing.py b/mathrace_interaction/mathrace_interaction/live_journal_to_live_turing.py index 35617f6..ff69f7f 100644 --- a/mathrace_interaction/mathrace_interaction/live_journal_to_live_turing.py +++ b/mathrace_interaction/mathrace_interaction/live_journal_to_live_turing.py @@ -37,7 +37,7 @@ def live_journal_to_live_turing( open_input_file A function that opens the input file, and returns a stream. turing_models - The python module containing the turing models Gara, Consegna and Jolly. + The python module containing the turing models Gara, Consegna, Jolly and Bonus. turing_race_id The ID of the turing race to follow. sleep @@ -53,6 +53,7 @@ def live_journal_to_live_turing( Squadra = getattr(turing_models, "Squadra") # noqa: N806 Consegna = getattr(turing_models, "Consegna") # noqa: N806 Jolly = getattr(turing_models, "Jolly") # noqa: N806 + Bonus = getattr(turing_models, "Bonus") # noqa: N806 # Get the turing race from its ID turing_race = Gara.objects.get(pk=turing_race_id) @@ -152,11 +153,13 @@ def live_journal_to_live_turing( del event_dict_copy["squadra_id"] # Create an object of the event subclass event_subclass = event_dict_copy.pop("subclass") - assert event_subclass in ("Consegna", "Jolly"), f"Invalid event subclass {event_subclass}" + assert event_subclass in ("Consegna", "Jolly", "Bonus"), f"Invalid event subclass {event_subclass}" if event_subclass == "Consegna": event_obj = Consegna(gara=turing_race, **event_dict_copy) elif event_subclass == "Jolly": event_obj = Jolly(gara=turing_race, **event_dict_copy) + elif event_subclass == "Bonus": + event_obj = Bonus(gara=turing_race, **event_dict_copy) event_obj.save() # Django requires to explicitly set the datetime field after saving, see Gara.create_from_dict event_obj.orario = event_dict_copy["orario"] diff --git a/mathrace_interaction/mathrace_interaction/test/live_conversion_tester.py b/mathrace_interaction/mathrace_interaction/test/live_conversion_tester.py index caa3666..6c38c53 100644 --- a/mathrace_interaction/mathrace_interaction/test/live_conversion_tester.py +++ b/mathrace_interaction/mathrace_interaction/test/live_conversion_tester.py @@ -39,7 +39,7 @@ class LiveConversionTester(abc.ABC): num_reads Maximum number of reads from the live journal. turing_models - The python module containing the turing models Gara, Consegna and Jolly. + The python module containing the turing models Gara, Consegna, Jolly and Bonus. Attributes ---------- @@ -54,7 +54,7 @@ class LiveConversionTester(abc.ABC): _live_journal LiveJournal instance built from the input arguments journal_stream and num_reads. _turing_models - The python module containing the turing models Gara, Consegna and Jolly, provided as input. + The python module containing the turing models Gara, Consegna, Jolly and Bonus, provided as input. _time_counter Time counter as in LiveJournal """ @@ -216,6 +216,7 @@ def _update_turing_and_termination_condition(self, time_counter: int) -> bool: Squadra = getattr(self._turing_models, "Squadra") # noqa: N806 Consegna = getattr(self._turing_models, "Consegna") # noqa: N806 Jolly = getattr(self._turing_models, "Jolly") # noqa: N806 + Bonus = getattr(self._turing_models, "Bonus") # noqa: N806 assert self._turing_race_id >= 0 turing_race = Gara.objects.get(pk=self._turing_race_id) for event_dict in turing_dict["eventi"]: @@ -236,11 +237,13 @@ def _update_turing_and_termination_condition(self, time_counter: int) -> bool: del event_dict_copy["mathrace_id"] # Create an object of the event subclass event_subclass = event_dict_copy.pop("subclass") - assert event_subclass in ("Consegna", "Jolly"), f"Invalid event subclass {event_subclass}" + assert event_subclass in ("Consegna", "Jolly", "Bonus"), f"Invalid event subclass {event_subclass}" if event_subclass == "Consegna": event_obj = Consegna(gara=turing_race, **event_dict_copy) elif event_subclass == "Jolly": event_obj = Jolly(gara=turing_race, **event_dict_copy) + elif event_subclass == "Bonus": + event_obj = Bonus(gara=turing_race, **event_dict_copy) event_obj.save() # Django requires to explicitly set the datetime field after saving, see Gara.create_from_dict event_obj.orario = event_dict_copy["orario"] diff --git a/mathrace_interaction/mathrace_interaction/test/mock_models.py b/mathrace_interaction/mathrace_interaction/test/mock_models.py index 5b9f222..879ce8f 100644 --- a/mathrace_interaction/mathrace_interaction/test/mock_models.py +++ b/mathrace_interaction/mathrace_interaction/test/mock_models.py @@ -88,8 +88,7 @@ def __init__(self, **kwargs: typing.Any) -> None: # noqa: ANN401 self.gara: Gara | None = None self.orario: datetime.datetime | None = None self.squadra: Squadra | None = None - self.problema: int | None = None - for key in ("gara", "squadra", "problema"): + for key in ("gara", "squadra"): if key in kwargs: setattr(self, key, kwargs[key]) assert "squadra_id" not in kwargs @@ -123,9 +122,11 @@ class Consegna(Evento): def __init__(self, **kwargs: typing.Any) -> None: # noqa: ANN401 super().__init__(**kwargs) + self.problema: int | None = None self.risposta: int | None = None - if "risposta" in kwargs: - setattr(self, "risposta", kwargs["risposta"]) + for key in ("problema", "risposta"): + if key in kwargs: + setattr(self, key, kwargs[key]) def to_dict(self) -> TuringDict: """Convert to a dictionary.""" @@ -143,6 +144,12 @@ def to_dict(self) -> TuringDict: class Jolly(Evento): """A mock turing Jolly class.""" + def __init__(self, **kwargs: typing.Any) -> None: # noqa: ANN401 + super().__init__(**kwargs) + self.problema: int | None = None + if "problema" in kwargs: + setattr(self, "problema", kwargs["problema"]) + def to_dict(self) -> TuringDict: """Convert to a dictionary.""" for key in ("orario", "squadra", "problema"): @@ -155,6 +162,27 @@ def to_dict(self) -> TuringDict: } +class Bonus(Evento): + """A mock turing Bonus class.""" + + def __init__(self, **kwargs: typing.Any) -> None: # noqa: ANN401 + super().__init__(**kwargs) + self.punteggio: int | None = None + if "punteggio" in kwargs: + setattr(self, "punteggio", kwargs["punteggio"]) + + def to_dict(self) -> TuringDict: + """Convert to a dictionary.""" + for key in ("orario", "squadra", "punteggio"): + assert getattr(self, key) is not None, f"{key} is still set to None" + return { + "subclass": "Bonus", + "orario": self.orario.isoformat(), # type: ignore[union-attr] + "punteggio": self.punteggio, + "squadra_id": self.squadra.num # type: ignore[union-attr] + } + + class GaraObjects(list["Gara"]): """A mock list of objects returned by Gara.objects.""" @@ -259,6 +287,8 @@ def create_from_dict(cls, data: TuringDict) -> typing.Self: e_class = Consegna elif e_data["subclass"] == "Jolly": e_class = Jolly + elif e_data["subclass"] == "Bonus": + e_class = Bonus else: # pragma: no cover raise RuntimeError("Invalid event") diff --git a/mathrace_interaction/tests/functional/test_journal_reader_writer_functional.py b/mathrace_interaction/tests/functional/test_journal_reader_writer_functional.py index 0b7157d..121aa28 100644 --- a/mathrace_interaction/tests/functional/test_journal_reader_writer_functional.py +++ b/mathrace_interaction/tests/functional/test_journal_reader_writer_functional.py @@ -68,6 +68,21 @@ def strip_protocol_numbers(journal_content: str) -> str: line_before_prot, line_after_prot = line.split("PROT:") _, line_after_prot = line_after_prot.split("squadra") output_lines.append(f"{line_before_prot} squadra {line_after_prot}") + else: + output_lines.append(line) + return "\n".join(output_lines) + + +def strip_manual_bonus_reason(journal_content: str) -> str: + """Strip reason explaining why manual bonus was added.""" + input_lines = [line for line in journal_content.split("\n")] + output_lines = [] + for line in input_lines: + if "motivazione:" in line: + line_before_reason, _ = line.split(" motivazione:") + output_lines.append(line_before_reason) + else: + output_lines.append(line) return "\n".join(output_lines) @@ -78,6 +93,9 @@ def same_version_comparison(journal: typing.TextIO, journal_name: str, exported_ # different conventions on setting the grace period for answer submission after race end stripped_journal = strip_race_end_line(stripped_journal) exported_journal = strip_race_end_line(exported_journal) + # We do not store the reason for manual bonus, hence it must be removed from the input journal in order + # to be able to carry out a comparison + stripped_journal = strip_manual_bonus_reason(stripped_journal) # Some journals have an expected timestamp offset due to the TIMER_UPDATE not happening on the minute if journal_name == "2013/disfida.journal": timer_offset = 24 @@ -114,7 +132,8 @@ def same_version_comparison(journal: typing.TextIO, journal_name: str, exported_ exported_journal = strip_race_setup_code_lines(exported_journal, "004") exported_journal = strip_race_setup_code_lines(exported_journal, "005") elif journal_name in ( - "2020/disfida.journal", "2022/cesenatico_finale.journal", "2022/cesenatico_finale_femminile.journal", + "2020/disfida.journal", "2022/cesenatico_finale.journal", + "2022/cesenatico_finale_femminile.journal", "2022/cesenatico_pubblico.journal", "2022/cesenatico_semifinale_A.journal", "2022/cesenatico_semifinale_B.journal", "2022/cesenatico_semifinale_C.journal", "2022/cesenatico_semifinale_D.journal", "2022/qualificazione_arezzo_cagliari_taranto_trento.journal", @@ -135,14 +154,16 @@ def same_version_comparison(journal: typing.TextIO, journal_name: str, exported_ stripped_journal = strip_race_setup_code_lines(stripped_journal, "003") exported_journal = strip_race_setup_code_lines(exported_journal, "002") if journal_name not in ( - "2022/cesenatico_finale.journal", "2023/qualificazione_femminile_1.journal", - "2023/qualificazione_femminile_2.journal", "2023/qualificazione_femminile_3.journal" + "2022/cesenatico_finale.journal", "2022/cesenatico_pubblico.journal", + "2023/qualificazione_femminile_1.journal", "2023/qualificazione_femminile_2.journal", + "2023/qualificazione_femminile_3.journal" ): exported_journal = strip_race_setup_code_lines(exported_journal, "005") # Some journals report in a slightly different format the bonus and superbonus entries. # This typically happens when setting a large superbonus cardinality and adding zeros at the end if journal_name in ( - "2020/disfida.journal", "2022/cesenatico_finale.journal", "2022/cesenatico_finale_femminile.journal", + "2020/disfida.journal", "2022/cesenatico_finale.journal", + "2022/cesenatico_finale_femminile.journal", "2022/cesenatico_pubblico.journal", "2022/cesenatico_semifinale_A.journal", "2022/cesenatico_semifinale_B.journal", "2022/cesenatico_semifinale_C.journal", "2022/cesenatico_semifinale_D.journal", "2022/qualificazione_arezzo_cagliari_taranto_trento.journal", diff --git a/mathrace_interaction/tests/unit/conftest.py b/mathrace_interaction/tests/unit/conftest.py index 76b97f1..7657266 100644 --- a/mathrace_interaction/tests/unit/conftest.py +++ b/mathrace_interaction/tests/unit/conftest.py @@ -45,6 +45,7 @@ 450 011 3 4 0 squadra 3, quesito 4: sbagliato 480 022 aggiorna punteggio esercizi, orologio: 8 510 011 8 2 0 squadra 8, quesito 2: sbagliato +520 091 7 43 squadra 7 bonus 43 540 022 aggiorna punteggio esercizi, orologio: 9 570 011 9 3 1 squadra 9, quesito 3: giusto 600 022 aggiorna punteggio esercizi, orologio: 10 @@ -80,6 +81,7 @@ 450 110 3 4 0 squadra 3, quesito 4: sbagliato 480 101 aggiorna punteggio esercizi, orologio: 8 510 110 8 2 0 squadra 8, quesito 2: sbagliato +520 130 7 43 squadra 7 bonus 43 540 101 aggiorna punteggio esercizi, orologio: 9 570 110 9 3 1 squadra 9, quesito 3: giusto 600 101 aggiorna punteggio esercizi, orologio: 10 @@ -115,6 +117,7 @@ 450 110 3 4 0 PROT:7 squadra 3, quesito 4: sbagliato 480 101 aggiorna punteggio esercizi, orologio: 8 510 110 8 2 0 PROT:8 squadra 8, quesito 2: sbagliato +520 130 7 43 squadra 7 bonus 43 540 101 aggiorna punteggio esercizi, orologio: 9 570 110 9 3 1 PROT:9 squadra 9, quesito 3: giusto 600 101 aggiorna punteggio esercizi, orologio: 10 @@ -150,6 +153,7 @@ 450 110 3 4 0 PROT:7 squadra 3, quesito 4: sbagliato 480 101 aggiorna punteggio esercizi, orologio: 8 510 110 8 2 0 PROT:8 squadra 8, quesito 2: sbagliato +520 130 7 43 squadra 7 bonus 43 540 101 aggiorna punteggio esercizi, orologio: 9 570 110 9 3 1 PROT:9 squadra 9, quesito 3: giusto 600 101 aggiorna punteggio esercizi, orologio: 10 @@ -187,6 +191,7 @@ 450 110 3 4 0 PROT:7 squadra 3, quesito 4: sbagliato 480 101 aggiorna punteggio esercizi, orologio: 8 510 110 8 2 0 PROT:8 squadra 8, quesito 2: sbagliato +520 130 7 43 squadra 7 bonus 43 540 101 aggiorna punteggio esercizi, orologio: 9 570 110 9 3 1 PROT:9 squadra 9, quesito 3: giusto 600 101 aggiorna punteggio esercizi, orologio: 10 @@ -232,6 +237,7 @@ 450 110 3 4 0 PROT:7 squadra 3, quesito 4: sbagliato 480 101 aggiorna punteggio esercizi, orologio: 8 510 110 8 2 0 PROT:8 squadra 8, quesito 2: sbagliato +520 130 7 43 squadra 7 bonus 43 540 101 aggiorna punteggio esercizi, orologio: 9 570 110 9 3 1 PROT:9 squadra 9, quesito 3: giusto 600 101 aggiorna punteggio esercizi, orologio: 10 @@ -277,6 +283,7 @@ 450 110 3 4 0 PROT:7 squadra 3, quesito 4: sbagliato 480 101 aggiorna punteggio esercizi, orologio: 8 510 110 8 2 0 PROT:8 squadra 8, quesito 2: sbagliato +520 130 7 43 squadra 7 bonus 43 540 101 aggiorna punteggio esercizi, orologio: 9 570 110 9 3 1 PROT:9 squadra 9, quesito 3: giusto 600 101 aggiorna punteggio esercizi, orologio: 10 @@ -324,6 +331,7 @@ 450 110 3 4 0 PROT:7 squadra 3, quesito 4: sbagliato 480 101 aggiorna punteggio esercizi, orologio: 8 510 110 8 2 0 PROT:8 squadra 8, quesito 2: sbagliato +520 130 7 43 squadra 7 bonus 43 540 101 aggiorna punteggio esercizi, orologio: 9 570 110 9 3 1 PROT:9 squadra 9, quesito 3: giusto 600 101 aggiorna punteggio esercizi, orologio: 10 @@ -371,6 +379,7 @@ 00:07:30.000 110 3 4 0 PROT:7 squadra 3, quesito 4: sbagliato 00:08:00.000 101 aggiorna punteggio esercizi, orologio: 8 00:08:30.000 110 8 2 0 PROT:8 squadra 8, quesito 2: sbagliato +00:08:40.000 130 7 43 squadra 7 bonus 43 00:09:00.000 101 aggiorna punteggio esercizi, orologio: 9 00:09:30.000 110 9 3 1 PROT:9 squadra 9, quesito 3: giusto 00:10:00.000 101 aggiorna punteggio esercizi, orologio: 10 @@ -418,6 +427,7 @@ 00:07:30.000 110 3 4 0 PROT:7 squadra 3, quesito 4: sbagliato 00:08:00.000 101 aggiorna punteggio esercizi, orologio: 8 00:08:30.000 110 8 2 0 PROT:8 squadra 8, quesito 2: sbagliato +00:08:40.000 130 7 43 squadra 7 bonus 43 00:09:00.000 101 aggiorna punteggio esercizi, orologio: 9 00:09:30.000 110 9 3 1 PROT:9 squadra 9, quesito 3: giusto 00:10:00.000 101 aggiorna punteggio esercizi, orologio: 10 @@ -524,6 +534,12 @@ def turing_dict(race_name: str, race_date: datetime.datetime) -> mathrace_intera "problema": 2, "risposta": 0 }, + { + "subclass": "Bonus", + "orario": (race_date + datetime.timedelta(minutes=8, seconds=40)).isoformat(), + "squadra_id": 7, + "punteggio": 43 + }, { "subclass": "Consegna", "orario": (race_date + datetime.timedelta(minutes=9, seconds=30)).isoformat(), diff --git a/mathrace_interaction/tests/unit/filter/test_live_journal.py b/mathrace_interaction/tests/unit/filter/test_live_journal.py index af72715..cf6844c 100644 --- a/mathrace_interaction/tests/unit/filter/test_live_journal.py +++ b/mathrace_interaction/tests/unit/filter/test_live_journal.py @@ -26,7 +26,7 @@ def test_live_journal_one_new_event_per_read(journal: io.StringIO) -> None: num_race_setup_lines = sum([line.startswith("---") for line in stripped_journal.split("\n")]) num_race_setup_lines -= 1 # discard the line with setup code 999 num_race_events = sum([not line.startswith("---") for line in stripped_journal.split("\n")]) - assert num_race_events == 11 + assert num_race_events == 12 # Read the live journal num_reads = num_race_events + 1 # the first read will read a file with no race events live_journal = mathrace_interaction.filter.LiveJournal(io.StringIO(stripped_journal), num_reads) @@ -65,7 +65,7 @@ def test_live_journal_one_new_event_every_two_reads(journal: io.StringIO) -> Non """Test live_journal in the case where the number of reads is equal to twice the number of handled race events.""" # Strip the journal of comments and unhandled events stripped_journal = mathrace_interaction.filter.strip_comments_and_unhandled_events_from_journal(journal) - num_race_events = 11 + num_race_events = 12 live_journal = mathrace_interaction.filter.LiveJournal(io.StringIO(stripped_journal), 2 * num_race_events + 1) # Carry out the initial read outside of the for loop, since it pulls in the entire race setup # rather than race events. @@ -90,11 +90,11 @@ def test_live_journal_one_new_event_every_two_reads(journal: io.StringIO) -> Non assert strip_file_end_line(mathrace_interaction.filter.strip_comments_and_unhandled_events_from_journal( journal)) == previous_read_without_end_line -@pytest.mark.parametrize("max_open_calls", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) +@pytest.mark.parametrize("max_open_calls", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) def test_live_journal_several_new_events_every_read(journal: io.StringIO, max_open_calls: int) -> None: """Test live_journal in the case where the every read introduces several new events.""" stripped_journal = mathrace_interaction.filter.strip_comments_and_unhandled_events_from_journal(journal) - num_race_events = 11 + num_race_events = 12 live_journal = mathrace_interaction.filter.LiveJournal(io.StringIO(stripped_journal), max_open_calls) # Determine the range of expected new events for every read if max_open_calls > 1: diff --git a/mathrace_interaction/tests/unit/filter/test_strip_comments_and_unhandled_events_from_journal.py b/mathrace_interaction/tests/unit/filter/test_strip_comments_and_unhandled_events_from_journal.py index 5eaa737..8cd6066 100644 --- a/mathrace_interaction/tests/unit/filter/test_strip_comments_and_unhandled_events_from_journal.py +++ b/mathrace_interaction/tests/unit/filter/test_strip_comments_and_unhandled_events_from_journal.py @@ -31,7 +31,6 @@ def random_journal(journal: io.StringIO, journal_version: str) -> io.StringIO: "# a comment", f"timestamp {journal_reader_class.JOLLY_TIMEOUT} timeout jolly", f"timestamp {journal_reader_class.TIMER_UPDATE} aggiorna punteggio esercizi, orologio: timestamp", - f"timestamp {journal_reader_class.MANUAL_BONUS} 2 10 squadra 2 bonus 10 motivazione: errore inserimento dati", f"timestamp {journal_reader_class.RACE_SUSPENDED} gara sospesa", f"timestamp {journal_reader_class.RACE_RESUMED} gara ripresa" ] diff --git a/mathrace_interaction/tests/unit/test_journal_reader.py b/mathrace_interaction/tests/unit/test_journal_reader.py index cc2ac09..f3f112f 100644 --- a/mathrace_interaction/tests/unit/test_journal_reader.py +++ b/mathrace_interaction/tests/unit/test_journal_reader.py @@ -303,11 +303,8 @@ def test_journal_reader_race_event_manual_bonus( """) with mathrace_interaction.journal_reader(journal_with_manual_bonus) as journal_stream: dict_with_manual_bonus = journal_stream.read("journal_with_manual_bonus", race_date) - assert len(dict_with_manual_bonus["mathrace_only"]["manual_bonuses"]) == 1 - assert dict_with_manual_bonus["mathrace_only"]["manual_bonuses"][0]["orario"] == ( - race_date + datetime.timedelta(minutes=5)).isoformat() - assert dict_with_manual_bonus["mathrace_only"]["manual_bonuses"][0]["motivazione"] == ( - "2 10 squadra 2 bonus 10 motivazione: errore inserimento dati") + assert len(dict_with_manual_bonus["eventi"]) == 1 + assert dict_with_manual_bonus["eventi"][0]["orario"] == (race_date + datetime.timedelta(minutes=5)).isoformat() @pytest.mark.parametrize( diff --git a/mathrace_interaction/tests/unit/test_journal_writer.py b/mathrace_interaction/tests/unit/test_journal_writer.py index b32d43b..20dd410 100644 --- a/mathrace_interaction/tests/unit/test_journal_writer.py +++ b/mathrace_interaction/tests/unit/test_journal_writer.py @@ -144,4 +144,4 @@ def test_journal_writer_not_started_yet_with_events( mathrace_interaction.journal_writer(exported_journal, journal_version) as journal_stream ): runtime_error_contains( - lambda: journal_stream.write(turing_dict), "Race has not started, yet there are 9 events") + lambda: journal_stream.write(turing_dict), "Race has not started, yet there are 10 events")