diff --git a/src/warchron/controller/app_controller.py b/src/warchron/controller/app_controller.py index 1548839..ac98aad 100644 --- a/src/warchron/controller/app_controller.py +++ b/src/warchron/controller/app_controller.py @@ -357,12 +357,20 @@ class AppController: except RequiresConfirmation as e: reply = QMessageBox.question( self.view, - "Confirm deletion", + "Confirm update", str(e), QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, ) if reply == QMessageBox.StandardButton.Yes: - e.action() + try: + e.action() + except DomainError as inner: + QMessageBox.warning( + self.view, + "Update forbidden", + str(inner), + ) + return else: return self.is_dirty = True diff --git a/src/warchron/controller/round_controller.py b/src/warchron/controller/round_controller.py index ec3ad78..3aa4b36 100644 --- a/src/warchron/controller/round_controller.py +++ b/src/warchron/controller/round_controller.py @@ -47,6 +47,7 @@ class RoundController: self.app = app def _fill_round_details(self, round_id: str) -> None: + # self.app.view.clear_round_page() rnd = self.app.model.get_round(round_id) camp = self.app.model.get_campaign_by_round(round_id) war = self.app.model.get_war_by_round(round_id) @@ -57,125 +58,144 @@ class RoundController: for part in participants: choice = rnd.get_choice(part.id) if not choice: - choice = self.app.model.create_choice( - round_id=rnd.id, participant_id=part.id + try: + choice = self.app.model.create_choice( + round_id=rnd.id, participant_id=part.id + ) + except DomainError as e: + QMessageBox.warning( + self.app.view, + "Create forbidden", + str(e), + ) + else: + priority_name = ( + camp.get_sector_name(choice.priority_sector_id) + if choice.priority_sector_id is not None + else "" ) - priority_name = ( - camp.get_sector_name(choice.priority_sector_id) - if choice.priority_sector_id is not None - else "" - ) - secondary_name = ( - camp.get_sector_name(choice.secondary_sector_id) - if choice.secondary_sector_id is not None - else "" - ) - priority_icon = None - secondary_icon = None - fallback_icon = None - alloc = Pairing.get_round_allocation( - war, - rnd, - part.id, - ) - if alloc.priority != ChoiceStatus.NONE: - priority_icon = QIcon(Icons.get_pixmap(IconName[alloc.priority.name])) - if alloc.secondary != ChoiceStatus.NONE: - secondary_icon = QIcon(Icons.get_pixmap(IconName[alloc.secondary.name])) - if alloc.fallback: - fallback_icon = QIcon(Icons.get_pixmap(IconName.FALLBACK)) - choices_for_display.append( - ChoiceDTO( - id=choice.participant_id, - participant_name=self.app.model.get_participant_name( - part.war_participant_id - ), - priority_sector=priority_name, - secondary_sector=secondary_name, - comment=choice.comment, - priority_icon=priority_icon, - secondary_icon=secondary_icon, - fallback_icon=fallback_icon, + secondary_name = ( + camp.get_sector_name(choice.secondary_sector_id) + if choice.secondary_sector_id is not None + else "" + ) + priority_icon = None + secondary_icon = None + fallback_icon = None + alloc = Pairing.get_round_allocation( + war, + rnd, + part.id, + ) + if alloc.priority != ChoiceStatus.NONE: + priority_icon = QIcon( + Icons.get_pixmap(IconName[alloc.priority.name]) + ) + if alloc.secondary != ChoiceStatus.NONE: + secondary_icon = QIcon( + Icons.get_pixmap(IconName[alloc.secondary.name]) + ) + if alloc.fallback: + fallback_icon = QIcon(Icons.get_pixmap(IconName.FALLBACK)) + choices_for_display.append( + ChoiceDTO( + id=choice.participant_id, + participant_name=self.app.model.get_participant_name( + part.war_participant_id + ), + priority_sector=priority_name, + secondary_sector=secondary_name, + comment=choice.comment, + priority_icon=priority_icon, + secondary_icon=secondary_icon, + fallback_icon=fallback_icon, + ) ) - ) self.app.view.display_round_choices(choices_for_display) battles_for_display: List[BattleDTO] = [] for sect in sectors: battle = rnd.get_battle(sect.id) - if not battle: - battle = self.app.model.create_battle( - round_id=rnd.id, sector_id=sect.id - ) - state_icon = Icons.get(IconName.ONGOING) - if battle.is_finished(): - state_icon = Icons.get(IconName.DONE) - if battle.player_1_id: - camp_part = camp.participants[battle.player_1_id] - player_1_name = self.app.model.get_participant_name( - camp_part.war_participant_id - ) - p1_id = battle.player_1_id - else: - player_1_name = "" - if battle.player_2_id: - camp_part = camp.participants[battle.player_2_id] - player_2_name = self.app.model.get_participant_name( - camp_part.war_participant_id - ) - p2_id = battle.player_2_id - else: - player_2_name = "" - if battle.winner_id: - camp_part = camp.participants[battle.winner_id] - winner_name = self.app.model.get_participant_name( - camp_part.war_participant_id - ) - else: - winner_name = "" - p1_icon = None - p2_icon = None - # TODO use uniform draw/tie icon logic with choice, war, campaign... - if battle.is_draw(): - p1_icon = Icons.get(IconName.DRAW) - p2_icon = Icons.get(IconName.DRAW) - context = TieContext( - ContextType.BATTLE, - battle.sector_id, - [p1_id, p2_id], - ) - if TieResolver.was_tie_broken_by_tokens(war, context): - effective_winner = ResultChecker.get_effective_winner_id( - war, ContextType.BATTLE, battle.sector_id, None + try: + battle = self.app.model.create_battle( + round_id=rnd.id, sector_id=sect.id + ) + except DomainError as e: + QMessageBox.warning( + self.app.view, + "Create forbidden", + str(e), + ) + else: + state_icon = Icons.get(IconName.ONGOING) + if battle.is_finished(): + state_icon = Icons.get(IconName.DONE) + if battle.player_1_id: + camp_part = camp.participants[battle.player_1_id] + player_1_name = self.app.model.get_participant_name( + camp_part.war_participant_id + ) + p1_id = battle.player_1_id + else: + player_1_name = "" + if battle.player_2_id: + camp_part = camp.participants[battle.player_2_id] + player_2_name = self.app.model.get_participant_name( + camp_part.war_participant_id + ) + p2_id = battle.player_2_id + else: + player_2_name = "" + if battle.winner_id: + camp_part = camp.participants[battle.winner_id] + winner_name = self.app.model.get_participant_name( + camp_part.war_participant_id + ) + else: + winner_name = "" + p1_icon = None + p2_icon = None + # TODO use uniform draw/tie icon logic with choice, war, campaign... + if battle.is_draw(): + p1_icon = Icons.get(IconName.DRAW) + p2_icon = Icons.get(IconName.DRAW) + context = TieContext( + ContextType.BATTLE, + battle.sector_id, + [p1_id, p2_id], + ) + if TieResolver.was_tie_broken_by_tokens(war, context): + effective_winner = ResultChecker.get_effective_winner_id( + war, ContextType.BATTLE, battle.sector_id, None + ) + p1_war = None + if battle.player_1_id is not None: + p1_war = camp.campaign_to_war_part_id(battle.player_1_id) + pixmap = Icons.get_pixmap(IconName.TIEBREAK_TOKEN) + if effective_winner == p1_war: + p1_icon = QIcon(pixmap) + else: + p2_icon = QIcon(pixmap) + elif battle.winner_id: + if battle.winner_id == battle.player_1_id: + p1_icon = Icons.get(IconName.WIN) + elif battle.winner_id == battle.player_2_id: + p2_icon = Icons.get(IconName.WIN) + battles_for_display.append( + BattleDTO( + id=battle.sector_id, + sector_name=camp.get_sector_name(battle.sector_id), + player_1=player_1_name, + player_2=player_2_name, + winner=winner_name, + score=battle.score, + victory_condition=battle.victory_condition, + comment=battle.comment, + state_icon=state_icon, + player1_icon=p1_icon, + player2_icon=p2_icon, ) - p1_war = None - if battle.player_1_id is not None: - p1_war = camp.campaign_to_war_part_id(battle.player_1_id) - pixmap = Icons.get_pixmap(IconName.TIEBREAK_TOKEN) - if effective_winner == p1_war: - p1_icon = QIcon(pixmap) - else: - p2_icon = QIcon(pixmap) - elif battle.winner_id: - if battle.winner_id == battle.player_1_id: - p1_icon = Icons.get(IconName.WIN) - elif battle.winner_id == battle.player_2_id: - p2_icon = Icons.get(IconName.WIN) - battles_for_display.append( - BattleDTO( - id=battle.sector_id, - sector_name=camp.get_sector_name(battle.sector_id), - player_1=player_1_name, - player_2=player_2_name, - winner=winner_name, - score=battle.score, - victory_condition=battle.victory_condition, - comment=battle.comment, - state_icon=state_icon, - player1_icon=p1_icon, - player2_icon=p2_icon, ) - ) self.app.view.display_round_battles(battles_for_display) self.app.view.endRoundBtn.setEnabled(not rnd.is_over) diff --git a/src/warchron/model/campaign.py b/src/warchron/model/campaign.py index 15fd545..ee2a136 100644 --- a/src/warchron/model/campaign.py +++ b/src/warchron/model/campaign.py @@ -144,8 +144,8 @@ class Campaign: def cleanup() -> None: for rnd in rounds_blocking: - rnd.clear_participant_references(participant_id) rnd.remove_choice(participant_id) + rnd.clear_participant_references(participant_id) del self.participants[participant_id] rounds_str = ", ".join( @@ -263,8 +263,8 @@ class Campaign: def cleanup_and_update() -> None: for rnd in affected_rounds: - rnd.clear_sector_references(sector_id) rnd.remove_battle(sector_id) + rnd.clear_sector_references(sector_id) apply_update() rounds_str = ", ".join( @@ -299,8 +299,8 @@ class Campaign: def cleanup() -> None: for rnd in rounds_blocking: - rnd.clear_sector_references(sector_id) rnd.remove_battle(sector_id) + rnd.clear_sector_references(sector_id) del self.sectors[sector_id] rounds_str = ", ".join( diff --git a/src/warchron/model/round.py b/src/warchron/model/round.py index f0db6e2..86b279d 100644 --- a/src/warchron/model/round.py +++ b/src/warchron/model/round.py @@ -78,7 +78,6 @@ class Round: def create_choice(self, participant_id: str) -> Choice: if self.is_over: - # TODO catch me if you can raise ForbiddenOperation("Can't create choice in a closed round.") if participant_id not in self.choices: choice = Choice( @@ -97,13 +96,16 @@ class Round: comment: str | None, ) -> None: if self.is_over: - # TODO catch me if you can raise ForbiddenOperation("Can't update choice in a closed round.") - if self.has_battle_with_participant(participant_id): - # TODO catch me if you can (inner) - raise ForbiddenOperation("Can't update choice already assigned to battle.") choice = self.get_choice(participant_id) if choice: + if self.has_battle_with_participant(participant_id) and ( + priority_sector_id != choice.priority_sector_id + or secondary_sector_id != choice.secondary_sector_id + ): + raise ForbiddenOperation( + "Can't update choice already assigned to battle." + ) choice.set_priority(priority_sector_id) choice.set_secondary(secondary_sector_id) choice.set_comment(comment) @@ -124,10 +126,8 @@ class Round: if participant_id not in self.choices: return if self.is_over: - # TODO catch me if you can (inner) raise ForbiddenOperation("Can't remove choice in a closed round.") if self.has_battle_with_participant(participant_id): - # TODO catch me if you can (inner) raise ForbiddenOperation("Can't remove choice already assigned to battle.") self.war.revert_choice_ties( self.id, @@ -176,7 +176,6 @@ class Round: def create_battle(self, sector_id: str) -> Battle: if self.is_over: - # TODO catch me if you can raise ForbiddenOperation("Can't create battle in a closed round.") if sector_id not in self.battles: battle = Battle(sector_id=sector_id, player_1_id=None, player_2_id=None) @@ -196,7 +195,6 @@ class Round: from warchron.model.pairing import Pairing if self.is_over: - # TODO catch me if you can raise ForbiddenOperation("Can't update battle in a closed round.") bat = self.get_battle(sector_id) if not bat: @@ -274,11 +272,9 @@ class Round: if sector_id not in self.battles: return if self.is_over: - # TODO catch me if you can raise ForbiddenOperation("Can't remove battle in a closed round.") bat = self.battles[sector_id] if bat and bat.is_finished(): - # TODO catch me if you can raise ForbiddenOperation("Can't remove finished battle.") self.war.revert_battle_ties(self.id, sector_id=sector_id) del self.battles[sector_id] diff --git a/src/warchron/view/view.py b/src/warchron/view/view.py index 80ac522..53fab4b 100644 --- a/src/warchron/view/view.py +++ b/src/warchron/view/view.py @@ -599,6 +599,12 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow): def show_round_details(self, *, index: int | None) -> None: self.roundNb.setText(f"Round {index}") + def clear_round_page(self) -> None: + choices_table = self.choicesTable + choices_table.clearContents() + battles_table = self.battlesTable + battles_table.clearContents() + def display_round_choices(self, participants: List[ChoiceDTO]) -> None: table = self.choicesTable table.setSortingEnabled(False)