Additional loading functions.

This commit is contained in:
luxick
2018-03-05 22:57:14 +01:00
parent 25d237e81e
commit 8b0422b1b0
9 changed files with 140 additions and 66 deletions

View File

@@ -8,31 +8,31 @@ except ImportError:
class Access: class Access:
def __init__(self, connection): def __init__(self, conn_dict):
self.host = connection.get('host') self.host = conn_dict.get('host')
self.port = connection.get('port') self.port = conn_dict.get('port')
self.buffer = connection.get('buffer_size') self.buffer = conn_dict.get('buffer_size')
self.auth_key = connection.get('auth_key') self.auth_token = conn_dict.get('auth_token')
self.socket = socket.socket()
def send_request(self, action: str, *args): def send_request(self, action: str, *args):
request = {'auth_key': self.auth_key, request = {'auth_token': self.auth_token,
'action': action, 'action': action,
'args': args} 'args': args}
request = pickle.dumps(request) request = pickle.dumps(request)
soc = socket.socket()
try: try:
self.socket.connect((self.host, self.port)) soc.connect((self.host, self.port))
util.send_msg(self.socket, request) util.send_msg(soc, request)
response = util.recv_msg(self.socket) message = util.recv_msg(soc)
response = pickle.loads(response) message = pickle.loads(message)
if not response.get('success'): if not message.get('success'):
raise Exception(response.get('message')) raise Exception(message.get('message'))
finally: finally:
self.socket.close() soc.close()
return response.get('data') return message.get('data')
if __name__ == '__main__': if __name__ == '__main__':
access = Access({'host': 'europa', 'port': 12345, 'buffer_size': 1024, 'auth_key': 'a'}) access = Access({'host': 'europa', 'port': 12345, 'buffer_size': 1024, 'auth_token': 'a'})
action = 'load_seasons' action = 'load_seasons'
response = access.send_request(action) response = access.send_request(action)
pp = pprint.PrettyPrinter(indent=1) pp = pprint.PrettyPrinter(indent=1)

View File

@@ -5,7 +5,7 @@ import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
from dsst_gtk3.handlers import handlers from dsst_gtk3.handlers import handlers
from dsst_gtk3 import util, reload from dsst_gtk3 import util, reload, client
import sql_func import sql_func
import sql import sql
@@ -26,27 +26,41 @@ class GtkUi:
self.ui.connect_signals(self.handlers) self.ui.connect_signals(self.handlers)
# Show all widgets # Show all widgets
self.ui.get_object('main_window').show_all() self.ui.get_object('main_window').show_all()
db_config = config['sql_connections'][0] # Connect to data server
# Initialize the database config = config['servers'][0]
sql.db.init(db_config['db_name'], host=db_config['host'], port=db_config['port'], self.data_client = client.Access(config)
user=db_config['user'], password=db_config['password']) self.data = {}
# Show database info in status bar # Load base data and seasons
self.set_db_status_label(db_config) self.initial_load()
# Create database if not exists
sql_func.create_tables() def initial_load(self):
self.reload() with util.handle_exception(Exception):
self.data['players'] = self.data_client.send_request('load_players')
self.data['drinks'] = self.data_client.send_request('load_drinks')
self.data['seasons'] = self.data_client.send_request('load_seasons')
reload.reload_base_data(self.ui, self)
def reload(self): def reload(self):
reload.reload_base_data(self.ui, self) with util.handle_exception(Exception):
season_id = self.get_selected_season_id() self.data['episodes'] = self.data_client.send_request('load_episodes', self.get_selected_season_id())
if season_id: reload.reload_episodes(self.ui, self)
reload.reload_episodes(self.ui, self, season_id) pass
reload.reload_season_stats(self.ui, self, season_id) # reload.reload_base_data(self.ui, self)
else: # season_id = self.get_selected_season_id()
return # if season_id:
episode_id = self.get_selected_episode_id() # reload.reload_episodes(self.ui, self, season_id)
if episode_id: # reload.reload_season_stats(self.ui, self, season_id)
reload.reload_episode_stats(self.ui, self, episode_id) # else:
# return
# episode_id = self.get_selected_episode_id()
# if episode_id:
# reload.reload_episode_stats(self.ui, self, episode_id)
def load(self, data_dict: dict, value_field: str, request_name: str):
try:
data_dict[value_field] = self.data_client.send_request('request_name')
except Exception as e:
print()
def set_db_status_label(self, db_conf: dict): def set_db_status_label(self, db_conf: dict):
self.ui.get_object('connection_label').set_text(f'{db_conf["user"]}@{db_conf["host"]}') self.ui.get_object('connection_label').set_text(f'{db_conf["user"]}@{db_conf["host"]}')

