add/edit/delete sectors and participants in campaign

This commit is contained in:
Maxime Réaux 2026-01-30 00:34:22 +01:00
parent 495a5adb98
commit 2816da010c
22 changed files with 1799 additions and 923 deletions

View file

@ -1,12 +1,12 @@
from pathlib import Path
from datetime import datetime
from PyQt6.QtWidgets import QMessageBox, QDialog
from warchron.model.model import Model
from warchron.view.view import View
from warchron.constants import ItemType, RefreshScope
from warchron.view.view import PlayerDialog, WarDialog, CampaignDialog, ObjectiveDialog, ParticipantDialog
from warchron.view.view import PlayerDialog, WarDialog, CampaignDialog, ObjectiveDialog, WarParticipantDialog, CampaignParticipantDialog, SectorDialog
from warchron.controller.dtos import ParticipantOption
class Controller:
def __init__(self, model: Model, view: View):
@ -37,6 +37,8 @@ class Controller:
self.view.addWarBtn.clicked.connect(self.add_war)
self.view.addObjectiveBtn.clicked.connect(self.add_objective)
self.view.addWarParticipantBtn.clicked.connect(self.add_war_participant)
self.view.addSectorBtn.clicked.connect(self.add_sector)
self.view.addCampaignParticipantBtn.clicked.connect(self.add_campaign_participant)
self.view.on_edit_item = self.edit_item
self.view.on_delete_item = self.delete_item
@ -145,6 +147,22 @@ class Controller:
def _fill_campaign_details(self, campaign_id: str):
camp = self.model.get_campaign(campaign_id)
self.view.show_campaign_details(name=camp.name, month=camp.month)
sectors = camp.get_all_sectors()
sectors_for_display = []
war = self.model.get_war_by_campaign(camp.id)
for sect in sectors:
round_index = camp.get_round_index(sect.round_id)
major_name = war.get_objective_name(sect.major_objective_id)
minor_name = war.get_objective_name(sect.minor_objective_id)
influence_name = war.get_objective_name(sect.influence_objective_id)
sectors_for_display.append((sect.name,round_index,major_name,minor_name,influence_name,sect.id))
self.view.display_campaign_sectors(sectors_for_display)
participants = camp.get_all_campaign_participants()
participants_for_display = [
(self.model.get_player_name(p.id), p.leader, p.theme, p.id)
for p in participants
]
self.view.display_campaign_participants(participants_for_display)
def _fill_round_details(self, round_id: str):
index = self.model.get_round_index(round_id)
@ -248,12 +266,65 @@ class Controller:
elif item_type == ItemType.WAR_PARTICIPANT:
part = self.model.get_war_participant(item_id)
player = self.model.get_player(part.id)
dialog = ParticipantDialog(self.view, players=[player], default_player_id=part.id, default_faction=part.faction)
dialog = WarParticipantDialog(
self.view,
players=[player],
default_player_id=part.id,
default_faction=part.faction,
editable_player=False
)
if dialog.exec() == QDialog.DialogCode.Accepted:
id = dialog.get_player_id()
faction = dialog.get_participant_faction()
self.model.update_war_participant(item_id, faction=faction)
self.refresh(RefreshScope.WAR_DETAILS)
elif item_type == ItemType.SECTOR:
sect = self.model.get_sector(item_id)
camp = self.model.get_campaign_by_sector(item_id)
war = self.model.get_war_by_campaign(camp.id)
rounds = camp.get_all_rounds()
objectives = war.get_all_objectives()
dialog = SectorDialog(
self.view,
default_name=sect.name,
rounds=rounds,
default_round_id=sect.round_id,
objectives=objectives,
default_major_id=sect.major_objective_id,
default_minor_id=sect.minor_objective_id,
default_influence_id=sect.influence_objective_id,
)
if dialog.exec() == QDialog.DialogCode.Accepted:
name = dialog.get_sector_name()
round_id = dialog.get_round_id()
major_id = dialog.get_major_id()
minor_id = dialog.get_minor_id()
influence_id = dialog.get_influence_id()
self.model.update_sector(
item_id,
name=name,
round_id=round_id,
major_id=major_id,
minor_id=minor_id,
influence_id=influence_id
)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
elif item_type == ItemType.CAMPAIGN_PARTICIPANT:
part = self.model.get_campaign_participant(item_id)
player = self.model.get_player(part.id)
part_opt = [ParticipantOption(id=player.id, name=player.name)]
dialog = CampaignParticipantDialog(
self.view,
participants=part_opt,
default_participant_id=part.id,
default_leader=part.leader,
default_theme=part.theme,
editable_player=False
)
if dialog.exec() == QDialog.DialogCode.Accepted:
leader = dialog.get_participant_leader()
theme = dialog.get_participant_theme()
self.model.update_campaign_participant(item_id, leader=leader, theme=theme)
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
self.is_dirty = True
def delete_item(self, item_type: str, item_id: str):
@ -282,6 +353,12 @@ class Controller:
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
@ -345,12 +422,12 @@ class Controller:
# Objective methods
def _validate_objective_inputs(self, name: str, description: int) -> bool:
def _validate_objective_inputs(self, name: str, description: str) -> bool:
if not name.strip():
QMessageBox.warning(
self.view,
"Invalid name",
"Campaign name cannot be empty."
"Objective name cannot be empty."
)
return False
return True
@ -363,7 +440,7 @@ class Controller:
return
name = dialog.get_objective_name()
description = dialog.get_objective_description()
if not name:
if not self._validate_objective_inputs(name, description):
return
self.model.add_objective(self.selected_war_id, name, description)
self.is_dirty = True
@ -375,7 +452,7 @@ class Controller:
if not self.selected_war_id:
return
players = self.model.get_available_players(self.selected_war_id)
dialog = ParticipantDialog(self.view, players=players)
dialog = WarParticipantDialog(self.view, players=players)
if dialog.exec() != QDialog.DialogCode.Accepted:
return
player_id = dialog.get_player_id()
@ -419,6 +496,59 @@ class Controller:
self.is_dirty = True
self.refresh_and_select(RefreshScope.WARS_TREE, item_type=ItemType.CAMPAIGN, item_id=camp.id)
# Campaign participant methods
def add_campaign_participant(self):
if not self.selected_campaign_id:
return
participants = self.model.get_available_war_participants(self.selected_campaign_id)
part_opts = [ParticipantOption(id=p.id, name=self.model.get_player_name(p.id)) for p in participants]
dialog = CampaignParticipantDialog(self.view, participants=part_opts)
if dialog.exec() != QDialog.DialogCode.Accepted:
return
player_id = dialog.get_player_id()
leader = dialog.get_participant_leader()
theme = dialog.get_participant_theme()
if not player_id:
return
self.model.add_campaign_participant(self.selected_campaign_id, player_id, leader, theme)
self.is_dirty = True
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
# Sector methods
def _validate_sector_inputs(self, name: str, round_id: str, major_id: str, minor_id: str, influence_id:str) -> bool:
if not name.strip():
QMessageBox.warning(
self.view,
"Invalid name",
"Sector name cannot be empty."
)
return False
# allow same objectives in different fields?
return True
def add_sector(self):
if not self.selected_campaign_id:
return
war = self.model.get_war_by_campaign(self.selected_campaign_id)
camp = self.model.get_campaign(self.selected_campaign_id)
rounds = camp.get_all_rounds()
objectives = war.get_all_objectives()
dialog = SectorDialog(self.view, default_name="", rounds=rounds, objectives=objectives)
if dialog.exec() != QDialog.DialogCode.Accepted:
return
name = dialog.get_sector_name()
round_id = dialog.get_round_id()
major_id = dialog.get_major_id()
minor_id = dialog.get_minor_id()
influence_id = dialog.get_influence_id()
if not self._validate_sector_inputs(name, round_id, major_id, minor_id, influence_id):
return
self.model.add_sector(self.selected_campaign_id, name, round_id, major_id, minor_id, influence_id)
self.is_dirty = True
self.refresh(RefreshScope.CAMPAIGN_DETAILS)
# Round methods
def add_round(self):

View file

@ -0,0 +1,6 @@
from dataclasses import dataclass
@dataclass(frozen=True)
class ParticipantOption:
id: str
name: str