From 6971fa553b1232b8faa61cb9545ac0ead78782f6 Mon Sep 17 00:00:00 2001 From: emkael Date: Sat, 24 Feb 2018 00:54:16 +0100 Subject: Lots of lots of messages are being logged --- jfr_playoff/data.py | 32 +++++++++++++-- jfr_playoff/db.py | 9 +++- jfr_playoff/dto.py | 20 +++++++++ jfr_playoff/filemanager.py | 23 ++++++++++- jfr_playoff/generator.py | 19 ++++++++- jfr_playoff/matchinfo.py | 84 ++++++++++++++++++++++++++++++++++---- jfr_playoff/remote.py | 6 +++ jfr_playoff/settings.py | 16 ++++++-- jfr_playoff/tournamentinfo.py | 95 +++++++++++++++++++++++++++++++++++-------- playoff.py | 2 +- 10 files changed, 271 insertions(+), 35 deletions(-) diff --git a/jfr_playoff/data.py b/jfr_playoff/data.py index a9c3a0c..d9eb314 100644 --- a/jfr_playoff/data.py +++ b/jfr_playoff/data.py @@ -4,13 +4,14 @@ from jfr_playoff.db import PlayoffDB from jfr_playoff.dto import Phase from jfr_playoff.matchinfo import MatchInfo from jfr_playoff.tournamentinfo import TournamentInfo +from jfr_playoff.logger import PlayoffLogger class PlayoffData(object): def __init__(self, settings): self.database = PlayoffDB(settings.get('database')) if settings.has_section('database') else None if self.database is None: - print PlayoffDB.DATABASE_NOT_CONFIGURED_WARNING + PlayoffLogger.get('db').warning(PlayoffDB.DATABASE_NOT_CONFIGURED_WARNING) self.team_settings = settings.get('teams') self.phases = settings.get('phases') self.swiss = [] @@ -23,9 +24,14 @@ class PlayoffData(object): @cached_property def teams(self): if isinstance(self.team_settings, list): + PlayoffLogger.get('data').info( + 'team list pre-defined: %s', self.team_settings) return self.team_settings tournament_info = TournamentInfo(self.team_settings, self.database) - return tournament_info.get_tournament_results() + team_list = tournament_info.get_tournament_results() + if len(team_list) == 0: + PlayoffLogger.get('data').warning('team list is empty!') + return team_list def generate_phases(self): self.grid = [] @@ -44,6 +50,7 @@ class PlayoffData(object): phase_pos += 1 phase_object.matches[phase_pos] = match['id'] phase_pos += 1 + PlayoffLogger.get('data').info('phase object: %s', phase_object) self.grid.append(phase_object) return self.grid @@ -59,6 +66,8 @@ class PlayoffData(object): for phase_obj in self.grid: if match['id'] in phase_obj.matches: phase_obj.running = True + PlayoffLogger.get('data').info( + 'match object: %s', self.match_info[match['id']]) return self.match_info def get_swiss_link(self, event): @@ -67,6 +76,7 @@ class PlayoffData(object): if ('relative_path' in event) and ( event['relative_path'] is not None): swiss_link = '%s/%s' % (event['relative_path'], swiss_link) + PlayoffLogger.get('data').info('swiss link: %s', swiss_link) return swiss_link def prefill_leaderboard(self, teams): @@ -75,6 +85,7 @@ class PlayoffData(object): if len(team) > 3: self.leaderboard[team[3]-1] = team[0] self.fill_swiss_leaderboard(self.swiss, teams) + PlayoffLogger.get('data').info('leaderboard pre-filled: %s', self.leaderboard) return self.leaderboard def fill_swiss_leaderboard(self, swiss, teams): @@ -106,6 +117,8 @@ class PlayoffData(object): self.leaderboard[ target_position - 1] = team[0] place += 1 + PlayoffLogger.get('data').info( + 'leaderboard after %s swiss: %s', event, self.leaderboard) def fill_leaderboard(self): self.prefill_leaderboard(self.teams) @@ -126,30 +139,41 @@ class PlayoffData(object): self.match_info[match['id']].loser) for positions, position_teams in leaderboard_teams.iteritems(): positions = list(positions) + PlayoffLogger.get('data').info( + 'filling leaderboard positions %s with teams %s', + positions, position_teams) if len(positions) == len([ team for team in position_teams if team is not None]): for table_team in self.teams: if table_team[0] in position_teams: position = positions.pop(0) self.leaderboard[position-1] = table_team[0] + PlayoffLogger.get('data').info( + 'team %s in position %d', table_team[0], position) + PlayoffLogger.get('data').info( + 'leaderboard filled: %s', self.leaderboard) return self.leaderboard def get_swiss_info(self): - return [{ + swiss_info = [{ 'link': self.get_swiss_link(event), 'position': event['position'], 'label': event['label'] if 'label' in event else None, 'finished': TournamentInfo(event, self.database).is_finished() } for event in self.swiss] + PlayoffLogger.get('data').info('swiss info: %s', swiss_info) + return swiss_info def get_dimensions(self): - return ( + dimensions = ( len(self.phases), max([ len(phase['matches']) + len(phase['dummies']) if 'dummies' in phase else len(phase['matches']) for phase in self.phases])) + PlayoffLogger.get('data').info('grid dimensions: %s', dimensions) + return dimensions def get_shortname(self, fullname): for team in self.teams: diff --git a/jfr_playoff/db.py b/jfr_playoff/db.py index b7c3726..9d26a67 100644 --- a/jfr_playoff/db.py +++ b/jfr_playoff/db.py @@ -1,9 +1,12 @@ import sys +from jfr_playoff.logger import PlayoffLogger + + class PlayoffDB(object): db_cursor = None - DATABASE_NOT_CONFIGURED_WARNING = 'WARNING: database not configured' + DATABASE_NOT_CONFIGURED_WARNING = 'database not configured' def __init__(self, settings): reload(sys) @@ -14,12 +17,14 @@ class PlayoffDB(object): password=settings['pass'], host=settings['host'], port=settings['port']) + PlayoffLogger.get('db').info('db settings: %s', settings) self.db_cursor = self.database.cursor(buffered=True) def get_cursor(self): return self.db_cursor def __execute_query(self, db_name, sql, params): + PlayoffLogger.get('db').info('query (%s): %s %s', db_name, sql.replace('\n', ' '), params) self.db_cursor.execute(sql.replace('#db#', db_name), params) def fetch(self, db_name, sql, params): @@ -29,6 +34,7 @@ class PlayoffDB(object): row = self.db_cursor.fetchone() return row except mysql.connector.Error as e: + PlayoffLogger.get('db').error(str(e)) raise IOError( message=str(e), filename=db_name, errno=e.errno, strerror=str(e)) @@ -40,6 +46,7 @@ class PlayoffDB(object): results = self.db_cursor.fetchall() return results except mysql.connector.Error as e: + PlayoffLogger.get('db').error(str(e)) raise IOError( message=str(e), filename=db_name, errno=e.errno, strerror=str(e)) diff --git a/jfr_playoff/dto.py b/jfr_playoff/dto.py index f5e08ef..a1b3a7f 100644 --- a/jfr_playoff/dto.py +++ b/jfr_playoff/dto.py @@ -1,7 +1,15 @@ +import sys + class Team(object): name = '' score = 0.0 + def __unicode__(self): + return u'%s (%.1f)' % (self.name, self.score) + + def __repr__(self): + return unicode(self).encode(sys.stdin.encoding) + class Match(object): id = None @@ -13,6 +21,13 @@ class Match(object): winner_matches = None loser_matches = None + def __repr__(self): + return (u'#%d (%s) %s [%s]' % ( + self.id, self.link, [unicode(team) for team in self.teams], + u'finished' if self.running < 0 else ( + u'%d boards' % self.running)) + ).encode(sys.stdin.encoding) + class Phase(object): title = None @@ -20,4 +35,9 @@ class Phase(object): matches = [] running = False + def __repr__(self): + return u'%s (%s) <%d matches> [%srunning]' % ( + self.title, self.link, + len(self.matches), '' if self.running else 'not ') + __all__ = ('Team', 'Match', 'Phase') diff --git a/jfr_playoff/filemanager.py b/jfr_playoff/filemanager.py index 97f9cb8..515845e 100644 --- a/jfr_playoff/filemanager.py +++ b/jfr_playoff/filemanager.py @@ -2,6 +2,8 @@ import os import shutil import socket +from jfr_playoff.logger import PlayoffLogger + import __main__ @@ -9,12 +11,15 @@ class PlayoffFileManager(object): def __init__(self, settings): self.goniec = settings.get('goniec') if settings.has_section('goniec') else None + PlayoffLogger.get('filemanager').info('goniec settings: %s', self.goniec) self.output_file = settings.get('output') + PlayoffLogger.get('filemanager').info('output file: %s', self.output_file) self.output_path = os.path.dirname( self.output_file ).strip(os.sep) if len(self.output_path) > 0: self.output_path += os.sep + PlayoffLogger.get('filemanager').info('output path: %s', self.output_path) self.files = set() def reset(self): @@ -22,14 +27,24 @@ class PlayoffFileManager(object): def register_file(self, path): if path.startswith(self.output_path): + PlayoffLogger.get('filemanager').info('registering file: %s', path) self.files.add(path.replace(self.output_path, '')) + else: + PlayoffLogger.get('filemanager').info( + 'file: %s outside of %s, not registering', path, self.output_path) def write_content(self, content): output_dir = os.path.dirname(self.output_file) if len(output_dir) > 0: if not os.path.exists(output_dir): + PlayoffLogger.get('filemanager').info( + 'output directory %s does not exist, creating', + output_dir) os.makedirs(output_dir) output = open(self.output_file, 'w') + PlayoffLogger.get('filemanager').info( + 'writing %d bytes into file %s', + len(content), self.output_file) output.write(content.encode('utf8')) output.close() self.register_file(self.output_file) @@ -40,7 +55,12 @@ class PlayoffFileManager(object): script_output_dir = os.path.dirname(script_output_path) if len(script_output_dir) > 0: if not os.path.exists(script_output_dir): + PlayoffLogger.get('filemanager').info( + 'output directory %s does not exist, creating', + script_output_dir) os.makedirs(script_output_dir) + PlayoffLogger.get('filemanager').info( + 'copying JS to %s', script_output_path) shutil.copy( unicode(os.path.join( os.path.dirname(__main__.__file__), 'playoff.js')), @@ -58,7 +78,8 @@ class PlayoffFileManager(object): content_lines = [self.output_path] + \ list(self.files) + \ ['bye', ''] - print '\n'.join(content_lines) + PlayoffLogger.get('goniec').info( + '\n'.join(content_lines)) goniec = socket.socket() goniec.connect((self.goniec['host'], self.goniec['port'])) goniec.sendall('\n'.join(content_lines)) diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index 06234fb..4596fed 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -2,15 +2,20 @@ from datetime import datetime import jfr_playoff.template as p_temp from jfr_playoff.data import PlayoffData +from jfr_playoff.logger import PlayoffLogger class PlayoffGenerator(object): def __init__(self, settings): self.data = PlayoffData(settings) self.page = settings.get('page') + PlayoffLogger.get('generator').info( + 'page settings: %s', self.page) self.canvas = {} if settings.has_section('canvas'): self.canvas = settings.get('canvas') + PlayoffLogger.get('generator').info( + 'canvas settings: %s', self.canvas) def generate_content(self): return p_temp.PAGE % ( @@ -55,6 +60,8 @@ class PlayoffGenerator(object): if match.running > 0: running_html = p_temp.MATCH_RUNNING % (match.running) html += p_temp.MATCH_LINK % (match.link, running_html) if match.link is not None else running_html + PlayoffLogger.get('generator').info( + 'match table for #%d generated: %d bytes', match.id, len(html)) return html def get_phase_header(self, phase, position): @@ -95,6 +102,8 @@ class PlayoffGenerator(object): dimensions[1] * ( self.page['height'] + self.page['margin'] ) - self.page['margin']) + PlayoffLogger.get('generator').info( + 'canvas size: %s', canvas_size) grid_boxes = '' col_no = 0 for phase in grid: @@ -105,6 +114,9 @@ class PlayoffGenerator(object): for match in phase.matches: grid_y = int(row_no * match_height + 0.5 * (match_height - self.page['height'])) + PlayoffLogger.get('generator').info( + 'grid box (%d, %d) position: (%d, %d)', + col_no, row_no, grid_x, grid_y) grid_boxes += self.get_match_box( matches[match] if match is not None else None, (grid_x, grid_y)) @@ -130,6 +142,8 @@ class PlayoffGenerator(object): position, self.get_flag(team), team or '') position += 1 html = p_temp.LEADERBOARD.decode('utf8') % (rows) + PlayoffLogger.get('generator').info( + 'leaderboard HTML generated: %d bytes', len(html)) return html def get_swiss_links(self): @@ -141,7 +155,10 @@ class PlayoffGenerator(object): info.append((p_temp.SWISS_LINK if event['finished'] else p_temp.SWISS_RUNNING_LINK) % ( event['link'], event_label )) - return '\n'.join(info) + html = '\n'.join(info) + PlayoffLogger.get('generator').info( + 'swiss HTML generated: %d bytes', len(html)) + return html def get_flag(self, team): flag = self.data.get_team_image(team) diff --git a/jfr_playoff/matchinfo.py b/jfr_playoff/matchinfo.py index 997e685..db49b07 100644 --- a/jfr_playoff/matchinfo.py +++ b/jfr_playoff/matchinfo.py @@ -5,6 +5,7 @@ import jfr_playoff.sql as p_sql from jfr_playoff.dto import Match, Team from jfr_playoff.remote import RemoteUrl as p_remote from jfr_playoff.tournamentinfo import TournamentInfo +from jfr_playoff.logger import PlayoffLogger class MatchInfo: @@ -36,10 +37,15 @@ class MatchInfo: def __fetch_match_link(self): if 'link' in self.config: self.info.link = self.config['link'] + PlayoffLogger.get('matchinfo').info( + 'match #%d link pre-defined: %s', self.info.id, self.info.link) elif ('round' in self.config) and ('database' in self.config): event_info = TournamentInfo(self.config, self.database) self.info.link = event_info.get_results_link( 'runda%d.html' % (self.config['round'])) + PlayoffLogger.get('matchinfo').info( + 'match #%d link fetched: %s', self.info.id, self.info.link) + PlayoffLogger.get('matchinfo').info('match #%d link empty', self.info.id) def __get_predefined_scores(self): teams = [Team(), Team()] @@ -62,6 +68,9 @@ class MatchInfo: if i == 2: break scores_fetched = True + PlayoffLogger.get('matchinfo').info( + 'pre-defined scores for match #%d: %s', + self.info.id, teams) return scores_fetched, teams_fetched, teams def __get_db_teams(self, teams, fetch_scores): @@ -77,6 +86,8 @@ class MatchInfo: teams[0].score += row[2] else: teams[1].score -= row[2] + PlayoffLogger.get('matchinfo').info( + 'db scores for match #%d: %s', self.info.id, teams) return teams def __find_table_row(self, url): @@ -84,7 +95,13 @@ class MatchInfo: for row in html_content.select('tr tr'): for cell in row.select('td.t1'): if cell.text.strip() == str(self.config['table']): + PlayoffLogger.get('matchinfo.html').debug( + 'HTML row for table %d found: %s', + self.config['table'], row) return row + PlayoffLogger.get('matchinfo.html').debug( + 'HTML row for table %d not found', + self.config['table']) return None def __get_html_teams(self, teams, fetch_score): @@ -104,6 +121,9 @@ class MatchInfo: for i in range(0, 2): teams[i].name = team_names[i] teams[i].score = scores[i] + PlayoffLogger.get('matchinfo').info( + 'HTML scores for match #%d: %s', + self.info.id, teams) return teams def __get_config_teams(self, teams): @@ -133,11 +153,16 @@ class MatchInfo: else '??' for team in match_teams]) else: teams[i].name = '' + PlayoffLogger.get('matchinfo').info( + 'config scores for match #%d: %s', + self.info.id, teams) return teams def __fetch_teams_with_scores(self): (scores_fetched, teams_fetched, self.info.teams) = self.__get_predefined_scores() if scores_fetched: + PlayoffLogger.get('matchinfo').info( + 'pre-defined scores for match #%d fetched', self.info.id) if 'running' in self.config: self.info.running = int(self.config['running']) else: @@ -151,10 +176,16 @@ class MatchInfo: raise KeyError('database not configured') self.info.teams = self.__get_db_teams( self.info.teams, not scores_fetched) - except (IOError, TypeError, IndexError, KeyError): + except (IOError, TypeError, IndexError, KeyError) as e: + PlayoffLogger.get('matchinfo').warning( + 'fetching DB scores for match #%d failed: %s(%s)', + self.info.id, type(e).__name__, e.message) self.info.teams = self.__get_html_teams( self.info.teams, not scores_fetched) - except (TypeError, IndexError, KeyError, IOError, ValueError): + except (TypeError, IndexError, KeyError, IOError, ValueError) as e: + PlayoffLogger.get('matchinfo').warning( + 'fetching HTML scores for match #%d failed: %s(%s)', + self.info.id, type(e).__name__, e.message) self.info.teams = self.__get_config_teams(self.info.teams) def __get_db_board_count(self): @@ -170,6 +201,9 @@ class MatchInfo: boards_played = max(int(row[1]), 0) if boards_to_play > 0: boards_played += int(towels[0]) + PlayoffLogger.get('matchinfo').info( + 'DB board count for match #%d: %d/%d', + self.info.id, boards_played, boards_to_play) return boards_played, boards_to_play def __has_segment_link(self, cell): @@ -196,8 +230,14 @@ class MatchInfo: played_boards = len([ row for row in board_rows if len( ''.join([cell.text.strip() for cell in row.select('td.bdc')])) > 0]) + PlayoffLogger.get('matchinfo').info( + 'HTML played boards count for segment: %d/%d', + played_boards, board_count) return board_count, played_boards >= board_count - except IOError: + except IOError as e: + PlayoffLogger.get('matchinfo').info( + 'cannot fetch HTML played boards count for segment: %s(%s)', + self.info.id, type(e).__name__, e.message) return 0, False return 0, False @@ -212,6 +252,8 @@ class MatchInfo: towels = [cell for cell in cells if self.__has_towel_image(cell)] if len(segments) == 0: if len(towels) > 0: + PlayoffLogger.get('matchinfo').info( + 'HTML board count for match #%d: all towels', self.info.id) return 1, 1 # entire match is toweled, so mark as finished else: raise ValueError('segments not found') @@ -226,8 +268,14 @@ class MatchInfo: finished_segments.append(segment) if boards_in_segment is None and boards > 0: boards_in_segment = boards + PlayoffLogger.get('matchinfo').info( + 'HTML board count for match #%d, found: %d finished segments, %d towels, %d boards per segment and %d boards in running segment', + self.info.id, len(finished_segments), len(towels), boards_in_segment, running_boards) total_boards = (len(segments) + len(towels) + len(running_segments)) * boards_in_segment played_boards = (len(towels) + len(finished_segments)) * boards_in_segment + running_boards + PlayoffLogger.get('matchinfo').info( + 'HTML board count for match #%d: %d/%d', + self.info.id, played_boards, total_boards) return played_boards, total_boards def __fetch_board_count(self): @@ -237,10 +285,16 @@ class MatchInfo: if self.database is None: raise KeyError('database not configured') boards_played, boards_to_play = self.__get_db_board_count() - except (IOError, TypeError, IndexError, KeyError): + except (IOError, TypeError, IndexError, KeyError) as e: + PlayoffLogger.get('matchinfo').warning( + 'fetching board count from DB for match #%d failed: %s(%s)', + self.info.id, type(e).__name__, e.message) try: boards_played, boards_to_play = self.__get_html_board_count() - except (TypeError, IndexError, KeyError, IOError, ValueError): + except (TypeError, IndexError, KeyError, IOError, ValueError) as e: + PlayoffLogger.get('matchinfo').warning( + 'fetching board count from HTML for match #%d failed: %s(%s)', + self.info.id, type(e).__name__, e.message) pass if boards_played > 0: self.info.running = -1 \ @@ -260,6 +314,9 @@ class MatchInfo: current_segment = int( self.database.fetch( self.config['database'], p_sql.CURRENT_SEGMENT, ())[0]) + PlayoffLogger.get('matchinfo').info( + 'fetched running segment from DB for match #%d: %d', + self.info.id, current_segment) return '%s%st%d-%d.html' % ( prefix, round_no, self.config['table'], current_segment) @@ -270,6 +327,9 @@ class MatchInfo: running_link = row.select('td.bdcg a[href]') if len(running_link) == 0: raise ValueError('running link not found') + PlayoffLogger.get('matchinfo').info( + 'fetched running link from HTML for match #%d: %s', + self.info.id, running_link) return urljoin(self.info.link, running_link[0]['href']) def __determine_running_link(self): @@ -282,11 +342,16 @@ class MatchInfo: raise KeyError('database not configured') self.info.link = self.__get_db_running_link( link_match.group(1), link_match.group(2)) - except (IOError, TypeError, IndexError, KeyError): + except (IOError, TypeError, IndexError, KeyError) as e: + PlayoffLogger.get('matchinfo').warning( + 'cannot determine running link from DB for match #%d: %s(%s)', + self.info.id, type(e).__name__, e.message) try: self.info.link = self.__get_html_running_link() - except (TypeError, IndexError, KeyError, IOError, ValueError): - pass + except (TypeError, IndexError, KeyError, IOError, ValueError) as e: + PlayoffLogger.get('matchinfo').warning( + 'cannot determine running link from HTML for match #%d: %s(%s)', + self.info.id, type(e).__name__, e.message) def set_phase_link(self, phase_link): if self.info.link is None: @@ -294,6 +359,9 @@ class MatchInfo: else: if self.info.link != '#': self.info.link = urljoin(phase_link, self.info.link) + PlayoffLogger.get('matchinfo').info( + 'applying phase link %s to match #%d: %s', + phase_link, self.info.id, self.info.link) def get_info(self): self.__fetch_teams_with_scores() diff --git a/jfr_playoff/remote.py b/jfr_playoff/remote.py index f02a8d4..a113c96 100644 --- a/jfr_playoff/remote.py +++ b/jfr_playoff/remote.py @@ -1,6 +1,7 @@ import requests from bs4 import BeautifulSoup as bs +from jfr_playoff.logger import PlayoffLogger class RemoteUrl: @@ -8,6 +9,11 @@ class RemoteUrl: @classmethod def fetch(cls, url): + PlayoffLogger.get('remote').info( + 'fetching content for: %s', url) if url not in cls.url_cache: cls.url_cache[url] = requests.get(url).text + PlayoffLogger.get('remote').info( + 'content for %s not in cache: retrieved %d bytes', + url, len(cls.url_cache[url])) return bs(cls.url_cache[url], 'lxml') diff --git a/jfr_playoff/settings.py b/jfr_playoff/settings.py index 021253f..181e19f 100644 --- a/jfr_playoff/settings.py +++ b/jfr_playoff/settings.py @@ -4,6 +4,8 @@ import readline import requests import sys +from jfr_playoff.logger import PlayoffLogger + def complete_filename(text, state): return (glob.glob(text+'*')+[None])[state] @@ -31,9 +33,9 @@ class PlayoffSettings(object): if (key not in base_config) or overwrite: base_config[key] = value except Exception as e: - print 'WARNING: unable to merge remote config: %s' % (str(e)) - if remote_url is not None: - print 'Offending URL: %s' % (remote_url) + PlayoffLogger.get('settings').warning( + 'unable to merge remote config %s: %s(%s)', + remote_url, type(e).__name__, e.message) return base_config def load(self): @@ -45,16 +47,24 @@ class PlayoffSettings(object): 'JSON settings file: ').decode(sys.stdin.encoding) if self.settings is None: + PlayoffLogger.get('settings').info( + 'loading config file: %s', unicode(self.settings_file)) self.settings = json.loads( open(unicode(self.settings_file)).read().decode('utf-8-sig')) if self.has_section('remotes'): remote_config = {} for remote in self.get('remotes'): + PlayoffLogger.get('settings').info( + 'merging remote config: %s', remote) remote_config = self.__merge_config( remote_config, remote_url=remote) + PlayoffLogger.get('settings').debug( + 'remote config: %s', remote_config) self.settings = self.__merge_config( self.settings, new_config=remote_config, overwrite=False) + PlayoffLogger.get('settings').debug( + 'parsed config: %s', self.settings) def has_section(self, key): self.load() diff --git a/jfr_playoff/tournamentinfo.py b/jfr_playoff/tournamentinfo.py index ee96214..24440b3 100644 --- a/jfr_playoff/tournamentinfo.py +++ b/jfr_playoff/tournamentinfo.py @@ -3,8 +3,9 @@ import re import jfr_playoff.sql as p_sql from jfr_playoff.remote import RemoteUrl as p_remote +from jfr_playoff.logger import PlayoffLogger -SWISS_TIE_WARNING = 'WARNING: tie detected in swiss %s.' + \ +SWISS_TIE_WARNING = 'tie detected in swiss %s.' + \ ' Make sure to resolve the tie by arranging teams' + \ ' in configuration file.' @@ -19,7 +20,9 @@ class TournamentInfo: if 'link' not in self.settings: raise KeyError('link not configured') if not self.settings['link'].endswith('leaderb.html'): - raise ValueError('unable to determine tournament results') + raise ValueError('invalid link to tournament results') + PlayoffLogger.get('tournamentinfo').info( + 'fetching tournament results from leaderboard URL: %s', self.settings['link']) leaderboard = p_remote.fetch(self.settings['link']) result_links = [row.select('a[onmouseover]') for row in leaderboard.find_all('tr') if len(row.select('a[onmouseover]')) > 0] results = [None] * (len(result_links) * max([len(links) for links in result_links])) @@ -39,15 +42,23 @@ class TournamentInfo: if team_image is not None: team_info.append(team_image['src'].replace('images/', '')) teams.append(team_info) + PlayoffLogger.get('tournamentinfo').info( + 'read tournament results from leaderboard: %s', teams) for table in range(1, int(ceil(len(teams)/2.0))+1): table_url = self.get_results_link('1t%d-1.html' % (table)) table_content = p_remote.fetch(table_url) + PlayoffLogger.get('tournamentinfo').info( + 'reading team shortnames from traveller: %s', table_url) for link in table_content.select('a.br'): if link['href'] in team_links: for team in teams: if team[0] == team_links[link['href']]: team[1] = link.text.strip(u'\xa0') + PlayoffLogger.get('tournamentinfo').info( + 'shortname for %s: %s', team[0], team[1]) break + PlayoffLogger.get('tournamentinfo').info( + 'tournament results from HTML: %s', teams) return teams def __get_db_results(self): @@ -66,23 +77,44 @@ class TournamentInfo: swiss_results = sorted( swiss_results, key=lambda t: t[1], reverse=True) swiss_results = sorted(swiss_results, key=lambda team: team[2]) + PlayoffLogger.get('tournamentinfo').info( + 'fetched tournament results from database %s: %s', + self.settings['database'], swiss_results) prev_result = None for team in swiss_results: if prev_result == team[1]: - print SWISS_TIE_WARNING % (self.settings['database']) + PlayoffLogger.get('tournamentinfo').warning( + SWISS_TIE_WARNING, self.settings['database']) prev_result = team[1] db_teams = [[team[0], team[3], team[4]] for team in swiss_results] + PlayoffLogger.get('tournamentinfo').info( + 'fetched team list from database %s: %s', + self.settings['database'], db_teams) return db_teams def __get_html_finished(self): if 'link' not in self.settings: raise KeyError('link not configured') if not self.settings['link'].endswith('leaderb.html'): - raise ValueError('unable to determine tournament status') + raise ValueError('invalid tournament leaderboard link') + PlayoffLogger.get('tournamentinfo').info( + 'fetching tournament finished status from HTML: %s', + self.settings['link']) leaderboard = p_remote.fetch(self.settings['link']) leaderb_heading = leaderboard.select('td.bdnl12')[0].text + contains_digits = any(char.isdigit() for char in leaderb_heading) + PlayoffLogger.get('tournamentinfo').info( + 'tournament header from HTML: %s, %s', + leaderb_heading, 'contains digits' if contains_digits else "doesn't contain digits") non_zero_scores = [imps.text for imps in leaderboard.select('td.bdc small') if imps.text != '0-0'] - return (not any(char.isdigit() for char in leaderb_heading)) and (len(non_zero_scores) > 0) + PlayoffLogger.get('tournamentinfo').info( + 'tournament leaderboard from HTML: has %d non-zero scores', + len(non_zero_scores)) + finished = (not contains_digits) and (len(non_zero_scores) > 0) + PlayoffLogger.get('tournamentinfo').info( + 'tournament leaderboard from HTML indicates finished: %s', + finished) + return finished def __get_db_finished(self): if self.database is None: @@ -91,14 +123,21 @@ class TournamentInfo: raise KeyError('database not configured') finished = self.database.fetch( self.settings['database'], p_sql.SWISS_ENDED, {}) + PlayoffLogger.get('tournamentinfo').info( + 'fetching tournament finished status from DB %s: %s', + self.settings['database'], finished) return (len(finished) > 0) and (finished[0] > 0) def __get_html_link(self, suffix='leaderb.html'): if 'link' not in self.settings: raise KeyError('link not configured') if not self.settings['link'].endswith('leaderb.html'): - raise ValueError('unable to determine html link') - return re.sub(r'leaderb.html$', suffix, self.settings['link']) + raise ValueError('invalid tournament leaderboard link') + link = re.sub(r'leaderb.html$', suffix, self.settings['link']) + PlayoffLogger.get('tournamentinfo').info( + 'generating tournament-specific link from leaderboard link %s: %s -> %s', + self.settings['link'], suffix, link) + return link def __get_db_link(self, suffix='leaderb.html'): if self.database is None: @@ -109,19 +148,31 @@ class TournamentInfo: self.settings['database'], p_sql.PREFIX, ()) if row is not None: if len(row) > 0: - return row[0] + suffix + link = row[0] + suffix + PlayoffLogger.get('tournamentinfo').info( + 'generating tournament-specific link from DB %s prefix: %s -> %s', + self.settings['database'], suffix, link) + return link raise ValueError('unable to fetch db link') def get_tournament_results(self): teams = [] try: teams = self.__get_db_results() - except (IOError, TypeError, IndexError, KeyError): + except (IOError, TypeError, IndexError, KeyError) as e: + PlayoffLogger.get('tournamentinfo').warning( + 'cannot determine tournament results from DB: %s(%s)', + type(e).__name__, e.message) try: teams = self.__get_html_results() - except (TypeError, IndexError, KeyError, IOError, ValueError): - pass + except (TypeError, IndexError, KeyError, IOError, ValueError) as e: + PlayoffLogger.get('tournamentinfo').warning( + 'cannot determine tournament results from HTML: %s(%s)', + type(e).__name__, e.message) if self.is_finished() and 'final_positions' in self.settings: + PlayoffLogger.get('tournamentinfo').info( + 'setting final positions from tournament results: %s', + self.settings['final_positions']) for position in self.settings['final_positions']: if len(teams) >= position: teams[position-1].append(position) @@ -130,19 +181,31 @@ class TournamentInfo: def is_finished(self): try: return self.__get_db_finished() - except (IOError, TypeError, IndexError, KeyError): + except (IOError, TypeError, IndexError, KeyError) as e: + PlayoffLogger.get('tournamentinfo').warning( + 'cannot determine tournament finished status from DB: %s(%s)', + type(e).__name__, e.message) try: return self.__get_html_finished() - except (TypeError, IndexError, KeyError, IOError, ValueError): - pass + except (TypeError, IndexError, KeyError, IOError, ValueError) as e: + PlayoffLogger.get('tournamentinfo').warning( + 'cannot determine tournament finished status from HTML: %s(%s)', + type(e).__name__, e.message) + PlayoffLogger.get('tournamentinfo').info( + 'assuming tournament is finished') return True def get_results_link(self, suffix='leaderb.html'): try: return self.__get_db_link(suffix) - except (IOError, TypeError, IndexError, KeyError): + except (IOError, TypeError, IndexError, KeyError) as e: + PlayoffLogger.get('tournamentinfo').warning( + 'cannot determine tournament link from DB: %s(%s)', + type(e).__name__, e.message) try: return self.__get_html_link(suffix) except (KeyError, ValueError): - pass + PlayoffLogger.get('tournamentinfo').warning( + 'cannot determine tournament link from HTML: %s(%s)', + type(e).__name__, e.message) return None diff --git a/playoff.py b/playoff.py index 16113fc..ac84c66 100644 --- a/playoff.py +++ b/playoff.py @@ -30,7 +30,7 @@ def main(): 'INFO' if arguments.verbose else ( 'DEBUG' if arguments.debug else 'WARNING'))) - PlayoffLogger.get().debug('started with arguments: %s', arguments) + PlayoffLogger.get().info('started with arguments: %s', arguments) settings = PlayoffSettings(arguments.config_file) interactive = settings.interactive -- cgit v1.2.3 From 8c500a75a6eafd65b332d0f8346d9059d5293cbb Mon Sep 17 00:00:00 2001 From: emkael Date: Sat, 24 Feb 2018 11:04:11 +0100 Subject: Extracting text representation of an exception instead of explicit .message property --- jfr_playoff/matchinfo.py | 14 +++++++------- jfr_playoff/settings.py | 2 +- jfr_playoff/tournamentinfo.py | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/jfr_playoff/matchinfo.py b/jfr_playoff/matchinfo.py index db49b07..8132fe2 100644 --- a/jfr_playoff/matchinfo.py +++ b/jfr_playoff/matchinfo.py @@ -179,13 +179,13 @@ class MatchInfo: except (IOError, TypeError, IndexError, KeyError) as e: PlayoffLogger.get('matchinfo').warning( 'fetching DB scores for match #%d failed: %s(%s)', - self.info.id, type(e).__name__, e.message) + self.info.id, type(e).__name__, str(e)) self.info.teams = self.__get_html_teams( self.info.teams, not scores_fetched) except (TypeError, IndexError, KeyError, IOError, ValueError) as e: PlayoffLogger.get('matchinfo').warning( 'fetching HTML scores for match #%d failed: %s(%s)', - self.info.id, type(e).__name__, e.message) + self.info.id, type(e).__name__, str(e)) self.info.teams = self.__get_config_teams(self.info.teams) def __get_db_board_count(self): @@ -237,7 +237,7 @@ class MatchInfo: except IOError as e: PlayoffLogger.get('matchinfo').info( 'cannot fetch HTML played boards count for segment: %s(%s)', - self.info.id, type(e).__name__, e.message) + self.info.id, type(e).__name__, str(e)) return 0, False return 0, False @@ -288,13 +288,13 @@ class MatchInfo: except (IOError, TypeError, IndexError, KeyError) as e: PlayoffLogger.get('matchinfo').warning( 'fetching board count from DB for match #%d failed: %s(%s)', - self.info.id, type(e).__name__, e.message) + self.info.id, type(e).__name__, str(e)) try: boards_played, boards_to_play = self.__get_html_board_count() except (TypeError, IndexError, KeyError, IOError, ValueError) as e: PlayoffLogger.get('matchinfo').warning( 'fetching board count from HTML for match #%d failed: %s(%s)', - self.info.id, type(e).__name__, e.message) + self.info.id, type(e).__name__, str(e)) pass if boards_played > 0: self.info.running = -1 \ @@ -345,13 +345,13 @@ class MatchInfo: except (IOError, TypeError, IndexError, KeyError) as e: PlayoffLogger.get('matchinfo').warning( 'cannot determine running link from DB for match #%d: %s(%s)', - self.info.id, type(e).__name__, e.message) + self.info.id, type(e).__name__, str(e)) try: self.info.link = self.__get_html_running_link() except (TypeError, IndexError, KeyError, IOError, ValueError) as e: PlayoffLogger.get('matchinfo').warning( 'cannot determine running link from HTML for match #%d: %s(%s)', - self.info.id, type(e).__name__, e.message) + self.info.id, type(e).__name__, str(e)) def set_phase_link(self, phase_link): if self.info.link is None: diff --git a/jfr_playoff/settings.py b/jfr_playoff/settings.py index 181e19f..58de66c 100644 --- a/jfr_playoff/settings.py +++ b/jfr_playoff/settings.py @@ -35,7 +35,7 @@ class PlayoffSettings(object): except Exception as e: PlayoffLogger.get('settings').warning( 'unable to merge remote config %s: %s(%s)', - remote_url, type(e).__name__, e.message) + remote_url, type(e).__name__, str(e)) return base_config def load(self): diff --git a/jfr_playoff/tournamentinfo.py b/jfr_playoff/tournamentinfo.py index 24440b3..a646d4e 100644 --- a/jfr_playoff/tournamentinfo.py +++ b/jfr_playoff/tournamentinfo.py @@ -162,13 +162,13 @@ class TournamentInfo: except (IOError, TypeError, IndexError, KeyError) as e: PlayoffLogger.get('tournamentinfo').warning( 'cannot determine tournament results from DB: %s(%s)', - type(e).__name__, e.message) + type(e).__name__, str(e)) try: teams = self.__get_html_results() except (TypeError, IndexError, KeyError, IOError, ValueError) as e: PlayoffLogger.get('tournamentinfo').warning( 'cannot determine tournament results from HTML: %s(%s)', - type(e).__name__, e.message) + type(e).__name__, str(e)) if self.is_finished() and 'final_positions' in self.settings: PlayoffLogger.get('tournamentinfo').info( 'setting final positions from tournament results: %s', @@ -184,13 +184,13 @@ class TournamentInfo: except (IOError, TypeError, IndexError, KeyError) as e: PlayoffLogger.get('tournamentinfo').warning( 'cannot determine tournament finished status from DB: %s(%s)', - type(e).__name__, e.message) + type(e).__name__, str(e)) try: return self.__get_html_finished() except (TypeError, IndexError, KeyError, IOError, ValueError) as e: PlayoffLogger.get('tournamentinfo').warning( 'cannot determine tournament finished status from HTML: %s(%s)', - type(e).__name__, e.message) + type(e).__name__, str(e)) PlayoffLogger.get('tournamentinfo').info( 'assuming tournament is finished') return True @@ -201,11 +201,11 @@ class TournamentInfo: except (IOError, TypeError, IndexError, KeyError) as e: PlayoffLogger.get('tournamentinfo').warning( 'cannot determine tournament link from DB: %s(%s)', - type(e).__name__, e.message) + type(e).__name__, str(e)) try: return self.__get_html_link(suffix) except (KeyError, ValueError): PlayoffLogger.get('tournamentinfo').warning( 'cannot determine tournament link from HTML: %s(%s)', - type(e).__name__, e.message) + type(e).__name__, str(e)) return None -- cgit v1.2.3 From 9b8e34179f2dc17cecb290f8573ad3b57e7178c5 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 6 Apr 2018 00:39:02 +0200 Subject: Coalescing nullable DTO properties --- jfr_playoff/dto.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/jfr_playoff/dto.py b/jfr_playoff/dto.py index a1b3a7f..a88cd2b 100644 --- a/jfr_playoff/dto.py +++ b/jfr_playoff/dto.py @@ -1,11 +1,18 @@ import sys +def coalesce(*arg): + for el in arg: + if el is not None: + return el + return None + + class Team(object): name = '' score = 0.0 def __unicode__(self): - return u'%s (%.1f)' % (self.name, self.score) + return u'%s (%.1f)' % (coalesce(self.name, ''), self.score) def __repr__(self): return unicode(self).encode(sys.stdin.encoding) @@ -23,7 +30,7 @@ class Match(object): def __repr__(self): return (u'#%d (%s) %s [%s]' % ( - self.id, self.link, [unicode(team) for team in self.teams], + self.id, coalesce(self.link, ''), [unicode(team) for team in self.teams], u'finished' if self.running < 0 else ( u'%d boards' % self.running)) ).encode(sys.stdin.encoding) @@ -37,7 +44,7 @@ class Phase(object): def __repr__(self): return u'%s (%s) <%d matches> [%srunning]' % ( - self.title, self.link, + self.title, coalesce(self.link, ''), len(self.matches), '' if self.running else 'not ') __all__ = ('Team', 'Match', 'Phase') -- cgit v1.2.3 From 28218c054785e3cb77b6e36182bce97c8010319e Mon Sep 17 00:00:00 2001 From: emkael Date: Tue, 1 May 2018 11:53:23 +0200 Subject: Refactoring template "engine" to provide a uniform interface --- jfr_playoff/generator.py | 90 +++++++----- jfr_playoff/template.py | 364 ++++++++++++++++++++++++----------------------- 2 files changed, 241 insertions(+), 213 deletions(-) diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index 2b5aaf0..e5d29a8 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -1,6 +1,6 @@ from datetime import datetime -import jfr_playoff.template as p_temp +from jfr_playoff.template import Template as p_temp from jfr_playoff.data import PlayoffData @@ -21,67 +21,82 @@ class PlayoffGenerator(object): self.data.generate_phases(), self.data.fill_match_info()) leaderboard_table = self.get_leaderboard_table() - return p_temp.PAGE % ( - p_temp.PAGE_HEAD % ( - p_temp.PAGE_HEAD_REFRESH % ( - self.page['refresh']) + return p_temp.get( + 'PAGE', + p_temp.get( + 'PAGE_HEAD', + p_temp.get( + 'PAGE_HEAD_REFRESH', + self.page['refresh']) \ if self.page['refresh'] > 0 else '', self.page['title']), - p_temp.PAGE_BODY % ( + p_temp.get( + 'PAGE_BODY', self.page['logoh'], match_grid, self.get_swiss_links(), leaderboard_table, self.get_leaderboard_caption_table() if leaderboard_table else '', - p_temp.PAGE_BODY_FOOTER.decode('utf8') % ( + p_temp.get( + 'PAGE_BODY_FOOTER', datetime.now().strftime('%Y-%m-%d o %H:%M:%S')))) def get_match_table(self, match): rows = '' for team in match.teams: - score_html = p_temp.MATCH_SCORE % (team.score) + score_html = p_temp.get('MATCH_SCORE', team.score) team_label = ' / '.join([ self.data.get_shortname(name) for name in team.name.split('
')]) - team_html = p_temp.MATCH_TEAM_LINK % ( - match.link, team.name, team_label) if match.link is not None \ - else p_temp.MATCH_TEAM_NON_LINK % ( - team.name, team_label) - rows += p_temp.MATCH_TEAM_ROW % ( + team_html = p_temp.get( + 'MATCH_TEAM_LINK', + match.link, team.name, team_label) \ + if match.link is not None \ + else p_temp.get( + 'MATCH_TEAM_NON_LINK', + team.name, team_label) + rows += p_temp.get( + 'MATCH_TEAM_ROW', ' '.join([ 'winner' if team.name == match.winner else '', 'loser' if team.name == match.loser else '' ]).strip(), team_html, - p_temp.MATCH_LINK % (match.link, score_html) if match.link is not None else score_html) - html = p_temp.MATCH_TABLE.decode('utf8') % ( + p_temp.get( + 'MATCH_LINK', + match.link, score_html) \ + if match.link is not None else score_html) + html = p_temp.get( + 'MATCH_TABLE', int(self.page['width'] * 0.75), int(self.page['width'] * 0.25), rows) if match.running > 0: - running_html = p_temp.MATCH_RUNNING % (match.running) - html += p_temp.MATCH_LINK % (match.link, running_html) if match.link is not None else running_html + running_html = p_temp.get('MATCH_RUNNING', match.running) + html += p_temp.get('MATCH_LINK', match.link, running_html) if match.link is not None else running_html return html def get_phase_header(self, phase, position): - if phase.running: - grid_header = p_temp.MATCH_GRID_PHASE_RUNNING - else: - grid_header = p_temp.MATCH_GRID_PHASE - grid_header = grid_header % (phase.title) + grid_header = p_temp.get( + 'MATCH_GRID_PHASE_RUNNING' if phase.running \ + else 'MATCH_GRID_PHASE', + phase.title) if phase.link is not None: - return p_temp.MATCH_GRID_PHASE_LINK % ( + return p_temp.get( + 'MATCH_GRID_PHASE_LINK', phase.link, self.page['width'], position, grid_header) else: - return p_temp.MATCH_GRID_PHASE_NON_LINK % ( + return p_temp.get( + 'MATCH_GRID_PHASE_NON_LINK', self.page['width'], position, grid_header) def get_match_box(self, match, position): if match is not None: - return p_temp.MATCH_BOX % ( + return p_temp.get( + 'MATCH_BOX', position[0], position[1], match.id, ' '.join([ @@ -116,7 +131,8 @@ class PlayoffGenerator(object): (grid_x, grid_y)) row_no += 1 col_no += 1 - return p_temp.MATCH_GRID % ( + return p_temp.get( + 'MATCH_GRID', canvas_size[0], canvas_size[1], canvas_size[0], canvas_size[1], ' '.join(['data-%s="%s"' % ( @@ -136,9 +152,10 @@ class PlayoffGenerator(object): rows = '' for style in self.leaderboard_classes: if 'caption' in style: - rows += p_temp.LEADERBOARD_CAPTION_TABLE_ROW % ( + rows += p_temp.get( + 'LEADERBOARD_CAPTION_TABLE_ROW', style['class'], style['caption']) - return p_temp.LEADERBOARD_CAPTION_TABLE % (rows) if rows else '' + return p_temp.get('LEADERBOARD_CAPTION_TABLE', rows) if rows else '' def get_leaderboard_table(self): leaderboard = self.data.fill_leaderboard() @@ -147,24 +164,27 @@ class PlayoffGenerator(object): position = 1 rows = '' for team in leaderboard: - rows += p_temp.LEADERBOARD_ROW % ( + rows += p_temp.get( + 'LEADERBOARD_ROW', self.get_leaderboard_row_class(position), position, self.get_flag(team), team or '') position += 1 - html = p_temp.LEADERBOARD.decode('utf8') % (rows) + html = p_temp.get('LEADERBOARD', rows) return html def get_swiss_links(self): info = [] for event in self.data.get_swiss_info(): - event_label = p_temp.SWISS_DEFAULT_LABEL % (event['position']) + event_label = p_temp.get('SWISS_DEFAULT_LABEL', event['position']) if 'label' in event and event['label'] is not None: event_label = event['label'] - info.append((p_temp.SWISS_LINK if event['finished'] else p_temp.SWISS_RUNNING_LINK) % ( - event['link'], event_label - )) + info.append((p_temp.get('SWISS_LINK') \ + if event['finished'] \ + else p_temp.get( + 'SWISS_RUNNING_LINK', + event['link'], event_label))) return '\n'.join(info) def get_flag(self, team): flag = self.data.get_team_image(team) - return '' if flag is None else p_temp.LEADERBOARD_ROW_FLAG % (flag) + return '' if flag is None else p_temp.get('LEADERBOARD_ROW_FLAG', flag) diff --git a/jfr_playoff/template.py b/jfr_playoff/template.py index b99c7c7..0699c1e 100644 --- a/jfr_playoff/template.py +++ b/jfr_playoff/template.py @@ -1,180 +1,188 @@ # -*- coding: utf-8 -*- -MATCH_TABLE = ''' - - - - - -%s -
  wynik 
