manage deleted refered player, participant, objective, sector

This commit is contained in:
Maxime Réaux 2026-02-05 16:17:18 +01:00
parent 7afbb5ea1d
commit 7fbdacf97c
6 changed files with 322 additions and 81 deletions

View file

@ -4,6 +4,11 @@ from pathlib import Path
from PyQt6.QtWidgets import QMessageBox, QDialog from PyQt6.QtWidgets import QMessageBox, QDialog
from warchron.model.model import Model from warchron.model.model import Model
from warchron.model.exception import (
DeletionForbidden,
DeletionRequiresConfirmation,
UpdateRequiresConfirmation,
)
from warchron.view.view import View from warchron.view.view import View
from warchron.constants import ItemType, RefreshScope from warchron.constants import ItemType, RefreshScope
from warchron.controller.dtos import ( from warchron.controller.dtos import (
@ -362,38 +367,49 @@ class Controller:
self.view.select_tree_item(item_type=item_type, item_id=item_id) self.view.select_tree_item(item_type=item_type, item_id=item_id)
def edit_item(self, item_type: str, item_id: str) -> None: def edit_item(self, item_type: str, item_id: str) -> None:
if item_type == ItemType.PLAYER: try:
self.edit_player(item_id) if item_type == ItemType.PLAYER:
self.refresh(RefreshScope.PLAYERS_LIST) self.edit_player(item_id)
elif item_type == ItemType.WAR: self.refresh(RefreshScope.PLAYERS_LIST)
self.edit_war(item_id) elif item_type == ItemType.WAR:
self.refresh_and_select( self.edit_war(item_id)
RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=item_id self.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=item_id
)
elif item_type == ItemType.CAMPAIGN:
self.edit_campaign(item_id)
self.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=item_id
)
elif item_type == ItemType.OBJECTIVE:
self.edit_objective(item_id)
self.refresh(RefreshScope.WAR_DETAILS)
elif item_type == ItemType.WAR_PARTICIPANT:
self.edit_war_participant(item_id)
self.refresh(RefreshScope.WAR_DETAILS)
elif item_type == ItemType.SECTOR:
self.edit_sector(item_id)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
elif item_type == ItemType.CAMPAIGN_PARTICIPANT:
self.edit_campaign_participant(item_id)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
elif item_type == ItemType.CHOICE:
self.edit_round_choice(item_id)
self.refresh(RefreshScope.ROUND_DETAILS)
elif item_type == ItemType.BATTLE:
self.edit_round_battle(item_id)
self.refresh(RefreshScope.ROUND_DETAILS)
self.is_dirty = True
except UpdateRequiresConfirmation as e:
reply = QMessageBox.question(
self.view,
"Confirm update",
e.message,
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
) )
elif item_type == ItemType.CAMPAIGN: if reply == QMessageBox.StandardButton.Yes:
self.edit_campaign(item_id) e.apply_update()
self.refresh_and_select( self.refresh(RefreshScope.CAMPAIGN_DETAILS)
RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=item_id
)
elif item_type == ItemType.OBJECTIVE:
self.edit_objective(item_id)
self.refresh(RefreshScope.WAR_DETAILS)
elif item_type == ItemType.WAR_PARTICIPANT:
self.edit_war_participant(item_id)
self.refresh(RefreshScope.WAR_DETAILS)
elif item_type == ItemType.SECTOR:
self.edit_sector(item_id)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
elif item_type == ItemType.CAMPAIGN_PARTICIPANT:
self.edit_campaign_participant(item_id)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
elif item_type == ItemType.CHOICE:
self.edit_round_choice(item_id)
self.refresh(RefreshScope.ROUND_DETAILS)
elif item_type == ItemType.BATTLE:
self.edit_round_battle(item_id)
self.refresh(RefreshScope.ROUND_DETAILS)
self.is_dirty = True
def delete_item(self, item_type: str, item_id: str) -> None: def delete_item(self, item_type: str, item_id: str) -> None:
reply = QMessageBox.question( reply = QMessageBox.question(
@ -404,39 +420,56 @@ class Controller:
) )
if reply != QMessageBox.StandardButton.Yes: if reply != QMessageBox.StandardButton.Yes:
return return
if item_type == ItemType.PLAYER: try:
self.model.remove_player(item_id) if item_type == ItemType.PLAYER:
self.refresh(RefreshScope.PLAYERS_LIST) self.model.remove_player(item_id)
elif item_type == ItemType.WAR: self.refresh(RefreshScope.PLAYERS_LIST)
self.model.remove_war(item_id) elif item_type == ItemType.WAR:
self.refresh(RefreshScope.WARS_TREE) self.model.remove_war(item_id)
elif item_type == ItemType.CAMPAIGN: self.refresh(RefreshScope.WARS_TREE)
war = self.model.get_war_by_campaign(item_id) elif item_type == ItemType.CAMPAIGN:
war_id = war.id war = self.model.get_war_by_campaign(item_id)
self.model.remove_campaign(item_id) war_id = war.id
self.refresh_and_select( self.model.remove_campaign(item_id)
RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war_id self.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.WAR, item_id=war_id
)
elif item_type == ItemType.OBJECTIVE:
self.model.remove_objective(item_id)
self.refresh(RefreshScope.WAR_DETAILS)
elif item_type == ItemType.WAR_PARTICIPANT:
self.model.remove_war_participant(item_id)
self.refresh(RefreshScope.WAR_DETAILS)
elif item_type == ItemType.SECTOR:
self.model.remove_sector(item_id)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
elif item_type == ItemType.CAMPAIGN_PARTICIPANT:
self.model.remove_campaign_participant(item_id)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
elif item_type == ItemType.ROUND:
camp = self.model.get_campaign_by_round(item_id)
camp_id = camp.id
self.model.remove_round(item_id)
self.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp_id
)
self.is_dirty = True
except DeletionForbidden as e:
QMessageBox.warning(
self.view,
"Deletion forbidden",
e.reason,
) )
elif item_type == ItemType.OBJECTIVE: except DeletionRequiresConfirmation as e:
self.model.remove_objective(item_id) reply = QMessageBox.question(
self.refresh(RefreshScope.WAR_DETAILS) self.view,
elif item_type == ItemType.WAR_PARTICIPANT: "Confirm deletion",
self.model.remove_war_participant(item_id) e.message,
self.refresh(RefreshScope.WAR_DETAILS) QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
elif item_type == ItemType.SECTOR:
self.model.remove_sector(item_id)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
elif item_type == ItemType.CAMPAIGN_PARTICIPANT:
self.model.remove_campaign_participant(item_id)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
elif item_type == ItemType.ROUND:
camp = self.model.get_campaign_by_round(item_id)
camp_id = camp.id
self.model.remove_round(item_id)
self.refresh_and_select(
RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp_id
) )
self.is_dirty = True if reply == QMessageBox.StandardButton.Yes:
e.cleanup_action()
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
# Player methods # Player methods

