warchron_app/src/warchron/model/model.py

425 lines
14 KiB
Python

from pathlib import Path
import json
import shutil
from datetime import datetime
from warchron.model.player import Player
from warchron.model.war import War, Objective, WarParticipant
from warchron.model.campaign import Campaign, Sector, CampaignParticipant
from warchron.model.round import Round, Choice, Battle
class Model:
def __init__(self):
self.players: dict[str, Player] = {}
self.wars: dict[str, War] = {}
# File management methods
def new(self):
self.players.clear()
self.wars.clear()
def load(self, path: Path):
self.players.clear()
self.wars.clear()
self._load_data(path)
def save(self, path: Path):
self._save_data(path)
def _load_data(self, path: Path):
if not path.exists() or path.stat().st_size == 0:
return # Start empty
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
self.players.clear()
self.wars.clear()
for p in data.get("players", []):
player = Player.fromDict(p)
self.players[player.id] = player
for w in data.get("wars", []):
war = War.fromDict(w)
self.wars[war.id] = war
except json.JSONDecodeError:
raise RuntimeError("Data file is corrupted")
def _save_data(self, path: Path):
if path.exists():
shutil.copy(path, path.with_suffix(".json.bak"))
data = {
"version": "1.0",
"players": [p.toDict() for p in self.players.values()],
"wars": [w.toDict() for w in self.wars.values()],
}
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
# Player methods
def add_player(self, name):
player = Player(name)
self.players[player.id] = player
return player
def get_player(self, id):
return self.players[id]
def get_player_name(self, player_id: str) -> str:
return self.players[player_id].name
def update_player(self, player_id: str, *, name: str):
player = self.get_player(player_id)
player.set_name(name)
def get_all_players(self) -> list[Player]:
return list(self.players.values())
def remove_player(self, player_id: str):
# TODO manage war_participants referring to it
del self.players[player_id]
# War methods
def get_default_war_values(self) -> dict:
return {"year": datetime.now().year}
def add_war(self, name: str, year: int) -> War:
war = War(name, year)
self.wars[war.id] = war
return war
def get_war(self, id) -> War:
return self.wars[id]
def get_war_by_campaign(self, campaign_id: str) -> War:
for war in self.wars.values():
for camp in war.campaigns:
if camp.id == campaign_id:
return war
raise KeyError(f"Campaign {campaign_id} not found in any War")
def get_war_by_sector(self, sector_id: str) -> Campaign:
for war in self.wars.values():
for camp in war.campaigns:
for sect in camp.sectors.values():
if sect.id == sector_id:
return camp
raise KeyError(f"Sector {sector_id} not found in any War")
def get_war_by_round(self, round_id: str) -> Campaign:
for war in self.wars.values():
for camp in war.campaigns:
for rnd in camp.rounds:
if rnd.id == round_id:
return camp
raise KeyError(f"Round {round_id} not found in any War")
def get_war_by_objective(self, objective_id: str) -> War:
for war in self.wars.values():
for obj in war.objectives.values():
if obj.id == objective_id:
return war
raise KeyError(f"Objective {objective_id} not found in any War")
# TODO don't use this method as participant with same ID (player) can be in several wars!
def get_war_by_war_participant(self, participant_id: str) -> War:
for war in self.wars.values():
if war.has_participant(participant_id):
return war
raise KeyError(f"Participant {participant_id} not found in any War")
def get_war_by_campaign_participant(self, participant_id: str) -> War:
for war in self.wars.values():
camp = war.get_campaign_by_campaign_participant(participant_id)
if camp is not None:
return war
raise KeyError(f"Participant {participant_id} not found")
def update_war(self, war_id: str, *, name: str, year: int):
war = self.get_war(war_id)
war.set_name(name)
war.set_year(year)
def get_all_wars(self) -> list[War]:
return list(self.wars.values())
def remove_war(self, war_id: str):
del self.wars[war_id]
# Objective methods
def add_objective(self, war_id: str, name: str, description: str) -> Objective:
war = self.get_war(war_id)
return war.add_objective(name, description)
def get_objective(self, objective_id) -> Objective:
for war in self.wars.values():
for obj in war.objectives.values():
if obj.id == objective_id:
return obj
raise KeyError("Objective not found")
def update_objective(self, objective_id: str, *, name: str, description: str):
war = self.get_war_by_objective(objective_id)
war.update_objective(objective_id, name=name, description=description)
def remove_objective(self, objective_id: str):
war = self.get_war_by_objective(objective_id)
war.remove_objective(objective_id)
# War participant methods
def get_available_players(self, war_id: str) -> list[Player]:
war = self.get_war(war_id)
return [
player
for player in self.players.values()
if not war.has_participant(player.id)
]
def add_war_participant(
self, war_id: str, player_id: str, faction: str
) -> WarParticipant:
war = self.get_war(war_id)
return war.add_war_participant(player_id, faction)
def get_war_participant(self, participant_id) -> WarParticipant:
for war in self.wars.values():
for part in war.participants.values():
if part.id == participant_id:
return part
raise KeyError("Participant not found")
def update_war_participant(self, participant_id: str, *, faction: str):
war = self.get_war_by_war_participant(participant_id)
war.update_war_participant(participant_id, faction=faction)
def remove_war_participant(self, participant_id: str):
war = self.get_war_by_war_participant(participant_id)
war.remove_war_participant(participant_id)
# Campaign methods
def get_default_campaign_values(self, war_id: str) -> dict:
war = self.get_war(war_id)
return war.get_default_campaign_values()
def add_campaign(self, war_id: str, name: str, month: int) -> Campaign:
war = self.get_war(war_id)
return war.add_campaign(name, month)
def get_campaign(self, campaign_id) -> Campaign:
for war in self.wars.values():
for campaign in war.campaigns:
if campaign.id == campaign_id:
return campaign
raise KeyError("Campaign not found")
def get_campaign_by_round(self, round_id: str) -> Campaign:
for war in self.wars.values():
camp = war.get_campaign_by_round(round_id)
if camp is not None:
return camp
raise KeyError(f"Round {round_id} not found")
def get_campaign_by_campaign_participant(self, participant_id: str) -> Campaign:
for war in self.wars.values():
camp = war.get_campaign_by_campaign_participant(participant_id)
if camp is not None:
return camp
raise KeyError(f"Participant {participant_id} not found")
def get_campaign_by_sector(self, sector_id: str) -> Campaign:
for war in self.wars.values():
camp = war.get_campaign_by_sector(sector_id)
if camp is not None:
return camp
raise KeyError(f"Sector {sector_id} not found")
def update_campaign(self, campaign_id: str, *, name: str, month: int):
war = self.get_war_by_campaign(campaign_id)
war.update_campaign(campaign_id, name=name, month=month)
def remove_campaign(self, campaign_id: str):
war = self.get_war_by_campaign(campaign_id)
war.remove_campaign(campaign_id)
# Sector methods
def add_sector(
self,
campaign_id: str,
name: str,
round_id: str,
major_id: str,
minor_id: str,
influence_id: str,
) -> Sector:
camp = self.get_campaign(campaign_id)
return camp.add_sector(name, round_id, major_id, minor_id, influence_id)
def get_sector(self, sector_id) -> Sector:
for war in self.wars.values():
for camp in war.campaigns:
for sect in camp.sectors.values():
if sect.id == sector_id:
return sect
raise KeyError("Sector not found")
def update_sector(
self,
sector_id: str,
*,
name: str,
round_id: str,
major_id: str,
minor_id: str,
influence_id: str,
):
war = self.get_war_by_sector(sector_id)
war.update_sector(
sector_id,
name=name,
round_id=round_id,
major_id=major_id,
minor_id=minor_id,
influence_id=influence_id,
)
def remove_sector(self, sector_id: str):
camp = self.get_campaign_by_sector(sector_id)
camp.remove_sector(sector_id)
# Campaign participant methods
def get_available_war_participants(self, campaign_id: str) -> list[WarParticipant]:
war = self.get_war_by_campaign(campaign_id)
return war.get_available_war_participants(campaign_id)
def add_campaign_participant(
self, camp_id: str, player_id: str, leader: str, theme: str
) -> CampaignParticipant:
camp = self.get_campaign(camp_id)
return camp.add_campaign_participant(player_id, leader, theme)
def get_participant_name(self, participant_id: str) -> str:
war = self.get_war_by_war_participant(participant_id)
war_part = war.get_war_participant(participant_id)
return self.players[war_part.player_id].name
def get_campaign_participant(self, participant_id) -> CampaignParticipant:
for war in self.wars.values():
for camp in war.campaigns:
for part in camp.participants.values():
if part.id == participant_id:
return part
raise KeyError("Participant not found")
def update_campaign_participant(
self,
participant_id: str,
*,
leader: str,
theme: str,
):
war = self.get_war_by_campaign_participant(participant_id)
war.update_campaign_participant(participant_id, leader=leader, theme=theme)
def remove_campaign_participant(self, participant_id: str):
war = self.get_war_by_campaign_participant(participant_id)
war.remove_campaign_participant(participant_id)
# Round methods
def add_round(self, campaign_id: str) -> Round:
camp = self.get_campaign(campaign_id)
return camp.add_round()
def get_round(self, round_id: str) -> Round:
for war in self.wars.values():
for camp in war.campaigns:
for rnd in camp.rounds:
if rnd.id == round_id:
return rnd
raise KeyError("Round not found")
def get_round_index(self, round_id: str) -> int:
camp = self.get_campaign_by_round(round_id)
return camp.get_round_index(round_id)
def get_round_sectors(self, round_id: str) -> list[Sector]:
camp = self.get_campaign_by_round(round_id)
return [s for s in camp.sectors.values() if s.round_id == round_id]
def get_round_participants(self, round_id: str) -> list[CampaignParticipant]:
camp = self.get_campaign_by_round(round_id)
return list(camp.participants.values())
def remove_round(self, round_id: str):
war = self.get_war_by_round(round_id)
war.remove_round(round_id)
# Choice methods
def create_choice(self, round_id: str, participant_id: str) -> Choice:
war = self.get_war_by_round(round_id)
return war.create_choice(round_id, participant_id)
def get_round_choices_data(self, round_id: str):
camp = self.get_campaign_by_round(round_id)
rnd = self.get_round(round_id)
participants = camp.participants.values()
sectors = [s for s in camp.sectors.values() if s.round_id == round_id]
return camp, rnd, participants, sectors
def update_choice(
self,
round_id: str,
participant_id: str,
priority_sector_id: str | None,
secondary_sector_id: str | None,
comment: str | None,
):
war = self.get_war_by_round(round_id)
war.update_choice(
round_id, participant_id, priority_sector_id, secondary_sector_id, comment
)
def remove_choice(self, round_id: str, participant_id: str):
war = self.get_war_by_round(round_id)
war.remove_choice(round_id, participant_id)
# Battle methods
def create_battle(self, round_id: str, sector_id: str) -> Battle:
war = self.get_war_by_round(round_id)
return war.create_battle(round_id, sector_id)
def update_battle(
self,
round_id: str,
sector_id: str,
player_1_id: str | None,
player_2_id: str | None,
winner_id: str | None,
score: str | None,
victory_condition: str | None,
comment: str | None,
):
war = self.get_war_by_round(round_id)
war.update_battle(
round_id,
sector_id,
player_1_id,
player_2_id,
winner_id,
score,
victory_condition,
comment,
)
def remove_battle(self, round_id: str, sector_id: str):
war = self.get_war_by_round(round_id)
war.remove_battle(round_id, sector_id)