wip close round/campaign/war + refacto json None

This commit is contained in:
Maxime Réaux 2026-02-11 19:22:43 +01:00
parent 4c8086caf4
commit 6cbb7c6534
26 changed files with 474 additions and 108 deletions

View file

@ -52,10 +52,13 @@ class AppController:
self.view.influenceToken.toggled.connect(self.wars.set_influence_token)
self.view.addObjectiveBtn.clicked.connect(self.wars.add_objective)
self.view.addWarParticipantBtn.clicked.connect(self.wars.add_war_participant)
self.view.endWarBtn.clicked.connect(self.wars.close_war)
self.view.addSectorBtn.clicked.connect(self.campaigns.add_sector)
self.view.addCampaignParticipantBtn.clicked.connect(
self.campaigns.add_campaign_participant
)
self.view.endCampaignBtn.clicked.connect(self.campaigns.close_campaign)
self.view.endRoundBtn.clicked.connect(self.rounds.close_round)
self.view.on_edit_item = self.edit_item
self.view.on_delete_item = self.delete_item

View file

@ -13,6 +13,7 @@ from warchron.controller.dtos import (
SectorDTO,
RoundDTO,
)
from warchron.model.closure_service import ClosureService
from warchron.view.campaign_dialog import CampaignDialog
from warchron.view.campaign_participant_dialog import CampaignParticipantDialog
from warchron.view.sector_dialog import SectorDialog
@ -50,6 +51,7 @@ class CampaignController:
for p in camp_parts
]
self.app.view.display_campaign_participants(participants_for_display)
self.app.view.endCampaignBtn.setEnabled(not camp.is_over)
def _validate_campaign_inputs(self, name: str, month: int) -> bool:
if not name.strip():
@ -99,6 +101,28 @@ class CampaignController:
return
self.app.model.update_campaign(campaign_id, name=name, month=month)
def close_campaign(self) -> None:
campaign_id = self.app.navigation.selected_campaign_id
if not campaign_id:
return
camp = self.app.model.get_campaign(campaign_id)
if camp.is_over:
return
try:
ties = ClosureService.close_campaign(camp)
except RuntimeError as e:
QMessageBox.warning(self.app.view, "Cannot close campaign", str(e))
return
if ties:
QMessageBox.information(
self.app.view,
"Tie detected",
"Campaign has unresolved ties.",
)
return
self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
# Campaign participant methods
def add_campaign_participant(self) -> None:
@ -148,7 +172,12 @@ class CampaignController:
# Sector methods
def _validate_sector_inputs(
self, name: str, round_id: str, major_id: str, minor_id: str, influence_id: str
self,
name: str,
round_id: str | None,
major_id: str | None,
minor_id: str | None,
influence_id: str | None,
) -> bool:
if not name.strip():

View file

@ -29,7 +29,7 @@ class WarDTO:
class ObjectiveDTO:
id: str
name: str
description: str
description: str | None
@dataclass(frozen=True, slots=True)
@ -63,9 +63,9 @@ class SectorDTO:
id: str
name: str
round_index: int | None
major: str
minor: str
influence: str
major: str | None
minor: str | None
influence: str | None
@dataclass
@ -78,8 +78,8 @@ class RoundDTO:
class ChoiceDTO:
id: str
participant_name: str
priority_sector: str
secondary_sector: str
priority_sector: str | None
secondary_sector: str | None
comment: str | None
@ -87,8 +87,8 @@ class ChoiceDTO:
class BattleDTO:
id: str
sector_name: str
player_1: str
player_2: str
player_1: str | None
player_2: str | None
winner: str | None
score: str | None
victory_condition: str | None

View file

@ -1,13 +1,13 @@
from typing import List, TYPE_CHECKING
from PyQt6.QtWidgets import QDialog
from PyQt6.QtWidgets import QDialog, QMessageBox
from warchron.constants import ItemType, RefreshScope
if TYPE_CHECKING:
from warchron.controller.app_controller import AppController
from warchron.controller.dtos import ParticipantOption, SectorDTO, ChoiceDTO, BattleDTO
from warchron.model.closure_service import ClosureService
from warchron.view.choices_dialog import ChoicesDialog
from warchron.view.battles_dialog import BattlesDialog
@ -92,6 +92,7 @@ class RoundController:
)
)
self.app.view.display_round_battles(battles_for_display)
self.app.view.endRoundBtn.setEnabled(not rnd.is_over)
def add_round(self) -> None:
if not self.app.navigation.selected_campaign_id:
@ -102,6 +103,28 @@ class RoundController:
RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=rnd.id
)
def close_round(self) -> None:
round_id = self.app.navigation.selected_round_id
if not round_id:
return
rnd = self.app.model.get_round(round_id)
if rnd.is_over:
return
try:
ties = ClosureService.close_round(rnd)
except RuntimeError as e:
QMessageBox.warning(self.app.view, "Cannot close round", str(e))
return
if ties:
QMessageBox.information(
self.app.view,
"Tie detected",
"Round has unresolved ties. Resolution system not implemented yet.",
)
return
self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
# Choice methods
def edit_round_choice(self, choice_id: str) -> None:

View file

@ -11,6 +11,7 @@ from warchron.controller.dtos import (
WarParticipantDTO,
ObjectiveDTO,
)
from warchron.model.closure_service import ClosureService
from warchron.view.war_dialog import WarDialog
from warchron.view.objective_dialog import ObjectiveDialog
from warchron.view.war_participant_dialog import WarParticipantDialog
@ -44,6 +45,7 @@ class WarController:
for p in war_parts
]
self.app.view.display_war_participants(participants_for_display)
self.app.view.endWarBtn.setEnabled(not war.is_over)
def _validate_war_inputs(self, name: str, year: int) -> bool:
if not name.strip():
@ -86,6 +88,28 @@ class WarController:
return
self.app.model.update_war(war_id, name=name, year=year)
def close_war(self) -> None:
war_id = self.app.navigation.selected_war_id
if not war_id:
return
war = self.app.model.get_war(war_id)
if war.is_over:
return
try:
ties = ClosureService.close_war(war)
except RuntimeError as e:
QMessageBox.warning(self.app.view, "Cannot close war", str(e))
return
if ties:
QMessageBox.information(
self.app.view,
"Tie detected",
"War has unresolved ties.",
)
return
self.app.is_dirty = True
self.app.navigation.refresh(RefreshScope.CURRENT_SELECTION_DETAILS)
def set_major_value(self, value: int) -> None:
war_id = self.app.navigation.selected_war_id
if not war_id:
@ -109,7 +133,7 @@ class WarController:
# Objective methods
def _validate_objective_inputs(self, name: str, description: str) -> bool:
def _validate_objective_inputs(self, name: str, description: str | None) -> bool:
if not name.strip():
QMessageBox.warning(
self.app.view, "Invalid name", "Objective name cannot be empty."