summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bidding_data.py43
-rw-r--r--src/bidding_data_gui.py221
2 files changed, 153 insertions, 111 deletions
diff --git a/src/bidding_data.py b/src/bidding_data.py
index 3b277bb..739df96 100644
--- a/src/bidding_data.py
+++ b/src/bidding_data.py
@@ -5,14 +5,15 @@ Utility to insert HTML tables with bidding data into traveller files generated
by JFR Pary.
"""
-import sys
import glob
+import logging as log
import re
import socket
+import sys
+from os import path, remove
+
import pypyodbc
-import logging as log
-from os import path, remove
from bs4 import BeautifulSoup as bs4
__version__ = '1.0.3'
@@ -356,6 +357,9 @@ class JFRBidding(object):
# all generated bidding table files, for cleanup purposes
__bidding_files = []
+ # configuration for Goniec
+ __goniec = {'host': None, 'port': None}
+
def __init__(self, bws_file, file_prefix, goniec_setup=None):
"""Construct parser object."""
log.getLogger('init').debug('reading BWS file: %s', bws_file)
@@ -382,13 +386,12 @@ class JFRBidding(object):
log.getLogger('init').debug('tournament files pattern: %s',
self.__tournament_files_match.pattern)
self.__map_board_numbers()
- self.__goniec_host = None
if goniec_setup is not None:
setup_parts = goniec_setup.split(':')
- self.__goniec_host = setup_parts[0] \
- if len(setup_parts) > 0 else 'localhost'
- self.__goniec_port = int(setup_parts[1]) \
- if len(setup_parts) > 1 else 8090
+ self.__goniec['host'] = setup_parts[0] if len(setup_parts) > 0 \
+ else 'localhost'
+ self.__goniec['port'] = int(setup_parts[1]) \
+ if len(setup_parts) > 1 else 8090
def write_bidding_tables(self):
"""Iterate over bidding and writes tables to HTML files."""
@@ -505,7 +508,8 @@ class JFRBidding(object):
return used_board_files
def send_changed_files(self, files_to_send):
- if self.__goniec_host is not None:
+ """Send specified files from working directory via Goniec."""
+ if self.__goniec['host'] is not None:
working_directory = path.dirname(self.__tournament_prefix) \
+ path.sep
files_to_send = [file_to_send.replace(working_directory, '', 1)
@@ -513,23 +517,26 @@ class JFRBidding(object):
if file_to_send.startswith(working_directory)]
try:
goniec_socket = socket.socket()
- goniec_socket.connect((self.__goniec_host, self.__goniec_port))
+ goniec_socket.connect((
+ self.__goniec['host'],
+ self.__goniec['port']))
log.getLogger('goniec').info(
- 'connected to Goniec at %s:%d' \
- % (self.__goniec_host, self.__goniec_port))
- content_lines = [working_directory] + files_to_send + ['bye', '']
+ 'connected to Goniec at %s:%d',
+ self.__goniec['host'], self.__goniec['port'])
+ content_lines = [working_directory] + \
+ files_to_send + ['bye', '']
goniec_socket.sendall('\n'.join(
[line.encode(sys.getfilesystemencoding())
for line in content_lines]))
log.getLogger('goniec').info(
- 'working directory is: %s' % working_directory)
+ 'working directory is: %s', working_directory)
goniec_socket.close()
for file_sent in files_to_send:
log.getLogger('goniec').info(
- 'sent file to Goniec: %s' % file_sent)
- except socket.error as er:
+ 'sent file to Goniec: %s', file_sent)
+ except socket.error as err:
log.getLogger('goniec').error(
- 'unable to connect to Goniec: %s' % er)
+ 'unable to connect to Goniec: %s', err)
def main():
@@ -611,7 +618,7 @@ def main():
all_files += bidding_parser.write_bidding_links()
bidding_parser.send_changed_files(all_files)
except Exception as ex:
- #log.getLogger('root').error(ex.strerror)
+ log.getLogger('root').error(ex)
raise
log.info('--------- program ended ---------')
diff --git a/src/bidding_data_gui.py b/src/bidding_data_gui.py
index 5ad2647..3a49b83 100644
--- a/src/bidding_data_gui.py
+++ b/src/bidding_data_gui.py
@@ -7,27 +7,31 @@ Graphical user interface to insert HTML tables with bidding data into traveller
files generated by JFR Pary.
"""
-import Tkinter as tk
-import tkFileDialog
-import tkMessageBox
-import Queue
-
+import json
import logging as log
import os
+import Queue
import socket
-import json
import threading
+import tkFileDialog
+import tkMessageBox
+
+import Tkinter as tk
from bidding_data import __version__ as bidding_data_version
+# config file path
+CONFIG_FILE = 'config.json'
+
+
class BiddingGUI(tk.Frame):
"""GUI frame class."""
- # Tk variable to store tournament result file path
- __tour_filename = None
- # Tk variable to store BWS file path
- __bws_filename = None
+ # Tk variables to bind widget values
+ __variables = {}
+ # widgets which are toggled in Goniec settings panel
+ __goniec_widgets = []
def run_bidding_data(self):
"""
@@ -43,28 +47,28 @@ class BiddingGUI(tk.Frame):
self.__gui_logger.reset_counts()
# check for input parameter paths
- if not os.path.exists(self.__bws_filename.get()):
+ if not os.path.exists(self.__variables['bws_filename'].get()):
raise Exception('BWS file not found')
- if not os.path.exists(self.__tour_filename.get()):
+ if not os.path.exists(self.__variables['tour_filename'].get()):
raise Exception('Tournament results file not found')
# Goniec parameters/switches
- goniec_params = '%s:%d' % (self.__goniec_host.get(),
- self.__goniec_port.get()) \
- if self.__goniec_enabled.get() == 1 \
- else None
+ goniec_params = '%s:%d' % (
+ self.__variables['goniec_host'].get(),
+ self.__variables['goniec_port'].get()
+ ) if self.__variables['goniec_enabled'].get() == 1 else None
# do the magic
from bidding_data import JFRBidding
parser = JFRBidding(
- bws_file=self.__bws_filename.get(),
- file_prefix=self.__tour_filename.get(),
+ bws_file=self.__variables['bws_filename'].get(),
+ file_prefix=self.__variables['tour_filename'].get(),
goniec_setup=goniec_params)
changed_files = []
changed_files += parser.write_bidding_tables()
changed_files += parser.write_bidding_scripts()
changed_files += parser.write_bidding_links()
- if self.__goniec_enabled.get() == 1:
+ if self.__variables['goniec_enabled'].get() == 1:
parser.send_changed_files(changed_files)
# inform of any warnings/errors that might have occuerd
@@ -103,7 +107,7 @@ class BiddingGUI(tk.Frame):
Displays file selection dialog for tournament file and stores user's
choice in Tk variable.
"""
- self.__tour_filename.set(tkFileDialog.askopenfilename(
+ self.__variables['tour_filename'].set(tkFileDialog.askopenfilename(
title='Wybierz główny plik wyników turnieju',
filetypes=[('HTML files', '.htm*'), ('all files', '.*')]))
@@ -115,7 +119,7 @@ class BiddingGUI(tk.Frame):
Displays file selection dialog for tournament file and stores user's
choice in Tk variable.
"""
- self.__bws_filename.set(tkFileDialog.askopenfilename(
+ self.__variables['bws_filename'].set(tkFileDialog.askopenfilename(
title='Wybierz plik z danymi licytacji',
filetypes=[('BWS files', '.bws'), ('all files', '.*')]))
@@ -132,38 +136,39 @@ class BiddingGUI(tk.Frame):
def toggle_goniec(self):
"""Toggle state for Goniec-related controls on Goniec switch toggle."""
- for control in [
- self.goniec_host_label, self.goniec_host_field,
- self.goniec_port_label, self.goniec_port_field,
- self.goniec_test_btn]:
- self.queue(control.__setitem__, 'state', tk.NORMAL
- if self.__goniec_enabled.get() == 1 else tk.DISABLED)
+ for control in self.__goniec_widgets:
+ self.queue(
+ control.__setitem__, 'state',
+ tk.NORMAL if self.__variables['goniec_enabled'].get() == 1
+ else tk.DISABLED
+ )
def test_goniec(self):
"""Test connectivity with Goniec and display a message accordingly."""
goniec_socket = socket.socket()
try:
- goniec_socket.connect((self.__goniec_host.get(),
- self.__goniec_port.get()))
+ goniec_socket.connect((self.__variables['goniec_host'].get(),
+ self.__variables['goniec_port'].get()))
goniec_socket.close()
self.queue(
tkMessageBox.showinfo, 'Hurra!',
'Goniec - albo coś, co go udaje - działa!')
- except socket.error as err:
+ except socket.error:
self.queue(
tkMessageBox.showerror, 'Buuu...',
'Pod podanym adresem Goniec nie działa :(')
except (ValueError, OverflowError):
self.queue(
tkMessageBox.showerror, 'Buuu...',
- 'Parametry Gońca mają niewłaściwy format, czemu mi to robisz :(')
+ 'Parametry Gońca mają niewłaściwy format, ' +
+ 'czemu mi to robisz :(')
def on_close(self):
- """Handles root window WM_DELETE_WINDOW message."""
+ """Handle root window WM_DELETE_WINDOW message."""
try:
self.__store_config()
- except:
- log.getLogger('config').error('Could not save config file')
+ except (ValueError, TypeError, OverflowError) as ex:
+ log.getLogger('config').error('Could not save config file: %s', ex)
self.master.destroy()
# GUI message queue (for background thread interaction)
@@ -194,17 +199,22 @@ class BiddingGUI(tk.Frame):
Initializes window appearence, controls, layout and logging facility.
"""
tk.Frame.__init__(self, master)
- # bind Tk variables to input parameter paths
- self.__tour_filename = tk.StringVar(master=self)
- self.__bws_filename = tk.StringVar(master=self)
- # and to Goniec parameters
- self.__goniec_host = tk.StringVar(master=self)
- self.__goniec_port = tk.IntVar(master=self)
- # "boolean" variable to hold chackbox state
- self.__goniec_enabled = tk.IntVar(master=self)
+
+ self.__variables = {
+ # bind Tk variables to input parameter paths
+ 'tour_filename': tk.StringVar(master=self),
+ 'bws_filename': tk.StringVar(master=self),
+ # and to Goniec parameters
+ 'goniec_host': tk.StringVar(master=self),
+ 'goniec_port': tk.IntVar(master=self),
+ # "boolean" variable to hold chackbox state
+ 'goniec_enabled': tk.IntVar(master=self)
+ }
+
# set window title and icon
self.master.title('JBBD - JFR/BWS bidding data')
self.__set_icon(self.__icon_data)
+
# create controls
self.__create_widgets()
# and align them within a layout
@@ -212,8 +222,10 @@ class BiddingGUI(tk.Frame):
self.__configure_grid_cells([1], [5])
# main frame should fill entire application window
self.pack(expand=1, fill=tk.BOTH)
+
# finally, set logging up
self.__configure_logging()
+
# default config values
self.__default_config = {
'paths': {
@@ -226,12 +238,13 @@ class BiddingGUI(tk.Frame):
'port': 8090
}
}
- # config file path
- self.__config_file = 'config.json'
+
# restore config from file
self.__restore_config()
+
# register on-close hook
self.master.protocol("WM_DELETE_WINDOW", self.on_close)
+
# fire up interthread queue
self.after(100, self.process_queue)
@@ -266,7 +279,8 @@ class BiddingGUI(tk.Frame):
self, text='Plik turnieju:')
# text field for tournament file path
tour_entry = tk.Entry(
- self, state=tk.DISABLED, textvariable=self.__tour_filename)
+ self, state=tk.DISABLED,
+ textvariable=self.__variables['tour_filename'])
# tournament selection button
tour_select_btn = tk.Button(
self, text='Szukaj', command=self.tour_select)
@@ -280,7 +294,8 @@ class BiddingGUI(tk.Frame):
self, text='BWS:')
# text field for BWS file path
bws_entry = tk.Entry(
- self, state=tk.DISABLED, textvariable=self.__bws_filename)
+ self, state=tk.DISABLED,
+ textvariable=self.__variables['bws_filename'])
# BWS selection button
bws_select_btn = tk.Button(
self, text='Szukaj', command=self.bws_select)
@@ -307,63 +322,72 @@ class BiddingGUI(tk.Frame):
# fourth row, rightmost 1/3 of window width
quit_btn.grid(row=3, column=4, columnspan=3, sticky=tk.E+tk.W)
+ self.__create_goniec_widgets()
+
+ # vertical scrollbar for log output field
+ log_scroll_y = tk.Scrollbar(self, orient=tk.VERTICAL)
+ # horizontal scrollbar for log output field
+ log_scroll_x = tk.Scrollbar(self, orient=tk.HORIZONTAL)
+ # log field, bound (both ways) to scrollbars
+ self.log_field = tk.Text(
+ self, height=5, width=80, wrap=tk.NONE,
+ xscrollcommand=log_scroll_x.set,
+ yscrollcommand=log_scroll_y.set)
+ log_scroll_x['command'] = self.log_field.xview
+ log_scroll_y['command'] = self.log_field.yview
+ # fifth row, entries window width, expands with window
+ self.log_field.grid(
+ row=5, column=0, columnspan=6, sticky=tk.N+tk.S+tk.E+tk.W)
+ # scrollbars to the right and to the bottom of the field
+ log_scroll_y.grid(row=5, column=6, sticky=tk.N+tk.S)
+ log_scroll_x.grid(row=6, column=0, columnspan=6, sticky=tk.E+tk.W)
+
+ def __create_goniec_widgets(self):
# Goniec toggle checkbox
- self.goniec_checkbox = tk.Checkbutton(
+ goniec_checkbox = tk.Checkbutton(
self, text='Ślij Gońcem',
command=self.toggle_goniec,
- variable=self.__goniec_enabled)
+ variable=self.__variables['goniec_enabled'])
# fifth row, leftmost column
- self.goniec_checkbox.grid(
+ goniec_checkbox.grid(
row=4, column=0)
# label for Goniec host entry field
- self.goniec_host_label = tk.Label(
+ goniec_host_label = tk.Label(
self, text='Host:')
# fifth row, second column, aligned to the right
- self.goniec_host_label.grid(
+ goniec_host_label.grid(
row=4, column=1, sticky=tk.E)
# Goniec host entry field
- self.goniec_host_field = tk.Entry(
- self, textvariable=self.__goniec_host)
+ goniec_host_field = tk.Entry(
+ self, textvariable=self.__variables['goniec_host'])
# fifth row, third column, aligned to the left
- self.goniec_host_field.grid(
+ goniec_host_field.grid(
row=4, column=2, sticky=tk.W)
# label for Goniec port entry field
- self.goniec_port_label = tk.Label(
+ goniec_port_label = tk.Label(
self, text='Port:')
# fifth row, fourth column, aligned to the right
- self.goniec_port_label.grid(
+ goniec_port_label.grid(
row=4, column=3, sticky=tk.E)
# Goniec port entry field
- self.goniec_port_field = tk.Entry(
- self, textvariable=self.__goniec_port)
+ goniec_port_field = tk.Entry(
+ self, textvariable=self.__variables['goniec_port'])
# fifth row, fifth column, aligned to the left
- self.goniec_port_field.grid(
+ goniec_port_field.grid(
row=4, column=4, sticky=tk.W)
# Goniec test button
- self.goniec_test_btn = tk.Button(
+ goniec_test_btn = tk.Button(
self, text='Test Gońca',
command=self.test_goniec)
# fifth row, rightmost column
- self.goniec_test_btn.grid(
+ goniec_test_btn.grid(
row=4, column=5)
- # vertical scrollbar for log output field
- log_scroll_y = tk.Scrollbar(self, orient=tk.VERTICAL)
- # horizontal scrollbar for log output field
- log_scroll_x = tk.Scrollbar(self, orient=tk.HORIZONTAL)
- # log field, bound (both ways) to scrollbars
- self.log_field = tk.Text(
- self, height=5, width=80, wrap=tk.NONE,
- xscrollcommand=log_scroll_x.set,
- yscrollcommand=log_scroll_y.set)
- log_scroll_x['command'] = self.log_field.xview
- log_scroll_y['command'] = self.log_field.yview
- # fifth row, entries window width, expands with window
- self.log_field.grid(
- row=5, column=0, columnspan=6, sticky=tk.N+tk.S+tk.E+tk.W)
- # scrollbars to the right and to the bottom of the field
- log_scroll_y.grid(row=5, column=6, sticky=tk.N+tk.S)
- log_scroll_x.grid(row=6, column=0, columnspan=6, sticky=tk.E+tk.W)
+ # aggregate all widgets for which goniec_checkbox toggles status
+ self.__goniec_widgets = [
+ goniec_host_label, goniec_host_field,
+ goniec_port_label, goniec_port_field,
+ goniec_test_btn]
def __configure_logging(self):
"""Set up logging facility, bound to log output field."""
@@ -426,30 +450,41 @@ class BiddingGUI(tk.Frame):
def __restore_config(self):
"""Read config from JSON file."""
try:
- if os.path.exists(self.__config_file):
- self.__default_config = json.load(file(self.__config_file))
+ if os.path.exists(CONFIG_FILE):
+ self.__default_config = json.load(file(CONFIG_FILE))
else:
log.getLogger('config').info(
'Config does not exist, using defaults')
- except:
+ except ValueError as ex:
log.getLogger('config').warning(
- 'Could not load complete config from file')
+ 'Could not load complete config from file: %s', ex)
finally:
- self.__tour_filename.set(self.__default_config['paths']['html'])
- self.__bws_filename.set(self.__default_config['paths']['bws'])
- self.__goniec_host.set(self.__default_config['goniec']['host'])
- self.__goniec_port.set(self.__default_config['goniec']['port'])
- self.__goniec_enabled.set(self.__default_config['goniec']['enabled'])
+ self.__variables['tour_filename'].set(
+ self.__default_config['paths']['html'])
+ self.__variables['bws_filename'].set(
+ self.__default_config['paths']['bws'])
+ self.__variables['goniec_host'].set(
+ self.__default_config['goniec']['host'])
+ self.__variables['goniec_port'].set(
+ self.__default_config['goniec']['port'])
+ self.__variables['goniec_enabled'].set(
+ self.__default_config['goniec']['enabled'])
self.toggle_goniec()
def __store_config(self):
"""Write config to JSON file."""
- self.__default_config['paths']['html'] = self.__tour_filename.get()
- self.__default_config['paths']['bws'] = self.__bws_filename.get()
- self.__default_config['goniec']['host'] = self.__goniec_host.get()
- self.__default_config['goniec']['port'] = self.__goniec_port.get()
- self.__default_config['goniec']['enabled'] = self.__goniec_enabled.get()
- json.dump(self.__default_config, file(self.__config_file, 'w'),
+ self.__default_config = {
+ 'paths': {
+ 'html': self.__variables['tour_filename'].get(),
+ 'bws': self.__variables['bws_filename'].get()
+ },
+ 'goniec': {
+ 'host': self.__variables['goniec_host'].get(),
+ 'port': self.__variables['goniec_port'].get(),
+ 'enabled': self.__variables['goniec_enabled'].get()
+ }
+ }
+ json.dump(self.__default_config, file(CONFIG_FILE, 'w'),
sort_keys=True, indent=4)
# embedded image data for app icon