View file

@ -2,6 +2,10 @@ from __future__ import annotations
from uuid import uuid4 from uuid import uuid4
from typing import Any, Dict, List from typing import Any, Dict, List
from warchron.model.exception import (
DeletionRequiresConfirmation,
UpdateRequiresConfirmation,
)
from warchron.model.campaign_participant import CampaignParticipant from warchron.model.campaign_participant import CampaignParticipant
from warchron.model.sector import Sector from warchron.model.sector import Sector
from warchron.model.round import Round from warchron.model.round import Round
@ -94,12 +98,47 @@ class Campaign:
part.set_theme(theme) part.set_theme(theme)
def remove_campaign_participant(self, participant_id: str) -> None: def remove_campaign_participant(self, participant_id: str) -> None:
# TODO manage choices referring to it rounds_blocking: list[Round] = []
# TODO manage battles referring to it for rnd in self.rounds:
del self.participants[participant_id] if rnd.has_choice_with_participant(
participant_id
) or rnd.has_battle_with_participant(participant_id):
rounds_blocking.append(rnd)
if not rounds_blocking:
del self.participants[participant_id]
return
def cleanup() -> None:
for rnd in rounds_blocking:
rnd.clear_participant_references(participant_id)
rnd.remove_choice(participant_id)
del self.participants[participant_id]
rounds_str = ", ".join(
str(self.get_round_index(rnd.id)) for rnd in rounds_blocking
)
raise DeletionRequiresConfirmation(
message=(
f"This participant is used in round(s): {rounds_str}.\n"
"Related choices and battles will be cleared.\n"
"Do you want to continue?"
),
cleanup_action=cleanup,
)
# Sector methods # Sector methods
def has_sector(self, sector_id: str) -> bool:
return sector_id in self.sectors
def has_sector_with_objective(self, objective_id: str) -> bool:
return any(
sect.major_objective_id == objective_id
or sect.minor_objective_id == objective_id
or sect.influence_objective_id == objective_id
for sect in self.sectors.values()
)
def add_sector( def add_sector(
self, name: str, round_id: str, major_id: str, minor_id: str, influence_id: str self, name: str, round_id: str, major_id: str, minor_id: str, influence_id: str
) -> Sector: ) -> Sector:
@ -118,7 +157,6 @@ class Campaign:
def get_all_sectors(self) -> List[Sector]: def get_all_sectors(self) -> List[Sector]:
return list(self.sectors.values()) return list(self.sectors.values())
# TODO manage choices referring to it (round order!)
def update_sector( def update_sector(
self, self,
sector_id: str, sector_id: str,
@ -130,16 +168,75 @@ class Campaign:
influence_id: str, influence_id: str,
) -> None: ) -> None:
sect = self.get_sector(sector_id) sect = self.get_sector(sector_id)
sect.set_name(name) old_round_id = sect.round_id
sect.set_round(round_id)
sect.set_major(major_id) def apply_update() -> None:
sect.set_minor(minor_id) sect.set_name(name)
sect.set_influence(influence_id) sect.set_round(round_id)
sect.set_major(major_id)
sect.set_minor(minor_id)
sect.set_influence(influence_id)
if old_round_id == round_id:
apply_update()
return
affected_rounds: list[Round] = []
for rnd in self.rounds:
if rnd.id == old_round_id and (
rnd.has_choice_with_sector(sector_id)
or rnd.has_battle_with_sector(sector_id)
):
affected_rounds.append(rnd)
if not affected_rounds:
apply_update()
return
def cleanup_and_update() -> None:
for rnd in affected_rounds:
rnd.clear_sector_references(sector_id)
rnd.remove_battle(sector_id)
apply_update()
rounds_str = ", ".join(
str(self.get_round_index(rnd.id)) for rnd in affected_rounds
)
raise UpdateRequiresConfirmation(
message=(
f"Changing the round of this sector will affect round(s): {rounds_str}."
"\nRelated battles and choices will be cleared.\n"
"Do you want to continue?"
),
apply_update=cleanup_and_update,
)
def remove_sector(self, sector_id: str) -> None: def remove_sector(self, sector_id: str) -> None:
# TODO manage choices referring to it rounds_blocking: list[Round] = []
# TODO manage battles referring to it for rnd in self.rounds:
del self.sectors[sector_id] if rnd.has_battle_with_sector(sector_id) or rnd.has_choice_with_sector(
sector_id
):
rounds_blocking.append(rnd)
if not rounds_blocking:
del self.sectors[sector_id]
return
def cleanup() -> None:
for rnd in rounds_blocking:
rnd.clear_sector_references(sector_id)
rnd.remove_battle(sector_id)
del self.sectors[sector_id]
rounds_str = ", ".join(
str(self.get_round_index(rnd.id)) for rnd in rounds_blocking
)
raise DeletionRequiresConfirmation(
message=(
f"This sector is used in round(s): {rounds_str}.\n"
"Battles and choices using this sector will be cleared.\n"
"Do you want to continue?"
),
cleanup_action=cleanup,
)
def get_sectors_in_round(self, round_id: str) -> List[Sector]: def get_sectors_in_round(self, round_id: str) -> List[Sector]:
sectors = [s for s in self.sectors.values() if s.round_id == round_id] sectors = [s for s in self.sectors.values() if s.round_id == round_id]