-''' - -MATCH_LINK = ''' - -%s - -''' - -MATCH_SCORE = ''' - %.1f  -''' - -MATCH_TEAM_LINK = ''' -%s -''' - -MATCH_TEAM_NON_LINK = ''' -%s -''' - -MATCH_TEAM_ROW = ''' - - %s  - -%s - - -''' - -MATCH_RUNNING = ''' - -%d - -''' - -MATCH_GRID = ''' -
- -%s - -
-''' - -MATCH_GRID_PHASE_LINK = ''' - -%s - -''' - -MATCH_GRID_PHASE_NON_LINK = ''' - -

%s

-
-''' - -MATCH_GRID_PHASE = ''' -%s -''' - -MATCH_GRID_PHASE_RUNNING = ''' - -%s - -''' - -MATCH_BOX = ''' -
-%s -
-''' - -LEADERBOARD = ''' - - - - - - - - - - - -%s -
 KLASYFIKACJA KOŃCOWA 
 
 miejsce  drużyna 
-''' - -LEADERBOARD_ROW = ''' - -%d - - %s  %s  - - -''' - -LEADERBOARD_ROW_FLAG = ''' - -''' - -LEADERBOARD_CAPTION_TABLE = ''' - - - -%s -
 
 LEGENDA 
-''' - -LEADERBOARD_CAPTION_TABLE_ROW = ''' - - - %s  - - -''' - -PAGE_HEAD = ''' - - - - - - -%s -%s - - -''' - -PAGE_HEAD_REFRESH = ''' - -''' - -PAGE_BODY = ''' - -%s -%s -

