auto create & edit choice

This commit is contained in:
Maxime Réaux 2026-01-30 15:32:44 +01:00
parent 723723dea1
commit 9f676f6b9d
6 changed files with 166 additions and 59 deletions

View file

@ -10,14 +10,14 @@ from warchron.view.view import PlayerDialog, WarDialog, CampaignDialog, Objectiv
class Controller:
def __init__(self, model: Model, view: View):
self.model = model
self.view = view
self.model: Model = model
self.view: View = view
self.current_file: Path | None = None
self.selected_war_id = None
self.selected_campaign_id = None
self.selected_round_id = None
self.selected_war_id: str = None
self.selected_campaign_id: str = None
self.selected_round_id: str = None
self.view.on_close_callback = self.on_app_close
self.is_dirty = False
self.is_dirty: bool = False
self.__connect()
self.refresh_players_view()
self.refresh_wars_view()
@ -165,26 +165,24 @@ class Controller:
self.view.display_campaign_participants(participants_for_display)
def _fill_round_details(self, round_id: str):
index = self.model.get_round_index(round_id)
self.view.show_round_details(index=index)
camp, rnd, participants, sectors = self.model.get_round_choices_data(round_id)
rows = []
rnd = self.model.get_round(round_id)
camp = self.model.get_campaign_by_round(round_id)
self.view.show_round_details(index=camp.get_round_index(round_id))
participants = self.model.get_round_participants(round_id)
choices_for_display = []
for part in participants:
player = self.model.get_player(part.id)
choice = rnd.get_choice(part.id)
priority_name = ""
secondary_name = ""
if choice and choice.priority_sector_id:
priority_name = self.model.get_sector(choice.priority_sector_id).name
if choice and choice.secondary_sector_id:
secondary_name = self.model.get_sector(choice.secondary_sector_id).name
rows.append({
"participant_id": part.id,
"participant_name": player.name,
"priority": priority_name,
"secondary": secondary_name,
})
self.view.display_round_choices(rows)
if not choice:
choice=self.model.create_choice(round_id=rnd.id, participant_id=part.id)
priority_name = camp.get_sector_name(choice.priority_sector_id) if choice else ""
secondary_name = camp.get_sector_name(choice.secondary_sector_id) if choice else ""
choices_for_display.append((
self.model.get_player_name(part.id),
priority_name,
secondary_name,
choice.participant_id
))
self.view.display_round_choices(choices_for_display)
def on_tree_selection_changed(self, selection):
self.selected_war_id = None
@ -578,6 +576,8 @@ class Controller:
self.is_dirty = True
self.refresh_and_select(RefreshScope.WARS_TREE, item_type=ItemType.ROUND, item_id=rnd.id)
# Choices methods
def edit_round_choice(self, choice_id: str):
round_id = self.selected_round_id
if not round_id:
@ -586,11 +586,13 @@ class Controller:
choice = rnd.get_choice(choice_id)
if not choice:
return
participant = camp.participants[choice.participant_id]
part = camp.participants[choice.participant_id]
player = self.model.get_player(part.id)
part_opt = ParticipantOption(id=player.id, name=player.name)
dialog = ChoicesDialog(
self.view,
participants=[participant],
default_participant_id=participant.id,
participants=[part_opt],
default_participant_id=part.id,
sectors=sectors,
default_priority_id=choice.priority_sector_id,
default_secondary_id=choice.secondary_sector_id,
@ -598,7 +600,8 @@ class Controller:
if dialog.exec() != QDialog.DialogCode.Accepted:
return
rnd.set_choice(
participant_id=participant.id,
participant_id=part.id,
priority_sector_id=dialog.get_priority_id(),
secondary_sector_id=dialog.get_secondary_id(),
)

View file

@ -1,7 +1,7 @@
from __future__ import annotations
from uuid import uuid4
from warchron.model.round import Round
from warchron.model.round import Round, Choice, Battle
class Campaign:
def __init__(self, name: str, month: int):
@ -10,7 +10,7 @@ class Campaign:
self.month: int = month
self.participants: dict[str, CampaignParticipant] = {}
self.sectors: dict[str, Sector] = {}
self.rounds = []
self.rounds: list[Round] = []
self.is_over = False
def set_id(self, new_id: str):
@ -72,6 +72,8 @@ class Campaign:
part.set_theme(theme)
def remove_campaign_participant(self, player_id: str):
# TODO manage choices referring to it
# TODO manage battles referring to it
del self.participants[player_id]
# Sector methods
@ -81,12 +83,18 @@ class Campaign:
self.sectors[sect.id] = sect
return sect
def get_sector(self, id: str) -> Sector:
return self.sectors[id]
def get_sector(self, sector_id: str) -> Sector:
return self.sectors[sector_id]
def get_sector_name(self, sector_id: str) -> str:
if sector_id is None:
return ""
return self.sectors[sector_id].name
def get_all_sectors(self) -> list[Sector]:
return list(self.sectors.values())
# TODO manage choices referring to it (round order!)
def update_sector(self, sector_id: str, *, name: str, round_id: str, major_id: str, minor_id: str, influence_id: str):
sect = self.get_sector(sector_id)
sect.set_name(name)
@ -96,6 +104,8 @@ class Campaign:
sect.set_influence(influence_id)
def remove_sector(self, sector_id: str):
# TODO manage choices referring to it
# TODO manage battles referring to it
del self.sectors[sector_id]
# Round methods
@ -104,7 +114,10 @@ class Campaign:
return any(r.id == round_id for r in self.rounds)
def get_round(self, round_id: str) -> Round:
return self.rounds[round_id]
for rnd in self.rounds:
if rnd.id == round_id:
return rnd
raise KeyError(f"Round {round_id} not found")
def get_all_rounds(self) -> list[Round]:
return list(self.rounds)
@ -135,6 +148,16 @@ class Campaign:
return rnd.name
return ""
# Choice methods
def create_choice(self, round_id: str, participant_id: str) -> Choice:
rnd = self.get_round(round_id)
return rnd.create_choice(participant_id)
def remove_choice(self, round_id: str, participant_id: str) -> Choice:
rnd = self.get_round(round_id)
rnd.remove_choice(participant_id)
class CampaignParticipant:
def __init__(self,player_id: str, leader: str, theme: str):
self.id: str = player_id # ref to War.participants

View file

@ -6,7 +6,7 @@ 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
from warchron.model.round import Round, Choice, Battle
class Model:
def __init__(self):
@ -76,6 +76,7 @@ class Model:
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
@ -100,6 +101,22 @@ class Model:
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():
@ -107,6 +124,7 @@ class Model:
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():
for part in war.participants.values():
@ -199,6 +217,7 @@ class Model:
return camp
raise KeyError(f"Round {round_id} not found")
# TODO don't use this method as participant with same ID (player) can be in several campaigns!
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)
@ -236,8 +255,8 @@ class Model:
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):
camp = self.get_campaign_by_sector(sector_id)
camp.update_sector(sector_id, name=name, round_id=round_id, major_id=major_id, minor_id=minor_id, influence_id=influence_id)
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)
@ -287,12 +306,28 @@ class Model:
camp = self.get_campaign_by_round(round_id)
return camp.get_round_index(round_id)
def remove_round(self, round_id: str):
def get_round_sectors(self, round_id: str) -> list[Sector]:
camp = self.get_campaign_by_round(round_id)
camp.remove_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)
# Choices 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 remove_choice(self, round_id: str, participant_id: str):
war = self.get_war_by_round(round_id)
war.remove_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)