View file

@ -0,0 +1,31 @@
from typing import Callable
class DeletionForbidden(Exception):
def __init__(self, reason: str):
self.reason = reason
super().__init__(reason)
class DeletionRequiresConfirmation(Exception):
def __init__(
self,
message: str,
*,
cleanup_action: Callable[[], None],
):
self.message = message
self.cleanup_action = cleanup_action
super().__init__(message)
class UpdateRequiresConfirmation(Exception):
def __init__(
self,
message: str,
*,
apply_update: Callable[[], None],
):
self.message = message
self.apply_update = apply_update
super().__init__(message)

View file

@ -4,6 +4,7 @@ import json
import shutil import shutil
from datetime import datetime from datetime import datetime
from warchron.model.exception import DeletionForbidden
from warchron.model.player import Player from warchron.model.player import Player
from warchron.model.war import War from warchron.model.war import War
from warchron.model.war_participant import WarParticipant from warchron.model.war_participant import WarParticipant
@ -84,7 +85,16 @@ class Model:
return list(self.players.values()) return list(self.players.values())
def remove_player(self, player_id: str) -> None: def remove_player(self, player_id: str) -> None:
# TODO manage war_participants referring to it wars_using_player: List[str] = []
for war in self.wars.values():
if war.has_player(player_id):
wars_using_player.append(war.name)
if wars_using_player:
wars_str = ", ".join(wars_using_player)
raise DeletionForbidden(
f"This player is participating in war(s): {wars_str}.\n"
"Remove it from participants first."
)
del self.players[player_id] del self.players[player_id]
# War methods # War methods
@ -100,6 +110,7 @@ class Model:
def get_war(self, id: str) -> War: def get_war(self, id: str) -> War:
return self.wars[id] return self.wars[id]
# TODO replace multiloops by internal has_* method
def get_war_by_campaign(self, campaign_id: str) -> War: def get_war_by_campaign(self, campaign_id: str) -> War:
for war in self.wars.values(): for war in self.wars.values():
for camp in war.campaigns: for camp in war.campaigns:
@ -107,6 +118,7 @@ class Model:
return war return war
raise KeyError(f"Campaign {campaign_id} not found in any War") raise KeyError(f"Campaign {campaign_id} not found in any War")
# TODO replace multiloops by internal has_* method
def get_war_by_sector(self, sector_id: str) -> War: def get_war_by_sector(self, sector_id: str) -> War:
for war in self.wars.values(): for war in self.wars.values():
for camp in war.campaigns: for camp in war.campaigns:
@ -115,6 +127,7 @@ class Model:
return war return war
raise KeyError(f"Sector {sector_id} not found in any War") raise KeyError(f"Sector {sector_id} not found in any War")
# TODO replace multiloops by internal has_* method
def get_war_by_round(self, round_id: str) -> War: def get_war_by_round(self, round_id: str) -> War:
for war in self.wars.values(): for war in self.wars.values():
for camp in war.campaigns: for camp in war.campaigns:
@ -123,6 +136,7 @@ class Model:
return war return war
raise KeyError(f"Round {round_id} not found in any War") raise KeyError(f"Round {round_id} not found in any War")
# TODO replace multiloops by internal has_* method
def get_war_by_objective(self, objective_id: str) -> War: def get_war_by_objective(self, objective_id: str) -> War:
for war in self.wars.values(): for war in self.wars.values():
for obj in war.objectives.values(): for obj in war.objectives.values():
@ -160,6 +174,7 @@ class Model:
war = self.get_war(war_id) war = self.get_war(war_id)
return war.add_objective(name, description) return war.add_objective(name, description)
# TODO replace multiloops by internal has_* method
def get_objective(self, objective_id: str) -> Objective: def get_objective(self, objective_id: str) -> Objective:
for war in self.wars.values(): for war in self.wars.values():
for obj in war.objectives.values(): for obj in war.objectives.values():
@ -191,6 +206,7 @@ class Model:
war = self.get_war(war_id) war = self.get_war(war_id)
return war.add_war_participant(player_id, faction) return war.add_war_participant(player_id, faction)
# TODO replace multiloops by internal has_* method
def get_war_participant(self, participant_id: str) -> WarParticipant: def get_war_participant(self, participant_id: str) -> WarParticipant:
for war in self.wars.values(): for war in self.wars.values():
for part in war.participants.values(): for part in war.participants.values():
@ -219,6 +235,7 @@ class Model:
war = self.get_war(war_id) war = self.get_war(war_id)
return war.add_campaign(name, month) return war.add_campaign(name, month)
# TODO replace multiloops by internal has_* method
def get_campaign(self, campaign_id: str) -> Campaign: def get_campaign(self, campaign_id: str) -> Campaign:
for war in self.wars.values(): for war in self.wars.values():
for campaign in war.campaigns: for campaign in war.campaigns:
@ -269,6 +286,7 @@ class Model:
camp = self.get_campaign(campaign_id) camp = self.get_campaign(campaign_id)
return camp.add_sector(name, round_id, major_id, minor_id, influence_id) return camp.add_sector(name, round_id, major_id, minor_id, influence_id)
# TODO replace multiloops by internal has_* method
def get_sector(self, sector_id: str) -> Sector: def get_sector(self, sector_id: str) -> Sector:
for war in self.wars.values(): for war in self.wars.values():
for camp in war.campaigns: for camp in war.campaigns:
@ -318,6 +336,7 @@ class Model:
war_part = war.get_war_participant(participant_id) war_part = war.get_war_participant(participant_id)
return self.players[war_part.player_id].name return self.players[war_part.player_id].name
# TODO replace multiloops by internal has_* method
def get_campaign_participant(self, participant_id: str) -> CampaignParticipant: def get_campaign_participant(self, participant_id: str) -> CampaignParticipant:
for war in self.wars.values(): for war in self.wars.values():
for camp in war.campaigns: for camp in war.campaigns:
@ -352,6 +371,7 @@ class Model:
war = self.get_war_by_campaign(campaign_id) war = self.get_war_by_campaign(campaign_id)
return war.add_round(campaign_id) return war.add_round(campaign_id)
# TODO replace multiloops by internal has_* method
def get_round(self, round_id: str) -> Round: def get_round(self, round_id: str) -> Round:
for war in self.wars.values(): for war in self.wars.values():
for camp in war.campaigns: for camp in war.campaigns:

