From c3e6793f6905f0f2d674bff2a6d8d3bd6aed7e29 Mon Sep 17 00:00:00 2001 From: luxick Date: Wed, 14 Mar 2018 13:40:02 +0100 Subject: [PATCH] Create and modify enemies function. --- dsst/common/models.py | 1 + dsst/dsst_gtk3/dialogs.py | 107 +-- dsst/dsst_gtk3/gtk_ui.py | 41 +- dsst/dsst_gtk3/handlers/base_data_handlers.py | 20 +- dsst/dsst_gtk3/handlers/death_handlers.py | 7 +- dsst/dsst_gtk3/handlers/dialog_handlers.py | 25 +- dsst/dsst_gtk3/handlers/season_handlers.py | 6 +- dsst/dsst_gtk3/handlers/victory_handlers.py | 2 +- dsst/dsst_gtk3/reload.py | 38 +- dsst/dsst_gtk3/resources/glade/window.glade | 709 +++++++++++------- dsst/dsst_server/func_read.py | 7 +- dsst/dsst_server/func_write.py | 9 + 12 files changed, 582 insertions(+), 390 deletions(-) diff --git a/dsst/common/models.py b/dsst/common/models.py index 9e29bd7..09958a0 100644 --- a/dsst/common/models.py +++ b/dsst/common/models.py @@ -57,6 +57,7 @@ class Death: self.enemy = arg.get('enemy') self.episode = arg.get('episode') self.penalties = arg.get('penalties') + self.time = arg.get('time') class Penalty: diff --git a/dsst/dsst_gtk3/dialogs.py b/dsst/dsst_gtk3/dialogs.py index 3364eba..4642f74 100644 --- a/dsst/dsst_gtk3/dialogs.py +++ b/dsst/dsst_gtk3/dialogs.py @@ -4,7 +4,7 @@ This module contains UI functions for displaying different dialogs import datetime from gi.repository import Gtk from common import models -from dsst_gtk3 import gtk_ui +from dsst_gtk3 import gtk_ui, util def enter_string_dialog(builder: Gtk.Builder, title: str, value=None) -> str: @@ -97,49 +97,68 @@ def edit_episode(app: 'gtk_ui.GtkUi', season_id: int, episode: 'models.Episode'= return episode -def show_edit_death_dialog(builder: Gtk.Builder, episode_id: int, death): - pass - # """Show a dialog for editing or creating death events. - # :param builder: A Gtk.Builder object - # :param episode_id: ID to witch the death event belongs to - # :param death: (Optional) Death event witch should be edited - # :return: Gtk.ResponseType of the dialog - # """ - # dialog = builder.get_object("edit_death_dialog") # type: Gtk.Dialog - # dialog.set_transient_for(builder.get_object("main_window")) - # with sql.db.atomic(): - # if death: - # index = util.get_index_of_combo_model(builder.get_object('edit_death_enemy_combo'), 0, death.enemy.id) - # builder.get_object('edit_death_enemy_combo').set_active(index) - # - # # TODO Default drink should be set in config - # default_drink = sql.Drink.get().name - # store = builder.get_object('player_penalties_store') - # store.clear() - # for player in builder.get_object('episode_players_store'): - # store.append([None, player[1], default_drink, player[0]]) - # - # # Run the dialog - # result = dialog.run() - # dialog.hide() - # if result != Gtk.ResponseType.OK: - # sql.db.rollback() - # return result - # - # # Collect info from widgets and save to database - # player_id = util.get_combo_value(builder.get_object('edit_death_player_combo'), 0) - # enemy_id = util.get_combo_value(builder.get_object('edit_death_enemy_combo'), 3) - # comment = builder.get_object('edit_death_comment_entry').get_text() - # if not death: - # death = sql.Death.create(episode=episode_id, player=player_id, enemy=enemy_id, info=comment) - # - # store = builder.get_object('player_penalties_store') - # size = builder.get_object('edit_death_size_spin').get_value() - # for entry in store: - # drink_id = sql.Drink.get(sql.Drink.name == entry[2]) - # sql.Penalty.create(size=size, player=entry[3], death=death.id, drink=drink_id) - # - # return result +def edit_death(app: 'gtk_ui.GtkUi', death: 'models.Death'=None): + """Show a dialog to create or edit death events for an episode + :param app: Main Gtk application + :param death: (Optional) Existing death object to edit + :return: Death object or None if dialog was canceled + """ + if not death: + death = models.Death() + death.episode = app.get_selected_episode_id() + death.info = "" + death.penalties = [] + death.time = datetime.time(0, 0) + hour_spin = app.ui.get_object('death_hour_spin') + min_spin = app.ui.get_object('death_min_spin') + # Set time of death + hour_spin.set_value(death.time.hour) + min_spin.set_value(death.time.minute) + # Set Enemy + if death.enemy: + index = util.get_index_of_combo_model(app.ui.get_object('edit_death_enemy_combo'), 0, death.enemy.id) + app.ui.get_object('edit_death_enemy_combo').set_active(index) + # Set player + if death.player: + index = util.get_index_of_combo_model(app.ui.get_object('edit_death_player_combo'), 0, death.player.id) + app.ui.get_object('edit_death_player_combo').set_active(index) + # Set shot size + if death.penalties: + app.ui.get_object('edit_death_size_spin').set_value(death.penalties[0].size) + # Set info comment + app.ui.get_object('edit_death_comment_entry').set_text(death.info) + # Set penalties + default_drink = app.drinks.data[0].name + store = app.ui.get_object('player_penalties_store') + store.clear() + if death.penalties: + for penalty in death.penalties: + store.append([penalty.id, penalty.player.name, penalty.drink.name, penalty.player.id]) + else: + for player in app.ui.get_object('episode_players_store'): + store.append([None, player[1], default_drink, player[0]]) + + # Run the dialog + dialog = app.ui.get_object("edit_death_dialog") # type: Gtk.Dialog + result = dialog.run() + dialog.hide() + if result != Gtk.ResponseType.OK: + return None + + # Parse the inputs + death.time = datetime.time(hour_spin.get_value(), min_spin.set_value) + death.enemy = util.get_combo_value(app.ui.get_object('edit_death_enemy_combo'), 3) + death.player = util.get_combo_value(app.ui.get_object('edit_death_player_combo'), 0) + death.info = app.ui.get_object('edit_death_comment_entry').get_text() + store = app.ui.get_object('player_penalties_store') + size = app.ui.get_object('edit_death_size_spin').get_value() + death.penalties.clear() + for entry in store: + drink_id = [drink.id for drink in app.drinks.data if drink.name == entry[2]][0] + penalty = models.Penalty({'id': entry[0], 'size': size, 'drink': drink_id, 'player': entry[3]}) + death.penalties.append(penalty) + + return death def show_edit_victory_dialog(builder: Gtk.Builder, episode_id: int, victory): diff --git a/dsst/dsst_gtk3/gtk_ui.py b/dsst/dsst_gtk3/gtk_ui.py index 61fd257..a54070a 100644 --- a/dsst/dsst_gtk3/gtk_ui.py +++ b/dsst/dsst_gtk3/gtk_ui.py @@ -40,35 +40,32 @@ class GtkUi: self.meta = {'connection': '{}:{}'.format(config.get('host'), config.get('port'))} # Load base data and seasons self.load_server_meta() - self.reload() + self.full_reload() self.update_status_bar_meta() def load_server_meta(self): self.meta['database'] = self.data_client.send_request('load_db_meta') - def reload(self): + def full_reload(self): with util.network_operation(self): - refresh_base = False - if not self.players.valid: - self.players.data = self.data_client.send_request('load_players') - refresh_base = True - if not self.drinks.valid: - self.drinks.data = self.data_client.send_request('load_drinks') - refresh_base= True - if not self.seasons.valid: - self.seasons.data = self.data_client.send_request('load_seasons') - refresh_base = True - if refresh_base: - reload.reload_base_data(self.ui, self) + self.players.data = self.data_client.send_request('load_players') + self.drinks.data = self.data_client.send_request('load_drinks') + self.seasons.data = self.data_client.send_request('load_seasons') + season_id = self.get_selected_season_id() + if season_id: + self.episodes.data = self.data_client.send_request('load_episodes', season_id) + self.season_stats.data = self.data_client.send_request('load_season_stats', season_id) + cur_season = [s for s in self.seasons.data if s.id == season_id][0] + self.enemies.data = cur_season.enemies + reload.rebuild_view_data(self) - if not self.episodes.valid: - with util.network_operation(self): - season_id = self.get_selected_season_id() - if season_id: - self.episodes.data = self.data_client.send_request('load_episodes', season_id) - self.season_stats.data = self.data_client.send_request('load_season_stats', season_id) - reload.reload_episodes(self.ui, self) - reload.reload_season_stats(self) + def reload(self): + pass + + def update_enemy(self, enemy: 'models.Enemy'): + with util.network_operation(self): + self.data_client.send_request('update_enemy', enemy) + self.full_reload() def update_season(self, season: 'models.Season'): with util.network_operation(self): diff --git a/dsst/dsst_gtk3/handlers/base_data_handlers.py b/dsst/dsst_gtk3/handlers/base_data_handlers.py index 9fa56de..eade0e0 100644 --- a/dsst/dsst_gtk3/handlers/base_data_handlers.py +++ b/dsst/dsst_gtk3/handlers/base_data_handlers.py @@ -1,4 +1,4 @@ -from dsst_gtk3 import dialogs +from dsst_gtk3 import dialogs, gtk_ui class BaseDataHandlers: @@ -6,48 +6,42 @@ class BaseDataHandlers: def __init__(self, app: 'gtk_ui.GtkUi'): self.app = app - def do_manage_players(self, *_): - dialogs.run_management_dialog(self.app.ui, 'manage_players_dialog') - def do_add_player(self, entry): if entry.get_text(): # sql.Player.create(name=entry.get_text()) entry.set_text('') - self.app.reload() - - def do_manage_enemies(self, *_): - dialogs.run_management_dialog(self.app.ui, 'manage_enemies_dialog') + self.app.full_reload() def on_player_name_edited(self, _, index, value): row = self.app.ui.get_object('all_players_store')[index] # sql.Player.update(name=value)\ # .where(sql.Player.id == row[0])\ # .execute() - self.app.reload() + self.app.full_reload() def on_player_hex_edited(self, _, index, value): row = self.app.ui.get_object('all_players_store')[index] # sql.Player.update(hex_id=value)\ # .where(sql.Player.id == row[0])\ # .execute() - self.app.reload() + self.app.full_reload() def do_add_drink(self, entry): if entry.get_text(): sql.Drink.create(name=entry.get_text(), vol=0) entry.set_text('') - self.app.reload() + self.app.full_reload() def on_drink_name_edited(self, _, index, value): row = self.app.ui.get_object('drink_store')[index] # sql.Drink.update(name=value)\ # .where(sql.Drink.id == row[0])\ # .execute() - self.app.reload() + self.app.full_reload() def on_drink_vol_edited(self, _, index, value): row = self.app.ui.get_object('drink_store')[index] # sql.Drink.update(vol=value) \ # .where(sql.Drink.id == row[0]) \ # .execute() - self.app.reload() \ No newline at end of file + self.app.full_reload() \ No newline at end of file diff --git a/dsst/dsst_gtk3/handlers/death_handlers.py b/dsst/dsst_gtk3/handlers/death_handlers.py index 410b3a0..3594afd 100644 --- a/dsst/dsst_gtk3/handlers/death_handlers.py +++ b/dsst/dsst_gtk3/handlers/death_handlers.py @@ -1,5 +1,5 @@ from gi.repository import Gtk -from dsst_gtk3 import dialogs +from dsst_gtk3 import dialogs, gtk_ui class DeathHandlers: @@ -11,9 +11,10 @@ class DeathHandlers: ep_id = self.app.get_selected_episode_id() if not ep_id: return - result = dialogs.show_edit_death_dialog(self.app.ui, ep_id) + result = dialogs.edit_death(self.app) if result == Gtk.ResponseType.OK: - self.app.reload() + self.app.episodes.valid = False + self.app.full_reload() def on_penalty_drink_changed(self, _, path, text): self.app.ui.get_object('player_penalties_store')[path][2] = text diff --git a/dsst/dsst_gtk3/handlers/dialog_handlers.py b/dsst/dsst_gtk3/handlers/dialog_handlers.py index a144db3..14ca4ae 100644 --- a/dsst/dsst_gtk3/handlers/dialog_handlers.py +++ b/dsst/dsst_gtk3/handlers/dialog_handlers.py @@ -1,6 +1,7 @@ import datetime -from dsst_gtk3 import dialogs, util, gtk_ui +from dsst_gtk3 import dialogs, util, gtk_ui, reload +from common import models from gi.repository import Gtk @@ -29,10 +30,28 @@ class DialogHandlers: def do_add_enemy(self, entry): if entry.get_text(): store = self.app.ui.get_object('enemy_season_store') - # enemy = sql.Enemy.create(name=entry.get_text(), season=self.app.get_selected_season_id()) - # store.append([enemy.name, False, 0, enemy.id]) + enemy = models.Enemy() + enemy.name = entry.get_text() + enemy.season = self.app.get_selected_season_id() + enemy.boss = not self.app.ui.get_object('enemy_optional_ckeck').get_active() + self.app.ui.get_object('enemy_optional_ckeck').set_active(False) entry.set_text('') + self.app.update_enemy(enemy) + + def on_enemy_name_edited(self, _, index, value): + row = self.app.ui.get_object('enemy_season_store')[index] + enemy = [enemy for enemy in self.app.enemies.data if enemy.id == row[4]][0] + enemy.name = value + self.app.update_enemy(enemy) + + def on_enemy_optional_edited(self, renderer, index): + new_optional_value = not renderer.get_active() + row = self.app.ui.get_object('enemy_season_store')[index] + enemy = [enemy for enemy in self.app.enemies.data if enemy.id == row[4]][0] + enemy.boss = new_optional_value + self.app.update_enemy(enemy) + def do_show_date_picker(self, entry: 'Gtk.Entry', *_): dialog = self.app.ui.get_object('date_picker_dialog') result = dialog.run() diff --git a/dsst/dsst_gtk3/handlers/season_handlers.py b/dsst/dsst_gtk3/handlers/season_handlers.py index e4756cf..c213bb1 100644 --- a/dsst/dsst_gtk3/handlers/season_handlers.py +++ b/dsst/dsst_gtk3/handlers/season_handlers.py @@ -10,12 +10,12 @@ class SeasonHandlers: season = dialogs.edit_season(self.app.ui) if season: self.app.update_season(season) - self.app.reload() + self.app.full_reload() def do_season_selected(self, *_): self.app.episodes.valid = False self.app.season_stats.valid = False - self.app.reload() + self.app.full_reload() def do_add_episode(self, *_): season_id = self.app.get_selected_season_id() @@ -24,7 +24,7 @@ class SeasonHandlers: ep = dialogs.edit_episode(self.app, season_id) if ep: self.app.update_episode(ep) - self.app.reload() + self.app.full_reload() def on_selected_episode_changed(self, *_): reload.reload_episode_stats(self.app) diff --git a/dsst/dsst_gtk3/handlers/victory_handlers.py b/dsst/dsst_gtk3/handlers/victory_handlers.py index 8b1f9a5..6359e2f 100644 --- a/dsst/dsst_gtk3/handlers/victory_handlers.py +++ b/dsst/dsst_gtk3/handlers/victory_handlers.py @@ -13,4 +13,4 @@ class VictoryHandlers: return result = dialogs.show_edit_victory_dialog(self.app.ui, ep_id) if result == Gtk.ResponseType.OK: - self.app.reload() + self.app.full_reload() diff --git a/dsst/dsst_gtk3/reload.py b/dsst/dsst_gtk3/reload.py index 10e330c..89bf92c 100644 --- a/dsst/dsst_gtk3/reload.py +++ b/dsst/dsst_gtk3/reload.py @@ -3,37 +3,38 @@ from gi.repository import Gtk from dsst_gtk3 import util, gtk_ui -def reload_base_data(builder: Gtk.Builder, app: 'gtk_ui.GtkUi',): +def reload_base_data(app: 'gtk_ui.GtkUi',): """Reload function for all base data witch is not dependant on a selected season or episode :param app: GtkUi instance :param builder: Gtk.Builder with loaded UI """ # Rebuild all players store - builder.get_object('all_players_store').clear() + app.ui.get_object('all_players_store').clear() for player in app.players.data: - builder.get_object('all_players_store').append([player.id, player.name, player.hex_id]) + app.ui.get_object('all_players_store').append([player.id, player.name, player.hex_id]) # Rebuild drink store - builder.get_object('drink_store').clear() + app.ui.get_object('drink_store').clear() for drink in app.drinks.data: - builder.get_object('drink_store').append([drink.id, drink.name, '{:.2f}%'.format(drink.vol)]) + app.ui.get_object('drink_store').append([drink.id, drink.name, '{:.2f}%'.format(drink.vol)]) # Rebuild seasons store - combo = builder.get_object('season_combo_box') # type: Gtk.ComboBox + combo = app.ui.get_object('season_combo_box') # type: Gtk.ComboBox active = combo.get_active() with util.block_handler(combo, app.handlers.do_season_selected): - store = builder.get_object('seasons_store') + store = app.ui.get_object('seasons_store') store.clear() for season in app.seasons.data: store.append([season.id, season.game_name]) combo.set_active(active) -def reload_episodes(builder: Gtk.Builder, app: 'gtk_ui.GtkUi'): +def reload_episodes(app: 'gtk_ui.GtkUi'): """Reload all data that is dependant on a selected season :param app: GtkUi instance :param builder: Gtk.Builder with loaded UI """ # Rebuild episodes store - selection = builder.get_object('episodes_tree_view').get_selection() + if not app.get_selected_season_id(): return + selection = app.ui.get_object('episodes_tree_view').get_selection() with util.block_handler(selection, app.handlers.on_selected_episode_changed): model, selected_paths = selection.get_selected_rows() model.clear() @@ -47,6 +48,7 @@ def reload_season_stats(app: 'gtk_ui.GtkUi'): """Load statistic data for selected season :param app: GtkUi instance """ + if not app.season_stats.valid: return season_stats = app.season_stats.data # Load player kill/death data store = app.ui.get_object('player_season_store') @@ -57,15 +59,17 @@ def reload_season_stats(app: 'gtk_ui.GtkUi'): # Load enemy stats for season store = app.ui.get_object('enemy_season_store') store.clear() - for enemy_name, deaths, defeated, boss in season_stats.enemies: - store.append([enemy_name, defeated, deaths, boss]) + for enemy_id, enemy_name, deaths, defeated, boss in season_stats.enemies: + store.append([enemy_name, defeated, deaths, boss, enemy_id]) def reload_episode_stats(app: 'gtk_ui.GtkUi'): """Reload all data that is dependant on a selected episode :param app: app: GtkUi instance """ - episode = [ep for ep in app.episodes.data if ep.id == app.get_selected_episode_id()][0] + ep_id = app.get_selected_episode_id() + if not app.episodes.valid or not ep_id: return + episode = [ep for ep in app.episodes.data if ep.id == ep_id][0] store = app.ui.get_object('episode_players_store') store.clear() for player in episode.players: @@ -106,4 +110,12 @@ def reload_episode_stats(app: 'gtk_ui.GtkUi'): def fill_list_store(store: Gtk.ListStore, models: list): store.clear() for model in models: - pass \ No newline at end of file + pass + + +def rebuild_view_data(app: 'gtk_ui.GtkUi'): + reload_base_data(app) + reload_episodes(app) + reload_episode_stats(app) + reload_season_stats(app) + diff --git a/dsst/dsst_gtk3/resources/glade/window.glade b/dsst/dsst_gtk3/resources/glade/window.glade index 7ae3f77..aede1f1 100644 --- a/dsst/dsst_gtk3/resources/glade/window.glade +++ b/dsst/dsst_gtk3/resources/glade/window.glade @@ -32,6 +32,8 @@ + + @@ -283,6 +285,16 @@ + + 24 + 1 + 1 + + + 60 + 1 + 1 + @@ -1586,292 +1598,6 @@ - - False - Edit Death Event - False - True - dialog - False - main_window - - - False - vertical - 4 - - - False - end - - - gtk-ok - True - True - True - True - - - True - True - 0 - - - - - gtk-cancel - True - True - True - True - - - True - True - 1 - - - - - False - False - 0 - - - - - True - False - vertical - 5 - - - True - False - 5 - - - True - False - 5 - 5 - Enemy - - - False - True - 0 - - - - - True - False - enemy_season_store - - - - 0 - - - - - True - True - end - 1 - - - - - False - True - 0 - - - - - True - False - - - True - False - 5 - 5 - 5 - 5 - Player - - - False - True - 0 - - - - - True - False - episode_players_store - - - - 1 - - - - - True - True - end - 1 - - - - - False - True - 1 - - - - - True - False - - - True - False - 5 - 5 - 5 - 5 - Drink Size - - - False - True - 0 - - - - - True - True - - - False - True - end - 1 - - - - - False - True - 2 - - - - - True - False - - - True - False - Comment - - - False - True - 0 - - - - - True - True - - - True - True - end - 1 - - - - - False - True - 3 - - - - - 100 - True - True - True - player_penalties_store - 0 - - - - - - Player - - - - 1 - - - - - - - Penalty - - - True - False - drink_store - 1 - - - - 2 - - - - - - - True - True - 4 - - - - - True - True - 1 - - - - - - okButtonRename4 - cancelButtonRename4 - - - - - False Edit Episode @@ -2649,6 +2375,20 @@ 1 + + + Optional + True + True + False + True + + + False + True + 2 + + False @@ -2689,14 +2429,31 @@ Name + True - + + True + + 0 + + + Boss + + + + + + 3 + + + + @@ -2891,4 +2648,382 @@ + + 100 + 0.20000000000000001 + 0.20000000000000001 + + + False + Edit Death Event + False + True + dialog + False + main_window + + + False + vertical + 4 + + + False + end + + + gtk-ok + True + True + True + True + + + True + True + 0 + + + + + gtk-cancel + True + True + True + True + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + vertical + 5 + + + True + False + 5 + + + True + False + 5 + 5 + Time + + + False + True + 0 + + + + + True + False + + + True + True + 2 + number + hour_adjustment + True + if-valid + + + False + True + 0 + + + + + True + False + : + + + False + True + 1 + + + + + True + True + 2 + number + minute_adjustment + True + if-valid + + + False + True + 2 + + + + + True + True + end + 1 + + + + + False + True + 0 + + + + + True + False + 5 + + + True + False + 5 + 5 + Enemy + + + False + True + 0 + + + + + True + False + enemy_season_store + + + + 0 + + + + + True + True + end + 1 + + + + + False + True + 1 + + + + + True + False + + + True + False + 5 + 5 + 5 + 5 + Player + + + False + True + 0 + + + + + True + False + episode_players_store + + + + 1 + + + + + True + True + end + 1 + + + + + False + True + 2 + + + + + True + False + + + True + False + 5 + 5 + 5 + 5 + Drink Size + + + False + True + 0 + + + + + True + True + shot_size_adjustment + 1 + True + if-valid + 0.20000000000000001 + + + False + True + end + 1 + + + + + False + True + 3 + + + + + True + False + + + True + False + Comment + + + False + True + 0 + + + + + True + True + + + True + True + end + 1 + + + + + False + True + 4 + + + + + 100 + True + True + True + player_penalties_store + 0 + + + + + + Player + + + + 1 + + + + + + + Penalty + + + True + False + drink_store + 1 + + + + 2 + + + + + + + True + True + 5 + + + + + True + True + 1 + + + + + + okButtonRename4 + cancelButtonRename4 + + + + + diff --git a/dsst/dsst_server/func_read.py b/dsst/dsst_server/func_read.py index 6a95f82..40afa44 100644 --- a/dsst/dsst_server/func_read.py +++ b/dsst/dsst_server/func_read.py @@ -26,6 +26,10 @@ class ReadFunctions: def load_players(*_): return [mapping.db_to_player(player) for player in sql.Player.select()] + @staticmethod + def load_enemies(season_id, *_): + pass + @staticmethod def load_drinks(*_): return [mapping.db_to_drink(drink) for drink in sql.Drink.select()] @@ -39,7 +43,8 @@ class ReadFunctions: sql_func.get_player_victories_for_season(season_id, player.id), sql_func.get_player_deaths_for_season(season_id, player.id)) for player in players] - model.enemies = [(enemy.name, + model.enemies = [(enemy.id, + enemy.name, sql_func.enemy_attempts(enemy.id), sql.Victory.select().where(sql.Victory.enemy == enemy.id).exists(), enemy.boss) diff --git a/dsst/dsst_server/func_write.py b/dsst/dsst_server/func_write.py index 7545d25..925f52d 100644 --- a/dsst/dsst_server/func_write.py +++ b/dsst/dsst_server/func_write.py @@ -7,6 +7,15 @@ class WriteFunctions: def create_season(season: 'models.Season'): return 'Season created.' + @staticmethod + def update_enemy(enemy: 'models.Enemy'): + (sql.Enemy + .insert(id=enemy.id, boss=enemy.boss, name=enemy.name, season=enemy.season) + .on_conflict(update={sql.Enemy.name: enemy.name, + sql.Enemy.boss: enemy.boss, + sql.Enemy.season: enemy.season}) + .execute()) + @staticmethod def update_season(season: 'models.Season', *_): (sql.Season