-%s -

-%s -%s -%s -''' - -PAGE_BODY_FOOTER = ''' -

 Admin ©Jan Romański'2005, PlayOff ©Michał Klichowicz'2017-2018, strona wygenerowana %s

-''' - -PAGE = ''' - - - -%s - - -%s - - -''' - -SWISS_LINK = ''' -[ %s ]

-''' - -SWISS_RUNNING_LINK = ''' -[  %s  ]

-''' - -SWISS_DEFAULT_LABEL = 'Turniej o %d. miejsce' +class TemplateStrings(object): + + MATCH_TABLE = ''' + + + + + + %s +
  wynik 
+ ''' + + MATCH_LINK = ''' + + %s + + ''' + + MATCH_SCORE = ''' +  %.1f  + ''' + + MATCH_TEAM_LINK = ''' + %s + ''' + + MATCH_TEAM_NON_LINK = ''' + %s + ''' + + MATCH_TEAM_ROW = ''' + +  %s  + + %s + + + ''' + + MATCH_RUNNING = ''' + + %d + + ''' + + MATCH_GRID = ''' +
+ + %s + +
+ ''' + + MATCH_GRID_PHASE_LINK = ''' + + %s + + ''' + + MATCH_GRID_PHASE_NON_LINK = ''' + +

%s

