diff --git a/dsst/dsst_core/core.py b/dsst/dsst_core/core.py deleted file mode 100644 index 4097970..0000000 --- a/dsst/dsst_core/core.py +++ /dev/null @@ -1,59 +0,0 @@ -import datetime -from dsst_core import models, sql - - -class DSSTCore: - def __init__(self): - # Set DB Connection - sql.create_tables() - - @staticmethod - def insert_player(model: models.Player): - sql.Player.create(name=model.name, hex_id=model.hex_id) - - @staticmethod - def insert_enemy(enemy: models.Enemy): - sql.Enemy.create(name=enemy.name) - - @staticmethod - def insert_drink(model: models.Drink): - sql.Drink.create(name=model.name, vol=model.vol) - - @staticmethod - def insert_season(season: models.Season): - sql.Season.create(number=season.number, game_name=season.game_name, start_date=season.start_date, - end_date=season.end_date) - - @staticmethod - def insert_episode(episode: models.Episode): - sql.Episode.update() - -if __name__ == '__main__': - core = DSSTCore() - # Insert player - # player = models.Player() - # player.name = 'Marvin' - # core.insert_player(player) - - # Insert a Season - # season = models.Season() - # season.number = 1 - # season.game_name = 'Dark Souls' - # season.start_date = datetime.date(2017, 1, 1) - # core.insert_season(season) - - # core.insert_drink(models.Drink({'name': 'Pfeffi', 'vol': 22.5})) - - # core.insert_enemy(models.Enemy({'name': 'Newton'})) - - # Insert an episode - ep = models.Episode() - ep.date = datetime.date(2017, 2, 5) - ep.number = 1 - ep.seq_number = 1 - ep.players = [models.Player({'id': 1,}), models.Player({'id': 2})] - ep.deaths = [models.Death({ - 'player': 1, - 'enemy': 1, - 'penalty': 1, - })] diff --git a/dsst/dsst_core/models.py b/dsst/dsst_core/models.py deleted file mode 100644 index 882c905..0000000 --- a/dsst/dsst_core/models.py +++ /dev/null @@ -1,63 +0,0 @@ -class Season: - def __init__(self, arg={}): - self.id = arg.get('id') - self.game_name = arg.get('game_name') - self.number = arg.get('number') - self.start_date = arg.get('start_date') - self.end_date = arg.get('end_date') - - self.players = arg.get('players') - self.enemies = arg.get('enemies') - self.episodes = arg.get('episodes') - - -class Episode: - def __init__(self, arg={}): - self.id = arg.get('id') - self.seq_number = arg.get('seq_number') - self.number = arg.get('number') - self.date = arg.get('date') - - self.players = arg.get('players') - self.enemies = arg.get('enemies') - self.deaths = arg.get('deaths') - self.victories = arg.get('victories') - - -class GameEvent: - def __init__(self, arg={}): - self.id = arg.get('id') - self.player = arg.get('player') - self.enemy = arg.get('enemy') - self.info = arg.get('info') - - -class Death(GameEvent): - def __init__(self, arg={}): - GameEvent.__init__(self, arg) - self.penalty = arg.get('penalty') - - -class Victory(GameEvent): - def __init__(self, arg={}): - GameEvent.__init__(self, arg) - - -class Player: - def __init__(self, arg={}): - self.id = arg.get('id') - self.name = arg.get('name') - self.hex_id = arg.get('hex_id') - - -class Enemy: - def __init__(self, arg={}): - self.id = arg.get('id') - self.name = arg.get('name') - - -class Drink: - def __init__(self, arg={}): - self.id = arg.get('id') - self.name = arg.get('name') - self.vol = arg.get('vol') \ No newline at end of file diff --git a/dsst/dsst_gtk3/dialogs.py b/dsst/dsst_gtk3/dialogs.py new file mode 100644 index 0000000..b437dd3 --- /dev/null +++ b/dsst/dsst_gtk3/dialogs.py @@ -0,0 +1,64 @@ +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +from dsst_sql import sql + + +def enter_string_dialog(builder: Gtk.Builder, title: str, value=None) -> str: + """ Simple modal dialog for entering a string value + :param builder: GtkBuilder with loaded dialogs.glade file + :param title: Dialog title + :param value: Pre set value for dialog + :return: + """ + dialog = builder.get_object("nameEnterDialog") # type: Gtk.Dialog + dialog.set_transient_for(builder.get_object("main_window")) + dialog.set_title(title) + entry = builder.get_object("nameEnterEntry") + if value: + entry.set_text(value) + entry.grab_focus() + + result = dialog.run() + dialog.hide() + + if result == Gtk.ResponseType.OK: + return entry.get_text() + else: + return value + + +def show_episode_dialog(builder: Gtk.Builder, title: str, episode: sql.Episode=None): + dialog = builder.get_object("edit_episode_dialog") # type: Gtk.Dialog + dialog.set_transient_for(builder.get_object("main_window")) + dialog.set_title(title) + if episode: + builder.get_object("episode_no_spin_button").set_value(episode.number) + ep_players = sql.Player.select().join(sql.EpisodePlayer).join(sql.Episode).get() + + result = dialog.run() + dialog.hide() + + if result == Gtk.ResponseType.OK: + player_ids = [row[0] for row in builder.get_object('episode_players_store')] + query = sql.EpisodePlayer\ + .delete()\ + .wher(sql.EpisodePlayer.episode == episode.id)\ + .where(sql.EpisodePlayer.player.not_in(player_ids)) + #query = sql.EpisodePlayer.get_or_create(episode=episode.id, player=pl) + + return episode + else: + return None + + +def show_manage_players_dialog(builder: Gtk.Builder, title: str): + dialog = builder.get_object("manage_players_dialog") # type: Gtk.Dialog + dialog.set_transient_for(builder.get_object("main_window")) + dialog.set_title(title) + + result = dialog.run() + dialog.hide() + + if result == Gtk.ResponseType.OK: + pass \ No newline at end of file diff --git a/dsst/dsst_gtk3/gtk_ui.py b/dsst/dsst_gtk3/gtk_ui.py index 8a4b9c8..3406d55 100644 --- a/dsst/dsst_gtk3/gtk_ui.py +++ b/dsst/dsst_gtk3/gtk_ui.py @@ -3,13 +3,40 @@ import os gi.require_version('Gtk', '3.0') from gi.repository import Gtk +from dsst_gtk3.handlers import handlers + +from dsst_sql import sql class DSSTGtkUi: + """ The main UI class """ def __init__(self): + # Load Glade UI files self.ui = Gtk.Builder() self.ui.add_from_file(os.path.join(os.path.dirname(__file__), 'resources', 'glade', 'window.glade')) + self.ui.add_from_file(os.path.join(os.path.dirname(__file__), 'resources', 'glade', 'dialogs.glade')) + # Connect signal handlers to UI + self.handlers = handlers.Handlers(self) + self.ui.connect_signals(self.handlers) + # Show all widgets self.ui.get_object('main_window').show_all() + # Create database if not exists + sql.create_tables() + + self.reload_view_data() + + def reload_view_data(self): + # Rebuild all players store + self.ui.get_object('all_players_store').clear() + for player in sql.Player.select(): + self.ui.get_object('all_players_store').append([player.id, player.name, player.hex_id]) + # Rebuild seasons store + store = self.ui.get_object('seasons_store') + store.clear() + for season in sql.Season.select().order_by(sql.Season.number): + store.append([season.id, season.game_name]) + + if __name__ == '__main__': DSSTGtkUi() diff --git a/dsst/dsst_core/__init__.py b/dsst/dsst_gtk3/handlers/__init__.py similarity index 100% rename from dsst/dsst_core/__init__.py rename to dsst/dsst_gtk3/handlers/__init__.py diff --git a/dsst/dsst_gtk3/handlers/handlers.py b/dsst/dsst_gtk3/handlers/handlers.py new file mode 100644 index 0000000..4108b0a --- /dev/null +++ b/dsst/dsst_gtk3/handlers/handlers.py @@ -0,0 +1,24 @@ +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +from dsst_gtk3.handlers.season_and_episodes import SeasonAndEpisodesHandlers +from dsst_gtk3.handlers.players import PlayerHandlers + + +class Handlers(SeasonAndEpisodesHandlers, PlayerHandlers): + """ Class containing all signal handlers for the GTK GUI """ + def __init__(self, app): + """ Initialize handler class + :param app: reference to the main application object + """ + self.app = app + # Call constructors of superclasses + SeasonAndEpisodesHandlers.__init__(self, app) + PlayerHandlers.__init__(self, app) + + @staticmethod + def do_delete_event(*args): + """ Signal will be sent when app should close + :param args: Arguments to the delete event + """ + Gtk.main_quit() \ No newline at end of file diff --git a/dsst/dsst_gtk3/handlers/players.py b/dsst/dsst_gtk3/handlers/players.py new file mode 100644 index 0000000..87a555c --- /dev/null +++ b/dsst/dsst_gtk3/handlers/players.py @@ -0,0 +1,27 @@ +from dsst_gtk3.gtk_ui import DSSTGtkUi +from dsst_gtk3 import dialogs, util +from dsst_sql import sql + + +class PlayerHandlers: + def __init__(self, app: DSSTGtkUi): + self.app = app + + def do_manage_players(self, *_): + result = dialogs.show_manage_players_dialog(self.app.ui, 'Manage Players') + + def do_add_player(self, entry): + if entry.get_text(): + sql.Player.create(name=entry.get_text()) + entry.set_text('') + self.app.reload_view_data() + + def do_add_player_to_episode(self, combo): + player_id = util.Util.get_combo_value(combo, 0) + if player_id: + self.app.ui.get_object('add_player_combo_box').set_active(-1) + player = sql.Player.get(sql.Player.id == player_id) + store = self.app.ui.get_object('episode_players_store') + if not any(row[0] == player_id for row in store): + store.append([player_id, player.name, player.hex_id]) + diff --git a/dsst/dsst_gtk3/handlers/season_and_episodes.py b/dsst/dsst_gtk3/handlers/season_and_episodes.py new file mode 100644 index 0000000..5a2eff4 --- /dev/null +++ b/dsst/dsst_gtk3/handlers/season_and_episodes.py @@ -0,0 +1,26 @@ +from dsst_gtk3.gtk_ui import DSSTGtkUi +from dsst_gtk3.util import Util +from dsst_sql import sql +from dsst_gtk3 import dialogs + + +class SeasonAndEpisodesHandlers: + def __init__(self, app: DSSTGtkUi): + self.app = app + + def do_add_season(self, *_): + name = dialogs.enter_string_dialog(self.app.ui, 'Name for the new Season') + if name: + sql.Season.create(game_name=name, number=1) + self.app.reload_view_data() + + def do_season_selected(self, *_): + combo = self.app.ui.get_object('season_combo_box') + season_id = Util.get_combo_value(combo, 0) + if not season_id: return + season = sql.Season.get(sql.Season.id == season_id) + for episode in season.episodes: + print(episode) + + def do_add_episode(self, *_): + episode = dialogs.show_episode_dialog(self.app.ui, 'Create new Episode') diff --git a/dsst/dsst_gtk3/resources/glade/dialogs.glade b/dsst/dsst_gtk3/resources/glade/dialogs.glade new file mode 100644 index 0000000..777a898 --- /dev/null +++ b/dsst/dsst_gtk3/resources/glade/dialogs.glade @@ -0,0 +1,710 @@ + + + + + + + + + + + + + + + + False + False + True + dialog + False + + + 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 + 5 + 5 + Add Player + + + False + True + 0 + + + + + True + True + + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 5 + 5 + 5 + 5 + All Players + 0 + + + False + True + 1 + + + + + True + True + True + all_players_store + + + + + + Name + + + + 1 + + + + + + + Hex ID + + + + 2 + + + + + + + False + True + 2 + + + + + True + True + 1 + + + + + + okButtonRename2 + cancelButtonRename2 + + + + + + + all_players_store + 1 + True + True + + + + 1 + + + + + 1000000 + 1 + 10 + + + + + + + + + + + + + False + False + True + dialog + False + + + 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 + 5 + + + True + False + vertical + 5 + + + True + False + + + True + False + Episode No. + + + False + True + 0 + + + + + True + True + digits + ep_number_ajustment + 1 + True + True + + + False + True + end + 1 + + + + + False + True + 0 + + + + + True + False + + + True + False + Player + + + False + True + 0 + + + + + True + False + all_players_store + 1 + 1 + + + + 1 + + + + + True + True + 1 + + + + + Add + True + True + True + + + + False + True + end + 2 + + + + + False + True + 1 + + + + + True + True + True + episode_players_store + + + + + + Name + + + + 1 + + + + + + + Hex ID + + + + 2 + + + + + + + False + True + 2 + + + + + False + True + 0 + + + + + True + False + vertical + + + True + False + 5 + 5 + 5 + 5 + Episode Date + 0 + + + False + True + 0 + + + + + True + True + 2018 + 1 + 22 + + + False + True + 1 + + + + + False + True + 1 + + + + + False + True + 1 + + + + + + button1 + button2 + + + + + + + False + False + True + dialog + False + + + 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 + True + + + False + True + 1 + + + + + + okButtonRename + cancelButtonRename + + + + + + + False + False + dialog + False + + + False + vertical + 15 + + + False + end + + + gtk-yes + True + True + True + True + + + True + True + 0 + + + + + gtk-no + True + True + True + True + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + 2 + 2 + 2 + 2 + center + + + False + True + 1 + + + + + + yn_yes + yn_no + + + + + + + False + False + dialog + False + + + False + vertical + 15 + + + False + end + + + gtk-yes + True + True + True + True + + + True + True + 0 + + + + + gtk-no + True + True + True + True + + + True + True + 1 + + + + + gtk-cancel + True + True + True + True + + + True + True + 2 + + + + + False + False + 0 + + + + + True + False + 2 + 2 + 2 + 2 + center + + + False + True + 1 + + + + + + ync_yes + ync_no + ync_cancel + + + + + + diff --git a/dsst/dsst_gtk3/resources/glade/window.glade b/dsst/dsst_gtk3/resources/glade/window.glade index 391b040..52cdf1f 100644 --- a/dsst/dsst_gtk3/resources/glade/window.glade +++ b/dsst/dsst_gtk3/resources/glade/window.glade @@ -26,236 +26,149 @@ Drunk Souls Stat Tool 1200 700 + - + True - True - True + False + vertical - - 150 + + menuBar True False - vertical - 5 - + True False - 5 - 5 - 5 - 5 - Select Season - 0 - - - False - True - 0 - - - - - True - False - seasons_store - 1 - - - False - True - 1 - - - - - True - False - 5 - 5 - 5 - 5 - Episodes - 0 - - - False - True - 2 - - - - - True - True - episodes_store - - - - - - autosize - Episode + Season + + + mainMenu + True + False - - - 1 - + + True + False + Add Season + True + + + + + + True + False + + + + + True + False + Manage Enemies + True + + + + + True + False + + + + + True + False + Manage Seasons + True + + + + + + + + + True + False + Players + True + + + True + False + + + True + False + Manage Players + True + + - - True - True - 3 - - False - True + False + True + 0 True True - - - True - True - True - True - - - True - False - vertical - - - - - - - - True - False - Stats - - - False - - - - - True - False - vertical - - - - - - 1 - - - - - True - False - Deaths - - - 1 - False - - - - - True - False - vertical - - - - - - 2 - - - - - True - False - Victories - - - 2 - False - - - - - True - True - - + True + 150 True False - True - True vertical + 5 - + True False - 5 - 5 - 5 - 5 - Players - 0 - - - False - True - 0 - - - - - True - True - True - player_store - - + + + True + False + 5 + 5 + 5 + 5 + Select Season + 0 + + + False + True + 0 + - - Name - - - - 0 - - - - - - - Hex ID - - - - 1 - - + + New + True + True + True + 5 + 5 + 5 + 5 + + + False + True + end + 1 + @@ -265,15 +178,24 @@ - + True False - 5 - 5 - 5 - 5 - Drinks - 0 + seasons_store + 1 + + + + + 0 + + + + + + 1 + + False @@ -282,35 +204,44 @@ - + True - True - True - drink_store - - + False + + + True + False + 5 + 5 + 5 + 5 + Episodes + 0 + + + False + True + 0 + - - Name - - - - 0 - - - - - - - Vol. - - - - 1 - - + + New + True + True + True + 5 + 5 + 5 + 5 + + + False + True + end + 1 + @@ -320,47 +251,287 @@ - - True - False - 5 - 5 - 5 - 5 - Enemies - 0 - - - False - True - 4 - - - - + True True - True - enemy_store + episodes_store - Name + autosize + Episode - 0 + 1 - False + True True - 5 + 4 + + + + + False + True + + + + + True + True + + + True + True + True + True + + + True + False + vertical + + + + + + + + True + False + Season Stats + + + False + + + + + True + False + vertical + + + + + + 1 + + + + + True + False + Episode Deaths + + + 1 + False + + + + + True + False + vertical + + + + + + 2 + + + + + True + False + Episode Victories + + + 2 + False + + + + + True + True + + + + + True + False + True + True + vertical + + + True + False + 5 + 5 + 5 + 5 + Players + 0 + + + False + True + 0 + + + + + True + True + True + player_store + + + + + + Name + + + + 0 + + + + + + + Hex ID + + + + 1 + + + + + + + False + True + 1 + + + + + True + False + 5 + 5 + 5 + 5 + Drinks + 0 + + + False + True + 2 + + + + + True + True + True + drink_store + + + + + + Name + + + + 0 + + + + + + + Vol. + + + + 1 + + + + + + + False + True + 3 + + + + + True + False + 5 + 5 + 5 + 5 + Enemies + 0 + + + False + True + 4 + + + + + True + True + True + enemy_store + + + + + + Name + + + + 0 + + + + + + + False + True + 5 + + + + + True + True @@ -371,13 +542,14 @@ - True - True + False + True + 1 - + diff --git a/dsst/dsst_gtk3/util.py b/dsst/dsst_gtk3/util.py new file mode 100644 index 0000000..7e9ef9c --- /dev/null +++ b/dsst/dsst_gtk3/util.py @@ -0,0 +1,9 @@ +class Util: + @staticmethod + def get_combo_value(combo, index: int): + """ Retrieve the selected value of a combo box at the selected index in the model """ + tree_iter = combo.get_active_iter() + if tree_iter: + return combo.get_model().get_value(tree_iter, index) + else: + return None diff --git a/dsst/dsst_sql/__init__.py b/dsst/dsst_sql/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dsst/dsst_sql/core.py b/dsst/dsst_sql/core.py new file mode 100644 index 0000000..bdbbf36 --- /dev/null +++ b/dsst/dsst_sql/core.py @@ -0,0 +1,56 @@ +from dsst_sql import models, sql + + +class DSSTCore: + def __init__(self): + # Set DB Connection + sql.create_tables() + + @staticmethod + def insert_player(model: models.Player): + return sql.Player.create(name=model.name, hex_id=model.hex_id) + + @staticmethod + def insert_enemy(enemy: models.Enemy): + return sql.Enemy.create(name=enemy.name) + + @staticmethod + def insert_drink(model: models.Drink): + return sql.Drink.create(name=model.name, vol=model.vol) + + @staticmethod + def insert_season(season: models.Season): + return sql.Season.create(number=season.number, game_name=season.game_name, start_date=season.start_date, + end_date=season.end_date) + + @staticmethod + def insert_death(death: models.Death): + return sql.Death.create(info=death.info, player=death.player.id, enemy=death.enemy.id, penalty=death.penalty.id) + + @staticmethod + def insert_victory(victory: models.Victory): + return sql.Death.create(info=victory.info, player=victory.player.id, enemy=victory.enemy.id) + + @staticmethod + def insert_episode(season_id: int, episode: models.Episode): + with sql.connection.atomic(): + # Insert Episode Row + new_ep = sql.Episode.create(seq_number=episode.seq_number, number=episode.number, date=episode.date, + season=season_id) + # Insert participating players + for player in episode.players: + sql.EpisodePlayer.insert(episode=new_ep.id, player=player.id) + # Insert deaths in this episode + if episode.deaths: + for death in episode.deaths: + new_death = DSSTCore.insert_death(death) + sql.EpisodeDeath.create(death=new_death.id, episode=new_ep.id) + # Insert victories in this episode + if episode.victories: + for victory in episode.victories: + new_vic = DSSTCore.insert_victory(victory) + sql.EpisodeVictory.create(victory=new_vic.id, episode=new_ep.id) + + +if __name__ == '__main__': + core = DSSTCore() \ No newline at end of file diff --git a/dsst/dsst_core/sql.py b/dsst/dsst_sql/sql.py similarity index 89% rename from dsst/dsst_core/sql.py rename to dsst/dsst_sql/sql.py index 19b9a77..8eb29ca 100644 --- a/dsst/dsst_core/sql.py +++ b/dsst/dsst_sql/sql.py @@ -19,6 +19,7 @@ class Episode(Model): seq_number = IntegerField() number = CharField() date = DateTimeField(null=True) + season = ForeignKeyField(Season, backref='episodes') class Meta: database = connection @@ -71,18 +72,9 @@ class Victory(Model): database = connection -class SeasonEpisode(Model): - id = AutoField() - season = ForeignKeyField(Season) - episode = ForeignKeyField(Episode) - - class Meta: - database = connection - - class EpisodePlayer(Model): id = AutoField() - episode = ForeignKeyField(Episode,) + episode = ForeignKeyField(Episode) player = ForeignKeyField(Player) class Meta: @@ -108,7 +100,7 @@ class EpisodeVictory(Model): def create_tables(): - models = [Season, Episode, Player, Drink, Enemy, Death, Victory, SeasonEpisode, EpisodePlayer, EpisodeDeath, + models = [Season, Episode, Player, Drink, Enemy, Death, Victory, EpisodePlayer, EpisodeDeath, EpisodeVictory] for model in models: model.create_table()