View File

@@ -29,7 +29,6 @@ class Handlers(SeasonHandlers, BaseDataHandlers, DialogHandlers, DeathHandlers,
""" Signal will be sent when app should close """ Signal will be sent when app should close
:param _: Arguments to the delete event :param _: Arguments to the delete event
""" """
sql.db.close()
Gtk.main_quit() Gtk.main_quit()
# DEBUG Functions ################################################################################################## # DEBUG Functions ##################################################################################################

View File

@@ -3,21 +3,21 @@ from collections import Counter
from gi.repository import Gtk from gi.repository import Gtk
from data_access import sql, sql_func from data_access import sql, sql_func
from dsst_gtk3 import util from dsst_gtk3 import util, gtk_ui
def reload_base_data(builder: Gtk.Builder, app: 'gtk_ui.GtkUi'): def reload_base_data(builder: Gtk.Builder, app: 'gtk_ui.GtkUi',):
"""Reload function for all base data witch is not dependant on a selected season or episode """Reload function for all base data witch is not dependant on a selected season or episode
:param app: GtkUi instance :param app: GtkUi instance
:param builder: Gtk.Builder with loaded UI :param builder: Gtk.Builder with loaded UI
""" """
# Rebuild all players store # Rebuild all players store
builder.get_object('all_players_store').clear() builder.get_object('all_players_store').clear()
for player in sql.Player.select(): for player in app.data['players']:
builder.get_object('all_players_store').append([player.id, player.name, player.hex_id]) builder.get_object('all_players_store').append([player.id, player.name, player.hex_id])
# Rebuild drink store # Rebuild drink store
builder.get_object('drink_store').clear() builder.get_object('drink_store').clear()
for drink in sql.Drink.select(): for drink in app.data['drinks']:
builder.get_object('drink_store').append([drink.id, drink.name, '{:.2f}%'.format(drink.vol)]) builder.get_object('drink_store').append([drink.id, drink.name, '{:.2f}%'.format(drink.vol)])
# Rebuild seasons store # Rebuild seasons store
combo = builder.get_object('season_combo_box') # type: Gtk.ComboBox combo = builder.get_object('season_combo_box') # type: Gtk.ComboBox
@@ -25,23 +25,22 @@ def reload_base_data(builder: Gtk.Builder, app: 'gtk_ui.GtkUi'):
with util.block_handler(combo, app.handlers.do_season_selected): with util.block_handler(combo, app.handlers.do_season_selected):
store = builder.get_object('seasons_store') store = builder.get_object('seasons_store')
store.clear() store.clear()
for season in sql.Season.select().order_by(sql.Season.number): for season in app.data['seasons']:
store.append([season.id, season.game_name]) store.append([season.id, season.game_name])
combo.set_active(active) combo.set_active(active)
def reload_episodes(builder: Gtk.Builder, app: 'gtk_ui.GtkUi', season_id: int): def reload_episodes(builder: Gtk.Builder, app: 'gtk_ui.GtkUi'):
"""Reload all data that is dependant on a selected season """Reload all data that is dependant on a selected season
:param app: GtkUi instance :param app: GtkUi instance
:param builder: Gtk.Builder with loaded UI :param builder: Gtk.Builder with loaded UI
:param season_id: ID of the season for witch to load data
""" """
# Rebuild episodes store # Rebuild episodes store
selection = builder.get_object('episodes_tree_view').get_selection() selection = builder.get_object('episodes_tree_view').get_selection()
with util.block_handler(selection, app.handlers.on_selected_episode_changed): with util.block_handler(selection, app.handlers.on_selected_episode_changed):
model, selected_paths = selection.get_selected_rows() model, selected_paths = selection.get_selected_rows()
model.clear() model.clear()
for episode in sql_func.get_episodes_for_season(season_id): for episode in app.data['episodes']:
model.append([episode.id, episode.name, str(episode.date), episode.number]) model.append([episode.id, episode.name, str(episode.date), episode.number])
if selected_paths: if selected_paths:
selection.select_path(selected_paths[0]) selection.select_path(selected_paths[0])

View File

@@ -5,18 +5,17 @@ import json
import os import os
from contextlib import contextmanager from contextlib import contextmanager
from gi.repository import Gtk from gi.repository import Gtk
from typing import Callable from typing import Callable, Type
from zipfile import ZipFile from zipfile import ZipFile
CONFIG_PATH = os.path.join(os.path.expanduser('~'), '.config', 'dsst', 'config.json') CONFIG_PATH = os.path.join(os.path.expanduser('~'), '.config', 'dsst', 'config.json')
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
'auto_connect': False, 'auto_connect': False,
'sql_connections': [{ 'servers': [{
'host': 'localhost', 'host': 'localhost',
'port': 3306, 'port': 12345,
'db_name': 'dsst', 'buffer_size': 1024,
'user': 'dsst', 'auth_token': 'a'}
'password': 'dsst'}
] ]
} }
@@ -32,6 +31,17 @@ def block_handler(widget: 'Gtk.Widget', handler_func: Callable):
widget.handler_unblock_by_func(handler_func) widget.handler_unblock_by_func(handler_func)
@contextmanager
def handle_exception(exception: 'Type[Exception]'):
"""Run operation in try/except block and display exception in a dialog
:param exception:
"""
try:
yield
except exception as e:
print(e)
def get_combo_value(combo, index: int): def get_combo_value(combo, index: int):
""" Retrieve the selected value of a combo box at the selected index in the model """ Retrieve the selected value of a combo box at the selected index in the model
:param combo: Any Gtk Widget that supports 'get_active_iter()' :param combo: Any Gtk Widget that supports 'get_active_iter()'