+
+ ''' + + MATCH_GRID_PHASE = ''' + %s + ''' + + MATCH_GRID_PHASE_RUNNING = ''' + + %s + + ''' + + MATCH_BOX = ''' +
+ %s +
+ ''' + + LEADERBOARD = ''' + + + + + + + + + + + + %s +
 KLASYFIKACJA KOŃCOWA 
 
 miejsce  drużyna 
+ ''' + + LEADERBOARD_ROW = ''' + + %d + +  %s  %s  + + + ''' + + LEADERBOARD_ROW_FLAG = ''' + + ''' + + LEADERBOARD_CAPTION_TABLE = ''' + + + + %s +
 
 LEGENDA 
+ ''' + + LEADERBOARD_CAPTION_TABLE_ROW = ''' + + +  %s  + + + ''' + + PAGE_HEAD = ''' + + + + + + + %s + %s + + + ''' + + PAGE_HEAD_REFRESH = ''' + + ''' + + PAGE_BODY = ''' + + %s + %s +

+ %s +

+ %s + %s + %s + ''' + + PAGE_BODY_FOOTER = ''' +

 Admin ©Jan Romański'2005, PlayOff ©Michał Klichowicz'2017-2018, strona wygenerowana %s

+ ''' + + PAGE = ''' + + + + %s + + + %s + + + ''' + + SWISS_LINK = ''' + [ %s ]