View file

@ -4,8 +4,8 @@ from uuid import uuid4
class Round:
def __init__(self):
self.id: str = str(uuid4())
self.choices: dict[str, RoundChoice] = {}
self.battles = {}
self.choices: dict[str, Choice] = {}
self.battles: dict[str, Battle] = {}
self.is_over: bool = False
def set_id(self, new_id: str):
@ -15,7 +15,7 @@ class Round:
self.is_over = new_state
def set_choice(self, participant_id: str, priority_sector_id: str | None, secondary_sector_id: str | None):
self.choices[participant_id] = RoundChoice(participant_id, priority_sector_id, secondary_sector_id)
self.choices[participant_id] = Choice(participant_id, priority_sector_id, secondary_sector_id)
def toDict(self):
return {
@ -38,12 +38,30 @@ class Round:
# Choices methods
def get_choice(self, participant_id: str) -> RoundChoice | None:
def get_choice(self, participant_id: str) -> Choice | None:
return self.choices.get(participant_id)
def create_choice(self, participant_id: str) -> Choice:
if participant_id not in self.choices:
choice = Choice(
participant_id=participant_id,
priority_sector_id=None,
secondary_sector_id=None
)
self.choices[participant_id] = choice
return self.choices[participant_id]
class RoundChoice:
def remove_choice(self,participant_id: str):
del self.choices[participant_id]
class Choice:
def __init__(self, participant_id: str, priority_sector_id: str | None = None, secondary_sector_id: str | None = None):
self.participant_id: str = participant_id # ref to Campaign.participants
self.priority_sector_id: str | None = priority_sector_id # ref to Campaign.sectors
self.secondary_sector_id: str | None = secondary_sector_id # ref to Campaign.sectors
class Battle:
def __init__(self, sector_id: str, player_1_id: str | None = None, player_2_id: str | None = None):
self.sector_id: str = sector_id # ref to Campaign.sector
self.player_1_id: str | None = player_1_id # ref to Campaign.participants
self.player_2_id: str | None = player_2_id # ref to Campaign.participants

View file

@ -3,6 +3,7 @@ from uuid import uuid4
from datetime import datetime
from warchron.model.campaign import Campaign, Sector, CampaignParticipant
from warchron.model.round import Round, Choice, Battle
class War:
@ -12,7 +13,7 @@ class War:
self.year: int = year
self.participants: dict[str, WarParticipant] = {}
self.objectives: dict[str, Objective] = {}
self.campaigns = []
self.campaigns: list[Campaign] = []
self.is_over: bool = False
def set_id(self, new_id: str):
@ -72,6 +73,7 @@ class War:
obj.set_description(description)
def remove_objective(self, objective_id: str):
# TODO manage sectors referring to it
del self.objectives[objective_id]
# War participant methods
@ -100,6 +102,7 @@ class War:
part.set_faction(faction)
def remove_war_participant(self, player_id: str):
# TODO manage campaign_participants referring to it
del self.participants[player_id]
# Campaign methods
@ -167,9 +170,9 @@ class War:
def get_sector(self, id: str) -> Sector:
return self.sectors[id]
def update_sector(self, objective_id: str, *, name: str, round_id: str, major_id: str, minor_id: str, influence_id: str):
obj = self.get_objective(objective_id)
obj.set_name(name)
def update_sector(self, sector_id: str, *, name: str, round_id: str, major_id: str, minor_id: str, influence_id: str):
camp = self.get_campaign_by_sector(sector_id)
camp.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)
@ -204,6 +207,26 @@ class War:
camp = self.get_campaign_by_campaign_participant(participant_id)
camp.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 remove_round(self, round_id: str):
camp = self.get_campaign_by_round(round_id)
camp.remove_round(round_id)
# Choice methods
def create_choice(self, round_id: str, participant_id: str) -> Choice:
camp = self.get_campaign_by_round(round_id)
return camp.create_choice(round_id, participant_id)
def remove_choice(self, round_id: str, participant_id: str):
camp = self.get_campaign_by_round(round_id)
camp.remove_choice(round_id, participant_id)
class Objective:
def __init__(self, name: str, description: str):
self.id: str = str(uuid4())