View File

@@ -10,20 +10,46 @@ def map_base_fields(cls, db_model):
return model return model
def db_to_drink(drink: 'sql.Drink'):
return map_base_fields(models.Drink, drink)
def db_to_enemy(enemy: 'sql.Enemy'): def db_to_enemy(enemy: 'sql.Enemy'):
return map_base_fields(models.Enemy, enemy) return map_base_fields(models.Enemy, enemy)
def db_to_player(player: 'sql.Player'): def db_to_player(player: 'sql.Player'):
model = map_base_fields(models.Player, player) return map_base_fields(models.Player, player)
def db_to_penalty(penalty: 'sql.Penalty'):
model = map_base_fields(models.Penalty, penalty)
model.drink = db_to_drink(penalty.drink)
model.player = db_to_player(penalty.player)
return model
def db_to_death(death: 'sql.Death'):
model = map_base_fields(models.Death, death)
model.player = db_to_player(death.player)
model.enemy = db_to_enemy(death.enemy)
model.penalties = [db_to_penalty(penalty) for penalty in death.penalties]
return model
def db_to_victory(victory: 'sql.Victory'):
model = map_base_fields(models.Victory, victory)
model.player = db_to_player(victory.player)
model.enemy = db_to_enemy(victory.enemy)
return model return model
def db_to_episode(episode: 'sql.Episode'): def db_to_episode(episode: 'sql.Episode'):
model = map_base_fields(models.Episode, episode) model = map_base_fields(models.Episode, episode)
model.players = [db_to_player(player) for player in episode.players] model.players = [db_to_player(player) for player in episode.players]
model.deaths = [] model.deaths = [db_to_death(death) for death in episode.deaths]
model.victories = [] model.victories = [db_to_victory(victory) for victory in episode.victories]
return model
def db_to_season(season: 'sql.Season'): def db_to_season(season: 'sql.Season'):

View File