+ ''' + + SWISS_RUNNING_LINK = ''' + [  %s  ]

+ ''' + + SWISS_DEFAULT_LABEL = 'Turniej o %d. miejsce' + +class Template(object): + + @staticmethod + def get(string, *params): + return getattr(TemplateStrings, string).decode('utf8') % params -- cgit v1.2.3 From 0bb79de94e9bf78deb76dadb4a02133942665c6b Mon Sep 17 00:00:00 2001 From: emkael Date: Tue, 1 May 2018 13:01:32 +0200 Subject: Class names specified --- jfr_playoff/generator.py | 2 +- jfr_playoff/template.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index e5d29a8..ce3a6f8 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -1,6 +1,6 @@ from datetime import datetime -from jfr_playoff.template import Template as p_temp +from jfr_playoff.template import PlayoffTemplate as p_temp from jfr_playoff.data import PlayoffData diff --git a/jfr_playoff/template.py b/jfr_playoff/template.py index 0699c1e..64bc0e7 100644 --- a/jfr_playoff/template.py +++ b/jfr_playoff/template.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -class TemplateStrings(object): +class PlayoffTemplateStrings(object): MATCH_TABLE = ''' @@ -181,8 +181,8 @@ class TemplateStrings(object): SWISS_DEFAULT_LABEL = 'Turniej o %d. miejsce' -class Template(object): +class PlayoffTemplate(object): @staticmethod def get(string, *params): - return getattr(TemplateStrings, string).decode('utf8') % params + return getattr(PlayoffTemplateStrings, string).decode('utf8') % params -- cgit v1.2.3 From 72c30e7b12a75d43b64e31094bf3f9d3a899fe65 Mon Sep 17 00:00:00 2001 From: emkael Date: Tue, 1 May 2018 13:07:00 +0200 Subject: Instantation of template "engine" with i18n config --- jfr_playoff/generator.py | 56 +++++++++++++++++++++++++----------------------- jfr_playoff/template.py | 6 ++++-- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index ce3a6f8..34d12e7 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -1,6 +1,6 @@ from datetime import datetime -from jfr_playoff.template import PlayoffTemplate as p_temp +from jfr_playoff.template import PlayoffTemplate from jfr_playoff.data import PlayoffData @@ -14,6 +14,8 @@ class PlayoffGenerator(object): self.leaderboard_classes = {} if settings.has_section('position_styles'): self.leaderboard_classes = settings.get('position_styles') + self.p_temp = PlayoffTemplate( + settings.get('i18n') if settings.has_section('i18n') else {}) def generate_content(self): match_grid = self.get_match_grid( @@ -21,81 +23,81 @@ class PlayoffGenerator(object): self.data.generate_phases(), self.data.fill_match_info()) leaderboard_table = self.get_leaderboard_table() - return p_temp.get( + return self.p_temp.get( 'PAGE', - p_temp.get( + self.p_temp.get( 'PAGE_HEAD', - p_temp.get( + self.p_temp.get( 'PAGE_HEAD_REFRESH', self.page['refresh']) \ if self.page['refresh'] > 0 else '', self.page['title']), - p_temp.get( + self.p_temp.get( 'PAGE_BODY', self.page['logoh'], match_grid, self.get_swiss_links(), leaderboard_table, self.get_leaderboard_caption_table() if leaderboard_table else '', - p_temp.get( + self.p_temp.get( 'PAGE_BODY_FOOTER', datetime.now().strftime('%Y-%m-%d o %H:%M:%S')))) def get_match_table(self, match): rows = '' for team in match.teams: - score_html = p_temp.get('MATCH_SCORE', team.score) + score_html = self.p_temp.get('MATCH_SCORE', team.score) team_label = ' / '.join([ self.data.get_shortname(name) for name in team.name.split('
')]) - team_html = p_temp.get( + team_html = self.p_temp.get( 'MATCH_TEAM_LINK', match.link, team.name, team_label) \ if match.link is not None \ - else p_temp.get( + else self.p_temp.get( 'MATCH_TEAM_NON_LINK', team.name, team_label) - rows += p_temp.get( + rows += self.p_temp.get( 'MATCH_TEAM_ROW', ' '.join([ 'winner' if team.name == match.winner else '', 'loser' if team.name == match.loser else '' ]).strip(), team_html, - p_temp.get( + self.p_temp.get( 'MATCH_LINK', match.link, score_html) \ if match.link is not None else score_html) - html = p_temp.get( + html = self.p_temp.get( 'MATCH_TABLE', int(self.page['width'] * 0.75), int(self.page['width'] * 0.25), rows) if match.running > 0: - running_html = p_temp.get('MATCH_RUNNING', match.running) - html += p_temp.get('MATCH_LINK', match.link, running_html) if match.link is not None else running_html + running_html = self.p_temp.get('MATCH_RUNNING', match.running) + html += self.p_temp.get('MATCH_LINK', match.link, running_html) if match.link is not None else running_html return html def get_phase_header(self, phase, position): - grid_header = p_temp.get( + grid_header = self.p_temp.get( 'MATCH_GRID_PHASE_RUNNING' if phase.running \ else 'MATCH_GRID_PHASE', phase.title) if phase.link is not None: - return p_temp.get( + return self.p_temp.get( 'MATCH_GRID_PHASE_LINK', phase.link, self.page['width'], position, grid_header) else: - return p_temp.get( + return self.p_temp.get( 'MATCH_GRID_PHASE_NON_LINK', self.page['width'], position, grid_header) def get_match_box(self, match, position): if match is not None: - return p_temp.get( + return self.p_temp.get( 'MATCH_BOX', position[0], position[1], match.id, @@ -131,7 +133,7 @@ class PlayoffGenerator(object): (grid_x, grid_y)) row_no += 1 col_no += 1 - return p_temp.get( + return self.p_temp.get( 'MATCH_GRID', canvas_size[0], canvas_size[1], canvas_size[0], canvas_size[1], @@ -152,10 +154,10 @@ class PlayoffGenerator(object): rows = '' for style in self.leaderboard_classes: if 'caption' in style: - rows += p_temp.get( + rows += self.p_temp.get( 'LEADERBOARD_CAPTION_TABLE_ROW', style['class'], style['caption']) - return p_temp.get('LEADERBOARD_CAPTION_TABLE', rows) if rows else '' + return self.p_temp.get('LEADERBOARD_CAPTION_TABLE', rows) if rows else '' def get_leaderboard_table(self): leaderboard = self.data.fill_leaderboard() @@ -164,27 +166,27 @@ class PlayoffGenerator(object): position = 1 rows = '' for team in leaderboard: - rows += p_temp.get( + rows += self.p_temp.get( 'LEADERBOARD_ROW', self.get_leaderboard_row_class(position), position, self.get_flag(team), team or '') position += 1 - html = p_temp.get('LEADERBOARD', rows) + html = self.p_temp.get('LEADERBOARD', rows) return html def get_swiss_links(self): info = [] for event in self.data.get_swiss_info(): - event_label = p_temp.get('SWISS_DEFAULT_LABEL', event['position']) + event_label = self.p_temp.get('SWISS_DEFAULT_LABEL', event['position']) if 'label' in event and event['label'] is not None: event_label = event['label'] - info.append((p_temp.get('SWISS_LINK') \ + info.append((self.p_temp.get('SWISS_LINK') \ if event['finished'] \ - else p_temp.get( + else self.p_temp.get( 'SWISS_RUNNING_LINK', event['link'], event_label))) return '\n'.join(info) def get_flag(self, team): flag = self.data.get_team_image(team) - return '' if flag is None else p_temp.get('LEADERBOARD_ROW_FLAG', flag) + return '' if flag is None else self.p_temp.get('LEADERBOARD_ROW_FLAG', flag) diff --git a/jfr_playoff/template.py b/jfr_playoff/template.py index 64bc0e7..5fd0c45 100644 --- a/jfr_playoff/template.py +++ b/jfr_playoff/template.py @@ -183,6 +183,8 @@ class PlayoffTemplateStrings(object): class PlayoffTemplate(object): - @staticmethod - def get(string, *params): + def __init__(self, settings): + self.settings = settings + + def get(self, string, *params): return getattr(PlayoffTemplateStrings, string).decode('utf8') % params -- cgit v1.2.3 From 24a151f45b5104471a1a07e2a5aa72066cba3be9 Mon Sep 17 00:00:00 2001 From: emkael Date: Tue, 1 May 2018 13:44:35 +0200 Subject: Internationalization and localization of strings --- jfr_playoff/i18n.py | 31 +++++++++++++++++++++++++++++++ jfr_playoff/template.py | 21 ++++++++++++--------- 2 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 jfr_playoff/i18n.py diff --git a/jfr_playoff/i18n.py b/jfr_playoff/i18n.py new file mode 100644 index 0000000..0caa107 --- /dev/null +++ b/jfr_playoff/i18n.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +import re + +PLAYOFF_I18N_DEFAULTS = { + 'SCORE': 'wynik', + 'FINAL_STANDINGS': 'klasyfikacja końcowa', + 'STANDINGS_PLACE': 'miejsce', + 'STANDINGS_TEAM': 'drużyna', + 'STANDINGS_CAPTIONS': 'legenda', + 'FOOTER_GENERATED': 'strona wygenerowana', + 'SWISS_DEFAULT_LABEL': 'Turniej o %d. miejsce' +} + +class PlayoffI18N(object): + + def __init__(self, settings): + self.settings = settings + self.string_match = re.compile(r'{{(.*)}}') + + def localize(self, string): + return re.sub( + self.string_match, + lambda x: self.__get_translation(x.group(1)), + string) + + def __get_translation(self, string): + for dictionary in [self.settings, PLAYOFF_I18N_DEFAULTS]: + if string in dictionary: + return dictionary[string].decode('utf8') + return string diff --git a/jfr_playoff/template.py b/jfr_playoff/template.py index 5fd0c45..70c179b 100644 --- a/jfr_playoff/template.py +++ b/jfr_playoff/template.py @@ -1,12 +1,14 @@ # -*- coding: utf-8 -*- +from jfr_playoff.i18n import PlayoffI18N + class PlayoffTemplateStrings(object): MATCH_TABLE = '''
- + %s
  wynik  {{SCORE}} 
@@ -84,14 +86,14 @@ class PlayoffTemplateStrings(object): LEADERBOARD = ''' - + - - + + %s
 KLASYFIKACJA KOŃCOWA  {{FINAL_STANDINGS}} 
 
 miejsce  drużyna  {{STANDINGS_PLACE}}  {{STANDINGS_TEAM}} 
@@ -113,7 +115,7 @@ class PlayoffTemplateStrings(object): LEADERBOARD_CAPTION_TABLE = ''' - + %s
 
 LEGENDA 
 {{STANDINGS_CAPTIONS}} 
''' @@ -156,7 +158,7 @@ class PlayoffTemplateStrings(object): ''' PAGE_BODY_FOOTER = ''' -

 Admin ©Jan Romański'2005, PlayOff ©Michał Klichowicz'2017-2018, strona wygenerowana %s

+

 Admin ©Jan Romański'2005, PlayOff ©Michał Klichowicz'2017-2018, {{FOOTER_GENERATED}} %s

''' PAGE = ''' @@ -179,12 +181,13 @@ class PlayoffTemplateStrings(object): [  %s  ]

''' - SWISS_DEFAULT_LABEL = 'Turniej o %d. miejsce' + SWISS_DEFAULT_LABEL = '{{SWISS_DEFAULT_LABEL}}' class PlayoffTemplate(object): def __init__(self, settings): - self.settings = settings + self.i18n = PlayoffI18N(settings) def get(self, string, *params): - return getattr(PlayoffTemplateStrings, string).decode('utf8') % params + return self.i18n.localize( + getattr(PlayoffTemplateStrings, string).decode('utf8')) % params -- cgit v1.2.3 From eaa2912f4dc571cb92fb68a6b0f53cc5fbff2670 Mon Sep 17 00:00:00 2001 From: emkael Date: Tue, 1 May 2018 13:55:11 +0200 Subject: If i18n string is not found, pass-thru the original braces --- jfr_playoff/i18n.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jfr_playoff/i18n.py b/jfr_playoff/i18n.py index 0caa107..e22a563 100644 --- a/jfr_playoff/i18n.py +++ b/jfr_playoff/i18n.py @@ -28,4 +28,4 @@ class PlayoffI18N(object): for dictionary in [self.settings, PLAYOFF_I18N_DEFAULTS]: if string in dictionary: return dictionary[string].decode('utf8') - return string + return '{{%s}}' % (string) -- cgit v1.2.3 From 4fa864f8b75a3865c714a2b864784b1eb85c9c1b Mon Sep 17 00:00:00 2001 From: emkael Date: Tue, 1 May 2018 14:00:17 +0200 Subject: Documentation for i18n Fixes #25 --- CONFIG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CONFIG.md b/CONFIG.md index d4fa87f..906081f 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -36,6 +36,25 @@ Ustawienia bazy danych nie są wymagane, program potrafi sobie poradzić bez nic Ustawienia Gońca nie są wymagane, domyślnie jest on wyłączony, a przy włączeniu - domyślnie komunikuje się z `localhost` na porcie `8090`. +Tłumaczenia tekstów +------------------- + +Program obsługuje możliwość ustawienia własnych łańcuchów znaków wyświetlanych w różnych miejscach wynikowej strony. Teksty te opcjonalnie określa sekcja `"i18n"` pliku konfiguracyjnego. + +Domyślna postać wszystkich obsługiwanych łańcuchów to: + +``` +{ + "SCORE": "wynik", + "FINAL_STANDINGS": "klasyfikacja końcowa", + "STANDINGS_PLACE": "miejsce", + "STANDINGS_TEAM": "drużyna", + "STANDINGS_CAPTIONS": "legenda", + "FOOTER_GENERATED": "strona wygenerowana", + "SWISS_DEFAULT_LABEL": "Turniej o %d. miejsce" +} +``` + Zdalne pliki konfiguracyjne --------------------------- -- cgit v1.2.3 From b662da599a1cf673790648f5c0c7b6962e529bfa Mon Sep 17 00:00:00 2001 From: emkael Date: Tue, 1 May 2018 14:11:38 +0200 Subject: Debug info for i18n --- jfr_playoff/i18n.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/jfr_playoff/i18n.py b/jfr_playoff/i18n.py index e22a563..c4134a9 100644 --- a/jfr_playoff/i18n.py +++ b/jfr_playoff/i18n.py @@ -2,6 +2,8 @@ import re +from jfr_playoff.logger import PlayoffLogger + PLAYOFF_I18N_DEFAULTS = { 'SCORE': 'wynik', 'FINAL_STANDINGS': 'klasyfikacja końcowa', @@ -27,5 +29,10 @@ class PlayoffI18N(object): def __get_translation(self, string): for dictionary in [self.settings, PLAYOFF_I18N_DEFAULTS]: if string in dictionary: - return dictionary[string].decode('utf8') + translation = dictionary[string].decode('utf8') + PlayoffLogger.get('i18n').info( + 'translation for %s: %s', string, translation) + return translation + PlayoffLogger.get('i18n').info( + 'translation for %s not found', string) return '{{%s}}' % (string) -- cgit v1.2.3 From 5b527501aff6e529146a91364fa1a1b1485ef11e Mon Sep 17 00:00:00 2001 From: emkael Date: Tue, 22 May 2018 11:50:40 +0200 Subject: Fixing vertical positioning of match boxes if there's a single match in every phase --- jfr_playoff/generator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index 2b5aaf0..98bd3fc 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -109,7 +109,8 @@ class PlayoffGenerator(object): match_height = canvas_size[1] / len(phase.matches) row_no = 0 for match in phase.matches: - grid_y = int(row_no * match_height + + grid_y = self.page['margin'] / 2 if dimensions[1] == 1 else \ + int(row_no * match_height + 0.5 * (match_height - self.page['height'])) grid_boxes += self.get_match_box( matches[match] if match is not None else None, -- cgit v1.2.3 From 78a319e7babdcde172afae18a2f7e21d8d16cd4b Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 6 Jul 2018 01:24:40 +0200 Subject: Adjusting match box column widths to accommodate margins/borders, so the table fits the bounding box --- jfr_playoff/generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index 98bd3fc..0eaee85 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -55,8 +55,8 @@ class PlayoffGenerator(object): team_html, p_temp.MATCH_LINK % (match.link, score_html) if match.link is not None else score_html) html = p_temp.MATCH_TABLE.decode('utf8') % ( - int(self.page['width'] * 0.75), - int(self.page['width'] * 0.25), + int(self.page['width'] * 0.7), + int(self.page['width'] * 0.2), rows) if match.running > 0: running_html = p_temp.MATCH_RUNNING % (match.running) -- cgit v1.2.3 From 26ea3c9d1fab000b388a30e017719394c4d38289 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 6 Jul 2018 03:31:56 +0200 Subject: Adding example output directory to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ee704a0..cbf7992 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ playoff-*.zip build/* dist/* +output/* -- cgit v1.2.3 From c8bf307f4a5782b84e0ba5b0f204f01dcbcee9e9 Mon Sep 17 00:00:00 2001 From: emkael Date: Thu, 9 Aug 2018 00:51:55 +0200 Subject: Fixing parameters for swiss links --- jfr_playoff/generator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index 22bdc82..c4a053e 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -197,7 +197,8 @@ class PlayoffGenerator(object): event_label = self.p_temp.get('SWISS_DEFAULT_LABEL', event['position']) if 'label' in event and event['label'] is not None: event_label = event['label'] - info.append((self.p_temp.get('SWISS_LINK') \ + info.append((self.p_temp.get('SWISS_LINK', + event['link'], event_label) \ if event['finished'] \ else self.p_temp.get( 'SWISS_RUNNING_LINK', -- cgit v1.2.3 From 9a42b9f29d4b51f6e777cea0dc2bd750d102fa13 Mon Sep 17 00:00:00 2001 From: emkael Date: Thu, 9 Aug 2018 10:26:51 +0200 Subject: Shortening long (multiple) team labels --- jfr_playoff/generator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index 59c83a6..66e9a01 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -57,6 +57,7 @@ class PlayoffGenerator(object): team_label = ' / '.join([ self.data.get_shortname(name) for name in team.name.split('
')]) + team_label = team_label[:30] + (team_label[30:] and '(...)') team_html = self.p_temp.get( 'MATCH_TEAM_LINK', match.link, team.name, team_label) \ -- cgit v1.2.3 From 2b906f5038565f718e589cd70c24f4614fd0f4cf Mon Sep 17 00:00:00 2001 From: emkael Date: Thu, 9 Aug 2018 20:17:08 +0200 Subject: Scores and team names for single-segment matches --- jfr_playoff/matchinfo.py | 52 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/jfr_playoff/matchinfo.py b/jfr_playoff/matchinfo.py index 93b997a..2aeb2c1 100644 --- a/jfr_playoff/matchinfo.py +++ b/jfr_playoff/matchinfo.py @@ -110,11 +110,31 @@ class MatchInfo: row = self.__find_table_row(self.info.link) if row is None: raise ValueError('table row not found') - score_cell = row.select('td.bdc')[-1] - scores = [ - float(text) for text - in score_cell.contents - if isinstance(text, unicode)] + try: + scores = [ + float(text) for text + in row.select('td.bdc')[-1].contents + if isinstance(text, unicode)] + except ValueError: + try: + # running single-segment + scores = [ + float(text.strip()) for text + in row.select('td.bdcg a')[-1].contents + if isinstance(text, unicode)] + except IndexError: + # static single-segment + scores = [ + float(text.strip()) for text + in row.select('td.bdc a')[-1].contents + if isinstance(text, unicode)] + # carry-over + carry_over = [ + float(text.strip()) if len(text.strip()) > 0 else 0.0 for text + in row.select('td.bdc')[0].contents + if isinstance(text, unicode)] + for i in range(0, 2): + scores[i] += carry_over[i] team_names = [[text for text in link.contents if isinstance(text, unicode)][0].strip(u'\xa0') for link in row.select('a[onmouseover]')] @@ -247,17 +267,21 @@ class MatchInfo: row = self.__find_table_row(self.info.link) if row is None: raise ValueError('table row not found') - cells = row.select('td.bdc') - segments = [cell for cell in cells if self.__has_segment_link(cell)] - towels = [cell for cell in cells if self.__has_towel_image(cell)] - if len(segments) == 0: - if len(towels) > 0: - PlayoffLogger.get('matchinfo').info( - 'HTML board count for match #%d: all towels', self.info.id) - return 1, 1 # entire match is toweled, so mark as finished + for selector in ['td.bdc', 'td.bdcg']: + cells = row.select(selector) + segments = [cell for cell in cells if self.__has_segment_link(cell)] + towels = [cell for cell in cells if self.__has_towel_image(cell)] + if len(segments) == 0: + if len(towels) > 0: + PlayoffLogger.get('matchinfo').info( + 'HTML board count for match #%d: all towels', self.info.id) + return 1, 1 # entire match is toweled, so mark as finished else: - raise ValueError('segments not found') + break + if len(segments) == 0: + raise ValueError('segments not found') running_segments = row.select('td.bdca') + # FIXME: running single-segment match board count running_boards = sum([self.__get_html_running_boards(segment) for segment in running_segments]) finished_segments = [] boards_in_segment = None -- cgit v1.2.3 From 008b5ba8af443f337db3e79b4495a1d8275fe201 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 01:17:59 +0200 Subject: Adding changelog information, for clarity --- BRANCHES.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 BRANCHES.txt diff --git a/BRANCHES.txt b/BRANCHES.txt new file mode 100644 index 0000000..c752345 --- /dev/null +++ b/BRANCHES.txt @@ -0,0 +1,25 @@ +Względem ostatniego sezonu + +[master] [v1.1.2]: + - oznaczenia (style) w tabeli końcowej + +[bugfixes]: + - poprawki wymiarów (całego obszaru rysowania oraz odstępów między obszarami poszczególnych meczów) + +[i18n]: + - nowy silnik szablonów + - tłumaczenia + +[devel]: + - [i18n] + - [bugfixes] + - obszerne logowanie + - poprawki z Sopotu: + + bugfix dla linków do swissów + + skracanie wyświetlania długich nazw teamów (> 30 znaków, TODO: parametryzacja długości) + + obsługa meczów jednosegmentowych + +[position-boxes]: + - [devel] + - możliwość wyświetlania elementów grafu dla pozycji początkowych i pozycji końcowych + - możliwość dowolnego pozycjonowania meczów (zarówno w pionie w ramach fazy, jak i o zupełnie dowolnych współrzędnych) -- cgit v1.2.3 From 7a90aa51e77e201b51e8a9270c5e5da28cc3566f Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 01:24:09 +0200 Subject: Tracking merges and change information, for clarity --- BRANCHES.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/BRANCHES.txt b/BRANCHES.txt index c752345..653003d 100644 --- a/BRANCHES.txt +++ b/BRANCHES.txt @@ -13,9 +13,8 @@ Względem ostatniego sezonu [devel]: - [i18n] - [bugfixes] - - obszerne logowanie + - obszerne komunikaty diagnostyczne - poprawki z Sopotu: - + bugfix dla linków do swissów + skracanie wyświetlania długich nazw teamów (> 30 znaków, TODO: parametryzacja długości) + obsługa meczów jednosegmentowych -- cgit v1.2.3 From 714a198c3cd4f0975c57010b3888d98057e6bb7c Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 12:32:36 +0200 Subject: Customize label character limit Fixes #27 --- CONFIG.md | 1 + jfr_playoff/generator.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CONFIG.md b/CONFIG.md index 906081f..3a26f81 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -25,6 +25,7 @@ Konfiguracja składa się, po kolei, z: + `"refresh"` - parametr odświeżania strony drabinki: `0` = wyłączone, liczba naturalna = interwał odświeżania, w sekundach + `"width"` i `"height"` - wymiary (w pikselach) miejsca rezerwowanego dla każdego meczu w widoku drabinki (`"width"` bezpośrednio wpływa na rozmieszczanie kolumn, wewnątrz każdej z kolumn mecze rozmieszczane są równomiernie, w zależnie od ich liczby) + `"margin"` - odstęp między w/w miejscem (minimalny - jak widać, w przypadku mniejszej liczby meczów w fazie, odstępy się dopasują) + + `"label_length_limit"` - maksymalna liczba znaków wyświetlanych jako skrócona nazwa drużyn(y) w schemacie (`0` lub brak wartości oznacza brak limitu) - sekcji `"canvas"`: ustawień rysowania linii + `"winner_h_offset"`, `"winner_v_offset"` - marginesy (poziomy i pionowy) rysowania linii zwycięzców (odpowiednio: pionowych i poziomych, względem środka obszaru) + `"loser_h_offset"`, `"loser_v_offset"` - analogiczne marginesy rysowania linii przegranych diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index 66e9a01..5dfed13 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -57,7 +57,10 @@ class PlayoffGenerator(object): team_label = ' / '.join([ self.data.get_shortname(name) for name in team.name.split('
')]) - team_label = team_label[:30] + (team_label[30:] and '(...)') + label_max_length = self.page['label_length_limit'] \ + if 'label_length_limit' in self.page else 0 + if label_max_length: + team_label = team_label[:label_max_length] + (team_label[label_max_length:] and '(...)') team_html = self.p_temp.get( 'MATCH_TEAM_LINK', match.link, team.name, team_label) \ -- cgit v1.2.3 From 528bbb28fafdef9aa107149fa5f2a90dccfa00f9 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 14:30:19 +0200 Subject: Comments on the code for single-segment match team/score fetching --- jfr_playoff/matchinfo.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jfr_playoff/matchinfo.py b/jfr_playoff/matchinfo.py index 2aeb2c1..b40d6d1 100644 --- a/jfr_playoff/matchinfo.py +++ b/jfr_playoff/matchinfo.py @@ -116,6 +116,7 @@ class MatchInfo: in row.select('td.bdc')[-1].contents if isinstance(text, unicode)] except ValueError: + # single-segment match try: # running single-segment scores = [ @@ -272,11 +273,14 @@ class MatchInfo: segments = [cell for cell in cells if self.__has_segment_link(cell)] towels = [cell for cell in cells if self.__has_towel_image(cell)] if len(segments) == 0: + # in single-segment match, there are no td.bdc cells with segment links + # but maybe it's a multi-segment match with towels if len(towels) > 0: PlayoffLogger.get('matchinfo').info( 'HTML board count for match #%d: all towels', self.info.id) return 1, 1 # entire match is toweled, so mark as finished else: + # not a single-segment match, no need to look for td.bdcg cells break if len(segments) == 0: raise ValueError('segments not found') -- cgit v1.2.3 From b1ce81da1478715b1d3a8dd6883c5655db0a8f68 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 14:30:55 +0200 Subject: Fixing score fetch for a single-segment match with a towel --- jfr_playoff/matchinfo.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/jfr_playoff/matchinfo.py b/jfr_playoff/matchinfo.py index b40d6d1..f726282 100644 --- a/jfr_playoff/matchinfo.py +++ b/jfr_playoff/matchinfo.py @@ -124,11 +124,15 @@ class MatchInfo: in row.select('td.bdcg a')[-1].contents if isinstance(text, unicode)] except IndexError: - # static single-segment - scores = [ - float(text.strip()) for text - in row.select('td.bdc a')[-1].contents - if isinstance(text, unicode)] + try: + # static single-segment + scores = [ + float(text.strip()) for text + in row.select('td.bdc a')[-1].contents + if isinstance(text, unicode)] + except IndexError: + # toweled single-segment + scores = [0.0, 0.0] # carry-over carry_over = [ float(text.strip()) if len(text.strip()) > 0 else 0.0 for text -- cgit v1.2.3 From cb1edf3fb102f347adde2a1c8aec205c24cd36be Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 14:31:35 +0200 Subject: Fixing carry-over fetch for a single-segment match if there's none --- jfr_playoff/matchinfo.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jfr_playoff/matchinfo.py b/jfr_playoff/matchinfo.py index f726282..d968a34 100644 --- a/jfr_playoff/matchinfo.py +++ b/jfr_playoff/matchinfo.py @@ -138,6 +138,9 @@ class MatchInfo: float(text.strip()) if len(text.strip()) > 0 else 0.0 for text in row.select('td.bdc')[0].contents if isinstance(text, unicode)] + if len(carry_over) < 2: + # no carry-over, possibly no carry-over cells or empty + carry_over = [0.0, 0.0] for i in range(0, 2): scores[i] += carry_over[i] team_names = [[text for text in link.contents -- cgit v1.2.3 From e23ef3b9dc245d134e4b0105e2dca7a1833f1ba5 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 14:32:23 +0200 Subject: Fixing overall bourd count fetch for a single-segment match --- jfr_playoff/matchinfo.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/jfr_playoff/matchinfo.py b/jfr_playoff/matchinfo.py index d968a34..900c210 100644 --- a/jfr_playoff/matchinfo.py +++ b/jfr_playoff/matchinfo.py @@ -292,7 +292,6 @@ class MatchInfo: if len(segments) == 0: raise ValueError('segments not found') running_segments = row.select('td.bdca') - # FIXME: running single-segment match board count running_boards = sum([self.__get_html_running_boards(segment) for segment in running_segments]) finished_segments = [] boards_in_segment = None @@ -303,10 +302,14 @@ class MatchInfo: finished_segments.append(segment) if boards_in_segment is None and boards > 0: boards_in_segment = boards - PlayoffLogger.get('matchinfo').info( - 'HTML board count for match #%d, found: %d finished segments, %d towels, %d boards per segment and %d boards in running segment', - self.info.id, len(finished_segments), len(towels), boards_in_segment, running_boards) - total_boards = (len(segments) + len(towels) + len(running_segments)) * boards_in_segment + if 'bdcg' in segments[0]['class']: + # only a single-segment match will yield td.bdcg cells with segment scores + total_boards = boards_in_segment + else: + PlayoffLogger.get('matchinfo').info( + 'HTML board count for match #%d, found: %d finished segments, %d towels, %d boards per segment and %d boards in running segment', + self.info.id, len(finished_segments), len(towels), boards_in_segment, running_boards) + total_boards = (len(segments) + len(towels) + len(running_segments)) * boards_in_segment played_boards = (len(towels) + len(finished_segments)) * boards_in_segment + running_boards PlayoffLogger.get('matchinfo').info( 'HTML board count for match #%d: %d/%d', -- cgit v1.2.3 From 1ab6233374e1c25e3f9f58db98b547125cbef452 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 15:46:28 +0200 Subject: Revert running segment link if there are no scores in the segment (possibly "Don't send scores") Part of #7 is implented by this change --- jfr_playoff/matchinfo.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/jfr_playoff/matchinfo.py b/jfr_playoff/matchinfo.py index 900c210..dd2ee30 100644 --- a/jfr_playoff/matchinfo.py +++ b/jfr_playoff/matchinfo.py @@ -245,6 +245,15 @@ class MatchInfo: def __get_html_running_boards(self, cell): return int(cell.contents[-1].strip()) + def __get_html_segment_board_count(self, segment_url): + segment_content = p_remote.fetch(segment_url) + board_rows = [row for row in segment_content.find_all('tr') if len(row.select('td.bdcc a.zb')) > 0] + board_count = len(board_rows) + played_boards = len([ + row for row in board_rows if len( + ''.join([cell.text.strip() for cell in row.select('td.bdc')])) > 0]) + return played_boards, board_count + def __get_finished_info(self, cell): segment_link = cell.select('a[href]') if len(segment_link) > 0: @@ -252,12 +261,7 @@ class MatchInfo: r'\.htm$', '.html', urljoin(self.info.link, segment_link[0]['href'])) try: - segment_content = p_remote.fetch(segment_url) - board_rows = [row for row in segment_content.find_all('tr') if len(row.select('td.bdcc a.zb')) > 0] - board_count = len(board_rows) - played_boards = len([ - row for row in board_rows if len( - ''.join([cell.text.strip() for cell in row.select('td.bdc')])) > 0]) + played_boards, board_count = self.__get_html_segment_board_count(segment_url) PlayoffLogger.get('matchinfo').info( 'HTML played boards count for segment: %d/%d', played_boards, board_count) @@ -373,6 +377,7 @@ class MatchInfo: def __determine_running_link(self): if self.info.link is None: return + match_link = self.info.link link_match = re.match(r'^(.*)runda(\d+)\.html$', self.info.link) if link_match: try: @@ -390,6 +395,21 @@ class MatchInfo: PlayoffLogger.get('matchinfo').warning( 'cannot determine running link from HTML for match #%d: %s(%s)', self.info.id, type(e).__name__, str(e)) + if self.info.link != match_link: + # we've detected a running segment link + # we should check if the segment's uploaded live + try: + boards_played, board_count = self.__get_html_segment_board_count(re.sub('\.htm$', '.html', self.info.link)) + except IOError as e: + PlayoffLogger.get('matchinfo').warning( + 'cannot determine running link (%s) board count for match #%d: %s(%s)', + self.info.link, self.info.id, type(e).__name__, str(e)) + boards_played = 0 + if not boards_played: + PlayoffLogger.get('matchinfo').warning( + 'running link (%s) for match #%d is not live, reverting to match link (%s)', + self.info.link, self.info.id, match_link) + self.info.link = match_link def set_phase_link(self, phase_link): if self.info.link is None: -- cgit v1.2.3 From fe41a61c97e558abec388bab81c041ae562fce37 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 15:52:02 +0200 Subject: Debug information on remote URL fetch --- jfr_playoff/remote.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jfr_playoff/remote.py b/jfr_playoff/remote.py index 2deb7ce..f0bf6c2 100644 --- a/jfr_playoff/remote.py +++ b/jfr_playoff/remote.py @@ -21,4 +21,7 @@ class RemoteUrl: if encoding_match: request.encoding = encoding_match.group(2) cls.url_cache[url] = request.text + PlayoffLogger.get('remote').info( + 'fetched %d bytes from remote location', + len(cls.url_cache[url])) return bs(cls.url_cache[url], 'lxml') -- cgit v1.2.3 From f01a522694aad85c857470b9a8630c6194a38d36 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 15:52:25 +0200 Subject: Additional info on features since last merge --- BRANCHES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/BRANCHES.txt b/BRANCHES.txt index 653003d..5eb0252 100644 --- a/BRANCHES.txt +++ b/BRANCHES.txt @@ -17,6 +17,7 @@ Względem ostatniego sezonu - poprawki z Sopotu: + skracanie wyświetlania długich nazw teamów (> 30 znaków, TODO: parametryzacja długości) + obsługa meczów jednosegmentowych + - nielinkowanie do segmentów, które nie wysyłają wyników na żywo [position-boxes]: - [devel] -- cgit v1.2.3 From f5ac370c094ae393dc0348bbbf31bef1fe9a7208 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 15:57:41 +0200 Subject: Updating branch feature info --- BRANCHES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BRANCHES.txt b/BRANCHES.txt index 5eb0252..ee53fe4 100644 --- a/BRANCHES.txt +++ b/BRANCHES.txt @@ -15,7 +15,7 @@ Względem ostatniego sezonu - [bugfixes] - obszerne komunikaty diagnostyczne - poprawki z Sopotu: - + skracanie wyświetlania długich nazw teamów (> 30 znaków, TODO: parametryzacja długości) + + skracanie wyświetlania długich nazw teamów + obsługa meczów jednosegmentowych - nielinkowanie do segmentów, które nie wysyłają wyników na żywo -- cgit v1.2.3 From 72e52cf8c572f262d022acaa1eb1aa65bccb099e Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 28 Sep 2018 17:37:16 +0200 Subject: Refactoring of some config dictionary default value fetches --- jfr_playoff/data.py | 37 +++++++++++++------------------------ jfr_playoff/generator.py | 5 ++--- jfr_playoff/matchinfo.py | 5 +---- jfr_playoff/tournamentinfo.py | 7 ++++--- 4 files changed, 20 insertions(+), 34 deletions(-) diff --git a/jfr_playoff/data.py b/jfr_playoff/data.py index 9f9a0c2..6adf00c 100644 --- a/jfr_playoff/data.py +++ b/jfr_playoff/data.py @@ -36,18 +36,16 @@ class PlayoffData(object): def generate_phases(self): self.grid = [] for phase in self.phases: - phase_count = len(phase['matches']) - if 'dummies' in phase: - phase_count += len(phase['dummies']) + dummies = phase.get('dummies', []) + phase_count = len(phase['matches']) + len(dummies) phase_object = Phase() phase_object.title = phase['title'] - phase_object.link = phase['link'] if 'link' in phase else None + phase_object.link = phase.get('link', None) phase_object.matches = [None] * phase_count phase_pos = 0 for match in phase['matches']: - if 'dummies' in phase: - while phase_pos in phase['dummies']: - phase_pos += 1 + while phase_pos in dummies: + phase_pos += 1 phase_object.matches[phase_pos] = match['id'] phase_pos += 1 PlayoffLogger.get('data').info('phase object: %s', phase_object) @@ -73,8 +71,7 @@ class PlayoffData(object): def get_swiss_link(self, event): event_info = TournamentInfo(event, self.database) swiss_link = event_info.get_results_link() - if ('relative_path' in event) and ( - event['relative_path'] is not None): + if event.get('relative_path', None): swiss_link = '%s/%s' % (event['relative_path'], swiss_link) PlayoffLogger.get('data').info('swiss link: %s', swiss_link) return swiss_link @@ -94,16 +91,8 @@ class PlayoffData(object): event['ties'] = teams event_info = TournamentInfo(event, self.database) if event_info.is_finished(): - swiss_position = ( - event['swiss_position'] - if 'swiss_position' in event - else 1 - ) - position_limit = ( - event['position_to'] - if 'position_to' in event - else 9999 - ) + swiss_position = event.get('swiss_position', 1) + position_limit = event.get('position_to', 9999) place = 1 swiss_results = event_info.get_tournament_results() for team in swiss_results: @@ -156,7 +145,7 @@ class PlayoffData(object): swiss_info = [{ 'link': self.get_swiss_link(event), 'position': event['position'], - 'label': event['label'] if 'label' in event else None, + 'label': event.get('label', None), 'finished': TournamentInfo(event, self.database).is_finished() } for event in self.swiss] PlayoffLogger.get('data').info('swiss info: %s', swiss_info) @@ -166,10 +155,10 @@ class PlayoffData(object): dimensions = ( len(self.phases), max([ - len(phase['matches']) + len(phase['dummies']) - if 'dummies' in phase - else len(phase['matches']) - for phase in self.phases])) + len(phase['matches']) + len(phase.get('dummies', [])) + for phase in self.phases + ]) + ) PlayoffLogger.get('data').info('grid dimensions: %s', dimensions) return dimensions diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py index 5dfed13..d358d86 100644 --- a/jfr_playoff/generator.py +++ b/jfr_playoff/generator.py @@ -57,8 +57,7 @@ class PlayoffGenerator(object): team_label = ' / '.join([ self.data.get_shortname(name) for name in team.name.split('
')]) - label_max_length = self.page['label_length_limit'] \ - if 'label_length_limit' in self.page else 0 + label_max_length = self.page.get('label_length_limit', 0) if label_max_length: team_label = team_label[:label_max_length] + (team_label[label_max_length:] and '(...)') team_html = self.p_temp.get( @@ -199,7 +198,7 @@ class PlayoffGenerator(object): info = [] for event in self.data.get_swiss_info(): event_label = self.p_temp.get('SWISS_DEFAULT_LABEL', event['position']) - if 'label' in event and event['label'] is not None: + if event.get('label', None): event_label = event['label'] info.append((self.p_temp.get('SWISS_LINK', event['link'], event_label) \ diff --git a/jfr_playoff/matchinfo.py b/jfr_playoff/matchinfo.py index dd2ee30..ab742d4 100644 --- a/jfr_playoff/matchinfo.py +++ b/jfr_playoff/matchinfo.py @@ -191,10 +191,7 @@ class MatchInfo: if scores_fetched: PlayoffLogger.get('matchinfo').info( 'pre-defined scores for match #%d fetched', self.info.id) - if 'running' in self.config: - self.info.running = int(self.config['running']) - else: - self.info.running = -1 + self.info.running = int(self.config.get('running', -1)) if not teams_fetched: try: try: diff --git a/jfr_playoff/tournamentinfo.py b/jfr_playoff/tournamentinfo.py index a646d4e..45a7752 100644 --- a/jfr_playoff/tournamentinfo.py +++ b/jfr_playoff/tournamentinfo.py @@ -169,11 +169,12 @@ class TournamentInfo: PlayoffLogger.get('tournamentinfo').warning( 'cannot determine tournament results from HTML: %s(%s)', type(e).__name__, str(e)) - if self.is_finished() and 'final_positions' in self.settings: + if self.is_finished(): + final_positions = self.settings.get('final_positions', []) PlayoffLogger.get('tournamentinfo').info( 'setting final positions from tournament results: %s', - self.settings['final_positions']) - for position in self.settings['final_positions']: + final_positions) + for position in final_positions: if len(teams) >= position: teams[position-1].append(position) return teams -- cgit v1.2.3 From 524d09e5cd6d1e026d706aa8c0eac346c26c6433 Mon Sep 17 00:00:00 2001 From: emkael Date: Wed, 17 Oct 2018 19:45:20 +0200 Subject: Version number bump --- .version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.version b/.version index 15a8060..a27e39d 100644 --- a/.version +++ b/.version @@ -1,8 +1,8 @@ # UTF-8 VSVersionInfo( ffi=FixedFileInfo( - filevers=(1, 1, 2, 0), - prodvers=(1, 1, 2, 0), + filevers=(1, 2, 0, 0), + prodvers=(1, 2, 0, 0), # Contains a bitmask that specifies the valid bits 'flags' mask=0x3f, # Contains a bitmask that specifies the Boolean attributes of the file. @@ -26,10 +26,10 @@ VSVersionInfo( u'040904b0', # 0x0409(1033)= English, 0x04b0(1200)= UTF-8 [StringStruct(u'CompanyName', u'emkael.info'), StringStruct(u'ProductName', u'teamy_playoff'), - StringStruct(u'ProductVersion', u'1, 1, 2, 0'), + StringStruct(u'ProductVersion', u'1, 2, 0, 0'), StringStruct(u'InternalName', u'teamy_playoff'), StringStruct(u'OriginalFilename', u'playoff.exe'), - StringStruct(u'FileVersion', u'1, 1, 2, 0'), + StringStruct(u'FileVersion', u'1, 2, 0, 0'), StringStruct( u'FileDescription', u'Play-off visualisation for JFR Teamy events'), -- cgit v1.2.3 From 31fc51ce22e7c5197ed367cadd14d8a258f8fd65 Mon Sep 17 00:00:00 2001 From: emkael Date: Wed, 17 Oct 2018 19:58:44 +0200 Subject: Removing branch information --- BRANCHES.txt | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 BRANCHES.txt diff --git a/BRANCHES.txt b/BRANCHES.txt deleted file mode 100644 index ee53fe4..0000000 --- a/BRANCHES.txt +++ /dev/null @@ -1,25 +0,0 @@ -Względem ostatniego sezonu - -[master] [v1.1.2]: - - oznaczenia (style) w tabeli końcowej - -[bugfixes]: - - poprawki wymiarów (całego obszaru rysowania oraz odstępów między obszarami poszczególnych meczów) - -[i18n]: - - nowy silnik szablonów - - tłumaczenia - -[devel]: - - [i18n] - - [bugfixes] - - obszerne komunikaty diagnostyczne - - poprawki z Sopotu: - + skracanie wyświetlania długich nazw teamów - + obsługa meczów jednosegmentowych - - nielinkowanie do segmentów, które nie wysyłają wyników na żywo - -[position-boxes]: - - [devel] - - możliwość wyświetlania elementów grafu dla pozycji początkowych i pozycji końcowych - - możliwość dowolnego pozycjonowania meczów (zarówno w pionie w ramach fazy, jak i o zupełnie dowolnych współrzędnych) -- cgit v1.2.3