View file

@ -387,6 +387,8 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow):
if not name_item:
return
choice_id = name_item.data(Qt.ItemDataRole.UserRole)
if choice_id is None:
return
menu = QMenu(self)
edit_action = menu.addAction("Edit")
action = menu.exec(self.choicesTable.viewport().mapToGlobal(pos))
@ -396,16 +398,19 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow):
def show_round_details(self, *, index: int):
self.roundNb.setText(f"Round {index}")
def display_round_choices(self, rows: list[dict]):
self.choicesTable.setRowCount(len(rows))
for row, data in enumerate(rows):
self.choicesTable.setItem(row, 0, QtWidgets.QTableWidgetItem(data["participant_name"]))
self.choicesTable.setItem(row, 1, QtWidgets.QTableWidgetItem(data["priority"]))
self.choicesTable.setItem(row, 2, QtWidgets.QTableWidgetItem(data["secondary"]))
self.choicesTable.item(row, 0).setData(
Qt.ItemDataRole.UserRole,
data["participant_id"]
)
def display_round_choices(self, participants: list[tuple[str, str, str, str]]):
table = self.choicesTable
table.clearContents()
table.setRowCount(len(participants))
for row, (participant, priority, secondary, choice_id) in enumerate(participants):
participant_item = QtWidgets.QTableWidgetItem(participant)
priority_item = QtWidgets.QTableWidgetItem(priority)
secondary_item = QtWidgets.QTableWidgetItem(secondary)
participant_item.setData(Qt.ItemDataRole.UserRole, choice_id)
table.setItem(row, 0, participant_item)
table.setItem(row, 1, priority_item)
table.setItem(row, 2, secondary_item)
table.resizeColumnsToContents()
class PlayerDialog(QDialog):
def __init__(self, parent=None, *, default_name: str = ""):