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

View file

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from uuid import uuid4 from uuid import uuid4
from warchron.model.round import Round from warchron.model.round import Round, Choice, Battle
class Campaign: class Campaign:
def __init__(self, name: str, month: int): def __init__(self, name: str, month: int):
@ -10,7 +10,7 @@ class Campaign:
self.month: int = month self.month: int = month
self.participants: dict[str, CampaignParticipant] = {} self.participants: dict[str, CampaignParticipant] = {}
self.sectors: dict[str, Sector] = {} self.sectors: dict[str, Sector] = {}
self.rounds = [] self.rounds: list[Round] = []
self.is_over = False self.is_over = False
def set_id(self, new_id: str): def set_id(self, new_id: str):
@ -72,6 +72,8 @@ class Campaign:
part.set_theme(theme) part.set_theme(theme)
def remove_campaign_participant(self, player_id: str): 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] del self.participants[player_id]
# Sector methods # Sector methods
@ -81,12 +83,18 @@ class Campaign:
self.sectors[sect.id] = sect self.sectors[sect.id] = sect
return sect return sect
def get_sector(self, id: str) -> Sector: def get_sector(self, sector_id: str) -> Sector:
return self.sectors[id] 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]: 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(self, sector_id: str, *, name: str, round_id: str, major_id: str, minor_id: str, influence_id: str): 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 = self.get_sector(sector_id)
sect.set_name(name) sect.set_name(name)
@ -96,6 +104,8 @@ class Campaign:
sect.set_influence(influence_id) sect.set_influence(influence_id)
def remove_sector(self, sector_id: str): def remove_sector(self, sector_id: str):
# TODO manage choices referring to it
# TODO manage battles referring to it
del self.sectors[sector_id] del self.sectors[sector_id]
# Round methods # Round methods
@ -104,7 +114,10 @@ class Campaign:
return any(r.id == round_id for r in self.rounds) return any(r.id == round_id for r in self.rounds)
def get_round(self, round_id: str) -> Round: 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]: def get_all_rounds(self) -> list[Round]:
return list(self.rounds) return list(self.rounds)
@ -135,6 +148,16 @@ class Campaign:
return rnd.name return rnd.name
return "" 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: class CampaignParticipant:
def __init__(self,player_id: str, leader: str, theme: str): def __init__(self,player_id: str, leader: str, theme: str):
self.id: str = player_id # ref to War.participants 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.player import Player
from warchron.model.war import War, Objective, WarParticipant from warchron.model.war import War, Objective, WarParticipant
from warchron.model.campaign import Campaign, Sector, CampaignParticipant from warchron.model.campaign import Campaign, Sector, CampaignParticipant
from warchron.model.round import Round from warchron.model.round import Round, Choice, Battle
class Model: class Model:
def __init__(self): def __init__(self):
@ -76,6 +76,7 @@ class Model:
return list(self.players.values()) return list(self.players.values())
def remove_player(self, player_id: str): def remove_player(self, player_id: str):
# TODO manage war_participants referring to it
del self.players[player_id] del self.players[player_id]
# War methods # War methods
@ -100,6 +101,22 @@ 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")
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: 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():
@ -107,6 +124,7 @@ class Model:
return war return war
raise KeyError(f"Objective {objective_id} not found in any 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: def get_war_by_war_participant(self, participant_id: str) -> War:
for war in self.wars.values(): for war in self.wars.values():
for part in war.participants.values(): for part in war.participants.values():
@ -199,6 +217,7 @@ class Model:
return camp return camp
raise KeyError(f"Round {round_id} not found") 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: def get_campaign_by_campaign_participant(self, participant_id: str) -> Campaign:
for war in self.wars.values(): for war in self.wars.values():
camp = war.get_campaign_by_campaign_participant(participant_id) camp = war.get_campaign_by_campaign_participant(participant_id)
@ -236,8 +255,8 @@ class Model:
raise KeyError("Sector not found") 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): 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) war = self.get_war_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.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): def remove_sector(self, sector_id: str):
camp = self.get_campaign_by_sector(sector_id) camp = self.get_campaign_by_sector(sector_id)
@ -287,12 +306,28 @@ class Model:
camp = self.get_campaign_by_round(round_id) camp = self.get_campaign_by_round(round_id)
return camp.get_round_index(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 = 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 # 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): def get_round_choices_data(self, round_id: str):
camp = self.get_campaign_by_round(round_id) camp = self.get_campaign_by_round(round_id)
rnd = self.get_round(round_id) rnd = self.get_round(round_id)

View file

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

View file

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