@@ -13,7 +13,16 @@ class ReadFunctions:
def load_seasons_all(*_): def load_seasons_all(*_):
return [shortcuts.model_to_dict(season, backrefs=True, max_depth=2) for season in sql.Season.select()] return [shortcuts.model_to_dict(season, backrefs=True, max_depth=2) for season in sql.Season.select()]
@staticmethod
def load_episodes(season_id, *_):
if not season_id:
raise Exception('Exception: Missing argument (season_id)')
return [mapping.db_to_episode(ep) for ep in sql.Season.get(sql.Season.id == season_id).episodes]
@staticmethod @staticmethod
def load_players(*_): def load_players(*_):
return [mapping.db_to_player(player) for player in sql.Player.select()] return [mapping.db_to_player(player) for player in sql.Player.select()]
@staticmethod
def load_drinks(*_):
return [mapping.db_to_drink(drink) for drink in sql.Drink.select()]

View File

@@ -6,7 +6,7 @@ import sys
import os import os
from common import util, models from common import util, models
from dsst_server import read_functions, write_functions from dsst_server import read_functions, write_functions, tokens
from dsst_server.func_proxy import FunctionProxy from dsst_server.func_proxy import FunctionProxy
from dsst_server.data_access import sql from dsst_server.data_access import sql
@@ -23,12 +23,19 @@ class DsstServer:
self.socket_server.bind((HOST, PORT)) self.socket_server.bind((HOST, PORT))
print(f'Bound socket to {PORT} on host {HOST}') print(f'Bound socket to {PORT} on host {HOST}')
self.read_actions = util.list_class_methods(read_functions.ReadFunctions) # Initialize database
self.write_actions = util.list_class_methods(write_functions.WriteFunctions)
sql.db.init('dsst', user='dsst', password='dsst') sql.db.init('dsst', user='dsst', password='dsst')
print(f'Database initialized ({sql.db.database})')
self.key_access = {'a': self.read_actions, # Load access tokens and map them to their allowed methods
'b': self.read_actions + self.write_actions} read_actions = util.list_class_methods(read_functions.ReadFunctions)
write_actions = util.list_class_methods(write_functions.WriteFunctions)
parm_access = {
'r': read_actions,
'rw': read_actions + write_actions
}
self.tokens = {token: parm_access[perms] for token, perms in tokens.TOKENS}
print(f'Loaded auth tokens: {self.tokens.keys()}')
def run(self): def run(self):
self.socket_server.listen(5) self.socket_server.listen(5)
@@ -41,17 +48,22 @@ class DsstServer:
data = util.recv_msg(client) data = util.recv_msg(client)
request = pickle.loads(data) request = pickle.loads(data)
print(f'Request: {request}') print(f'Request: {request}')
# Validate auth key in request # Validate auth token in request
key = request.get('auth_key') token = request.get('auth_token')
if key not in self.key_access: if token not in self.tokens:
util.send_msg(client, pickle.dumps({'success': False, 'message': 'Auth Key invalid'})) util.send_msg(client, pickle.dumps({'success': False, 'message': 'Auth token invalid'}))
print(f'Rejected request from {address}. Auth key invalid ({key})') print(f'Rejected request from {address}. Auth token invalid ({token})')
continue continue
# Check read functions # Check read functions
action_name = request.get('action') action_name = request.get('action')
if action_name in self.key_access[key]: if action_name in self.tokens[token]:
action = getattr(FunctionProxy, action_name) action = getattr(FunctionProxy, action_name)
value = action(request.get('args')) try:
value = action(request.get('args'))
except Exception as e:
response = {'success': False, 'message': f'Exception was thrown on server.\n{e}'}
util.send_msg(client, pickle.dumps(response))
raise
response = {'success': True, 'data': value} response = {'success': True, 'data': value}
util.send_msg(client, pickle.dumps(response)) util.send_msg(client, pickle.dumps(response))
continue continue

View File

@@ -0,0 +1,5 @@
# Define access tokens here
# i.E: { 'read': ['a', 'b''],
# 'write': ['a']
# }
TOKENS = [('a', 'rw'), ('b', 'r')]