View file

@ -43,6 +43,18 @@ class Round:
def get_choice(self, participant_id: str) -> Choice | None: def get_choice(self, participant_id: str) -> Choice | None:
return self.choices.get(participant_id) return self.choices.get(participant_id)
def has_choice_with_sector(self, sector_id: str) -> bool:
return any(
choice.priority_sector_id == sector_id
or choice.secondary_sector_id == sector_id
for choice in self.choices.values()
)
def has_choice_with_participant(self, participant_id: str) -> bool:
return any(
choice.participant_id == participant_id for choice in self.choices.values()
)
def create_choice(self, participant_id: str) -> Choice: def create_choice(self, participant_id: str) -> Choice:
if participant_id not in self.choices: if participant_id not in self.choices:
choice = Choice( choice = Choice(
@ -66,6 +78,13 @@ class Round:
choice.set_secondary(secondary_sector_id) choice.set_secondary(secondary_sector_id)
choice.set_comment(comment) choice.set_comment(comment)
def clear_sector_references(self, sector_id: str) -> None:
for choice in self.choices.values():
if choice.priority_sector_id == sector_id:
choice.priority_sector_id = None
if choice.secondary_sector_id == sector_id:
choice.secondary_sector_id = None
def remove_choice(self, participant_id: str) -> None: def remove_choice(self, participant_id: str) -> None:
del self.choices[participant_id] del self.choices[participant_id]
@ -74,6 +93,15 @@ class Round:
def get_battle(self, sector_id: str) -> Battle | None: def get_battle(self, sector_id: str) -> Battle | None:
return self.battles.get(sector_id) return self.battles.get(sector_id)
def has_battle_with_sector(self, sector_id: str) -> bool:
return any(bat.sector_id == sector_id for bat in self.battles.values())
def has_battle_with_participant(self, participant_id: str) -> bool:
return any(
bat.player_1_id == participant_id or bat.player_2_id == participant_id
for bat in self.battles.values()
)
def create_battle(self, sector_id: str) -> Battle: def create_battle(self, sector_id: str) -> Battle:
if sector_id not in self.battles: if sector_id not in self.battles:
battle = Battle(sector_id=sector_id, player_1_id=None, player_2_id=None) battle = Battle(sector_id=sector_id, player_1_id=None, player_2_id=None)
@ -99,5 +127,14 @@ class Round:
bat.set_victory_condition(victory_condition) bat.set_victory_condition(victory_condition)
bat.set_comment(comment) bat.set_comment(comment)
def clear_participant_references(self, participant_id: str) -> None:
for battle in self.battles.values():
if battle.player_1_id == participant_id:
battle.player_1_id = None
if battle.player_2_id == participant_id:
battle.player_2_id = None
if battle.winner_id == participant_id:
battle.winner_id = None
def remove_battle(self, sector_id: str) -> None: def remove_battle(self, sector_id: str) -> None:
del self.battles[sector_id] del self.battles[sector_id]

View file

@ -3,6 +3,7 @@ from uuid import uuid4
from datetime import datetime from datetime import datetime
from typing import Any, Dict, List from typing import Any, Dict, List
from warchron.model.exception import DeletionForbidden
from warchron.model.war_participant import WarParticipant from warchron.model.war_participant import WarParticipant
from warchron.model.objective import Objective from warchron.model.objective import Objective
from warchron.model.campaign_participant import CampaignParticipant from warchron.model.campaign_participant import CampaignParticipant
@ -82,7 +83,16 @@ class War:
obj.set_description(description) obj.set_description(description)
def remove_objective(self, objective_id: str) -> None: def remove_objective(self, objective_id: str) -> None:
# TODO manage sectors referring to it camp_using_obj: List[str] = []
for camp in self.campaigns:
if camp.has_sector_with_objective(objective_id):
camp_using_obj.append(camp.name)
if camp_using_obj:
camps_str = ", ".join(camp_using_obj)
raise DeletionForbidden(
f"This objective is used in campaign(s) sector(s): {camps_str}.\n"
"Remove it from campaign(s) first."
)
del self.objectives[objective_id] del self.objectives[objective_id]
# War participant methods # War participant methods
@ -109,14 +119,23 @@ class War:
def get_all_war_participants(self) -> List[WarParticipant]: def get_all_war_participants(self) -> List[WarParticipant]:
return list(self.participants.values()) return list(self.participants.values())
def update_war_participant(self, player_id: str, *, faction: str) -> None: def update_war_participant(self, participant_id: str, *, faction: str) -> None:
part = self.get_war_participant(player_id) part = self.get_war_participant(participant_id)
# Can't change referred Model.players # Can't change referred Model.players
part.set_faction(faction) part.set_faction(faction)
def remove_war_participant(self, player_id: str) -> None: def remove_war_participant(self, participant_id: str) -> None:
# TODO manage campaign_participants referring to it camp_using_part: List[str] = []
del self.participants[player_id] for camp in self.campaigns:
if camp.has_war_participant(participant_id):
camp_using_part.append(camp.name)
if camp_using_part:
camps_str = ", ".join(camp_using_part)
raise DeletionForbidden(
f"This war participant is used in campaign(s): {camps_str}.\n"
"Remove it from campaign(s) first."
)
del self.participants[participant_id]
# Campaign methods # Campaign methods
@ -139,6 +158,7 @@ class War:
return camp return camp
raise KeyError(f"Campaign {campaign_id} not found in War {self.id}") raise KeyError(f"Campaign {campaign_id} not found in War {self.id}")
# TODO replace multiloops by internal has_* method
def get_campaign_by_round(self, round_id: str) -> Campaign | None: def get_campaign_by_round(self, round_id: str) -> Campaign | None:
for camp in self.campaigns: for camp in self.campaigns:
for rnd in camp.rounds: for rnd in camp.rounds:
@ -146,6 +166,7 @@ class War:
return camp return camp
return None return None
# TODO replace multiloops by internal has_* method
def get_campaign_by_sector(self, sector_id: str) -> Campaign: def get_campaign_by_sector(self, sector_id: str) -> Campaign:
for camp in self.campaigns: for camp in self.campaigns:
for sect in camp.sectors.values(): for sect in camp.sectors.values():
@ -185,6 +206,7 @@ class War:
camp = self.get_campaign(campaign_id) camp = self.get_campaign(campaign_id)
return camp.add_sector(name, round_id, major_id, minor_id, influence_id) return camp.add_sector(name, round_id, major_id, minor_id, influence_id)
# TODO replace multiloops by internal has_* method
def get_sector(self, sector_id: str) -> Sector: def get_sector(self, sector_id: str) -> Sector:
for camp in self.campaigns: for camp in self.campaigns:
for sect in camp.sectors.values(): for sect in camp.sectors.values():
@ -232,6 +254,7 @@ class War:
camp = self.get_campaign(campaign_id) camp = self.get_campaign(campaign_id)
return camp.add_campaign_participant(participant_id, leader, theme) return camp.add_campaign_participant(participant_id, leader, theme)
# TODO replace multiloops by internal has_* method
def get_campaign_participant(self, participant_id: str) -> CampaignParticipant: def get_campaign_participant(self, participant_id: str) -> CampaignParticipant:
for camp in self.campaigns: for camp in self.campaigns:
for part in camp.participants.values(): for part in camp.participants.values():