summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoremkael <emkael@tlen.pl>2019-12-30 13:00:32 +0100
committeremkael <emkael@tlen.pl>2019-12-30 13:00:32 +0100
commit26446dc62087c16925b1bae5f2861c8142dec4d0 (patch)
treecf409f5b3df22d08ff5745cc1907faedef6b07c1
parentb33b987e2fc217a488c40979c219deaf536498eb (diff)
Refactoring package structure (MatchInfo together with TournamentInfo)
-rw-r--r--jfr_playoff/data/__init__.py3
-rw-r--r--jfr_playoff/data/info.py465
-rw-r--r--jfr_playoff/data/match/__init__.py467
3 files changed, 466 insertions, 469 deletions
diff --git a/jfr_playoff/data/__init__.py b/jfr_playoff/data/__init__.py
index 638c01f..8c94466 100644
--- a/jfr_playoff/data/__init__.py
+++ b/jfr_playoff/data/__init__.py
@@ -2,8 +2,7 @@ from cached_property import cached_property
from jfr_playoff.db import PlayoffDB
from jfr_playoff.dto import Phase
-from jfr_playoff.data.match import MatchInfo
-from jfr_playoff.data.info import TournamentInfo
+from jfr_playoff.data.info import TournamentInfo, MatchInfo
from jfr_playoff.logger import PlayoffLogger
diff --git a/jfr_playoff/data/info.py b/jfr_playoff/data/info.py
index cb84b75..aec78c2 100644
--- a/jfr_playoff/data/info.py
+++ b/jfr_playoff/data/info.py
@@ -1,3 +1,9 @@
+import re
+from urlparse import urljoin
+
+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.logger import PlayoffLogger
@@ -67,3 +73,462 @@ class TournamentInfo(ResultInfo):
def get_results_link(self, suffix='leaderb.html'):
return self.call_client('get_results_link', None, suffix)
+
+
+class MatchInfo(ResultInfo):
+
+ matches = {}
+
+ def __init__(self, match_config, teams, database, aliases=None):
+ self.config = match_config
+ self.teams = teams
+ self.database = database
+ self.aliases = {}
+ if aliases:
+ for team, team_aliases in aliases.iteritems():
+ for alias in team_aliases:
+ self.aliases[alias] = team
+ ResultInfo.__init__(self, match_config, database)
+ self.info = Match()
+ self.__init_info()
+ self.__fetch_match_link()
+
+ def fill_client_list(self, settings, database):
+ return []
+
+ def __init_info(self):
+ self.info.id = self.config['id']
+ MatchInfo.matches[self.info.id] = self.info
+ self.info.running = 0
+ self.info.winner_matches = []
+ self.info.loser_matches = []
+ for i in range(0, 2):
+ if 'winner' in self.config['teams'][i]:
+ self.info.winner_matches += self.config['teams'][i]['winner']
+ if 'loser' in self.config['teams'][i]:
+ self.info.loser_matches += self.config['teams'][i]['loser']
+ self.info.winner_matches = list(set(self.info.winner_matches))
+ self.info.loser_matches = list(set(self.info.loser_matches))
+ self.info.winner_place = self.config.get('winner', [])
+ self.info.loser_place = self.config.get('loser', [])
+ self.info.teams = []
+
+ 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)
+ else:
+ PlayoffLogger.get('matchinfo').info('match #%d link empty', self.info.id)
+
+ def __get_predefined_scores(self):
+ teams = [Team(), Team()]
+ scores_fetched = False
+ teams_fetched = False
+ if 'score' in self.config:
+ i = 0
+ for score in self.config['score']:
+ if isinstance(self.config['score'], dict):
+ teams[i].score = self.config['score'][score]
+ try:
+ team_no = int(score)
+ teams[i].name = [self.teams[team_no-1][0]]
+ except ValueError:
+ teams[i].name = [score]
+ teams_fetched = True
+ else:
+ teams[i].score = score
+ i += 1
+ 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):
+ row = self.database.fetch(
+ self.config['database'], p_sql.MATCH_RESULTS,
+ (self.config['table'], self.config['round']))
+ for i in range(0, 2):
+ teams[i].name = [row[i]]
+ teams[i].known_teams = 1
+ if fetch_scores:
+ teams[0].score = row[3] + row[5]
+ teams[1].score = row[4] + row[6]
+ if row[2] > 0:
+ 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):
+ html_content = p_remote.fetch(url)
+ 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):
+ if self.info.link is None:
+ raise ValueError('link not set')
+ row = self.__find_table_row(self.info.link)
+ if row is None:
+ raise ValueError('table row not found')
+ try:
+ scores = [
+ float(text) for text
+ in row.select('td.bdc')[-1].contents
+ if isinstance(text, unicode)]
+ except ValueError:
+ # single-segment match
+ try:
+ # running single-segment
+ scores = [
+ float(text.strip()) for text
+ in row.select('td.bdcg a')[-1].contents
+ if isinstance(text, unicode)]
+ except IndexError:
+ 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
+ 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
+ if isinstance(text, unicode)][0].strip(u'\xa0')
+ for link in row.select('a[onmouseover]')]
+ for i in range(0, 2):
+ teams[i].name = [team_names[i]]
+ teams[i].known_teams = 1
+ 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):
+ for i in range(0, 2):
+ match_teams = []
+ possible_teams = []
+ if isinstance(self.config['teams'][i], basestring):
+ match_teams = [self.config['teams'][i]]
+ elif isinstance(self.config['teams'][i], list):
+ match_teams = self.config['teams'][i]
+ else:
+ if 'winner' in self.config['teams'][i]:
+ match_teams += [
+ MatchInfo.matches[winner_match].winner
+ for winner_match in self.config['teams'][i]['winner']]
+ possible_teams += [
+ MatchInfo.matches[winner_match].possible_winner
+ for winner_match in self.config['teams'][i]['winner']]
+ if 'loser' in self.config['teams'][i]:
+ match_teams += [
+ MatchInfo.matches[loser_match].loser
+ for loser_match in self.config['teams'][i]['loser']]
+ possible_teams += [
+ MatchInfo.matches[loser_match].possible_loser
+ for loser_match in self.config['teams'][i]['loser']]
+ if 'place' in self.config['teams'][i]:
+ match_teams += [
+ self.teams[place-1][0]
+ for place in self.config['teams'][i]['place']]
+ teams[i].name = match_teams
+ teams[i].possible_name = possible_teams
+ teams[i].known_teams = len([team for team in match_teams if team is not None])
+ teams[i].selected_team = self.config['selected_teams'][i] if 'selected_teams' in self.config else -1
+ PlayoffLogger.get('matchinfo').info(
+ 'config scores for match #%d: %s',
+ self.info.id, teams)
+ return teams
+
+ def __resolve_team_aliases(self, teams):
+ return [self.aliases[team] if team in self.aliases else team for team in 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)
+ self.info.running = int(self.config.get('running', -1))
+ if not teams_fetched:
+ try:
+ try:
+ if self.database is None:
+ raise KeyError('database not configured')
+ if 'database' not in self.config:
+ raise KeyError('database not configured')
+ self.info.teams = self.__get_db_teams(
+ self.info.teams, not scores_fetched)
+ 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__, 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__, str(e))
+ self.info.teams = self.__get_config_teams(self.info.teams)
+ for team in range(0, len(self.info.teams)):
+ if isinstance(self.config['teams'][team], dict):
+ self.info.teams[team].place = self.config['teams'][team].get(
+ 'place', self.info.teams[team].place)
+ self.info.teams[team].name = self.__resolve_team_aliases(self.info.teams[team].name)
+ PlayoffLogger.get('matchinfo').info('team list after resolving aliases: %s', self.info.teams[team].name)
+ self.info.teams[team].possible_name = self.__resolve_team_aliases(self.info.teams[team].possible_name)
+ PlayoffLogger.get('matchinfo').info('predicted team list after resolving aliases: %s', self.info.teams[team].possible_name)
+
+
+ def __get_db_board_count(self):
+ towels = self.database.fetch(
+ self.config['database'], p_sql.TOWEL_COUNT,
+ (self.config['table'], self.config['round']))
+ row = [0 if r is None
+ else r for r in
+ self.database.fetch(
+ self.config['database'], p_sql.BOARD_COUNT,
+ (self.config['table'], self.config['round']))]
+ boards_to_play = int(row[0])
+ 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):
+ links = [link for link in cell.select('a[href]')
+ if re.match(r'^.*\d+t\d+-\d+\.htm$', link['href'])]
+ return len(links) > 0
+
+ def __has_towel_image(self, cell):
+ return len(cell.select('img[alt="towel"]')) > 0
+
+ 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:
+ segment_url = re.sub(
+ r'\.htm$', '.html',
+ urljoin(self.info.link, segment_link[0]['href']))
+ try:
+ 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)
+ return board_count, played_boards >= board_count
+ except IOError as e:
+ PlayoffLogger.get('matchinfo').info(
+ 'cannot fetch HTML played boards count for segment: %s(%s)',
+ self.info.id, type(e).__name__, str(e))
+ return 0, False
+ return 0, False
+
+ def __get_html_board_count(self):
+ if self.info.link is None:
+ raise ValueError('link not set')
+ row = self.__find_table_row(self.info.link)
+ if row is None:
+ raise ValueError('table row not found')
+ 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:
+ # 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')
+ running_segments = row.select('td.bdca')
+ running_boards = sum([self.__get_html_running_boards(segment) for segment in running_segments])
+ finished_segments = []
+ boards_in_segment = None
+ for segment in segments:
+ if segment not in running_segments:
+ boards, is_finished = self.__get_finished_info(segment)
+ if is_finished:
+ finished_segments.append(segment)
+ if boards_in_segment is None and boards > 0:
+ boards_in_segment = boards
+ 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',
+ self.info.id, played_boards, total_boards)
+ return played_boards, total_boards
+
+ def __fetch_board_count(self):
+ boards_played = 0
+ boards_to_play = 0
+ try:
+ 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) as e:
+ PlayoffLogger.get('matchinfo').warning(
+ 'fetching board count from DB for match #%d failed: %s(%s)',
+ 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__, str(e))
+ pass
+ if boards_played > 0:
+ self.info.running = -1 \
+ if boards_played >= boards_to_play \
+ else boards_played
+
+ def __determine_outcome(self):
+ if (self.info.teams[0].known_teams == 1) \
+ and (self.info.teams[1].known_teams == 1):
+ if self.info.running == -1:
+ if self.info.teams[0].score > self.info.teams[1].score:
+ self.info.winner = self.info.teams[0].name[0]
+ self.info.loser = self.info.teams[1].name[0]
+ else:
+ self.info.loser = self.info.teams[0].name[0]
+ self.info.winner = self.info.teams[1].name[0]
+ elif self.info.running > 0:
+ if self.info.teams[0].score > self.info.teams[1].score:
+ self.info.possible_winner = self.info.teams[0].name[0]
+ self.info.possible_loser = self.info.teams[1].name[0]
+ elif self.info.teams[0].score < self.info.teams[1].score:
+ self.info.possible_loser = self.info.teams[0].name[0]
+ self.info.possible_winner = self.info.teams[1].name[0]
+
+ def __get_db_running_link(self, prefix, round_no):
+ 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)
+
+ def __get_html_running_link(self):
+ if self.info.link is None:
+ raise ValueError('link not set')
+ row = self.__find_table_row(self.info.link)
+ 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):
+ 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:
+ if self.database is None:
+ 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) as e:
+ PlayoffLogger.get('matchinfo').warning(
+ 'cannot determine running link from DB for match #%d: %s(%s)',
+ 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__, 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:
+ self.info.link = phase_link
+ 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()
+ self.__fetch_board_count()
+ self.__determine_outcome()
+ if self.info.running > 0:
+ self.__determine_running_link()
+ return self.info
diff --git a/jfr_playoff/data/match/__init__.py b/jfr_playoff/data/match/__init__.py
index d61f8f8..e69de29 100644
--- a/jfr_playoff/data/match/__init__.py
+++ b/jfr_playoff/data/match/__init__.py
@@ -1,467 +0,0 @@
-import re
-from urlparse import urljoin
-
-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.data.info import ResultInfo, TournamentInfo
-from jfr_playoff.logger import PlayoffLogger
-
-
-class MatchInfo(ResultInfo):
-
- matches = {}
-
- def __init__(self, match_config, teams, database, aliases=None):
- self.config = match_config
- self.teams = teams
- self.database = database
- self.aliases = {}
- if aliases:
- for team, team_aliases in aliases.iteritems():
- for alias in team_aliases:
- self.aliases[alias] = team
- ResultInfo.__init__(self, match_config, database)
- self.info = Match()
- self.__init_info()
- self.__fetch_match_link()
-
- def fill_client_list(self, settings, database):
- return []
-
- def __init_info(self):
- self.info.id = self.config['id']
- MatchInfo.matches[self.info.id] = self.info
- self.info.running = 0
- self.info.winner_matches = []
- self.info.loser_matches = []
- for i in range(0, 2):
- if 'winner' in self.config['teams'][i]:
- self.info.winner_matches += self.config['teams'][i]['winner']
- if 'loser' in self.config['teams'][i]:
- self.info.loser_matches += self.config['teams'][i]['loser']
- self.info.winner_matches = list(set(self.info.winner_matches))
- self.info.loser_matches = list(set(self.info.loser_matches))
- self.info.winner_place = self.config.get('winner', [])
- self.info.loser_place = self.config.get('loser', [])
- self.info.teams = []
-
- 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)
- else:
- PlayoffLogger.get('matchinfo').info('match #%d link empty', self.info.id)
-
- def __get_predefined_scores(self):
- teams = [Team(), Team()]
- scores_fetched = False
- teams_fetched = False
- if 'score' in self.config:
- i = 0
- for score in self.config['score']:
- if isinstance(self.config['score'], dict):
- teams[i].score = self.config['score'][score]
- try:
- team_no = int(score)
- teams[i].name = [self.teams[team_no-1][0]]
- except ValueError:
- teams[i].name = [score]
- teams_fetched = True
- else:
- teams[i].score = score
- i += 1
- 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):
- row = self.database.fetch(
- self.config['database'], p_sql.MATCH_RESULTS,
- (self.config['table'], self.config['round']))
- for i in range(0, 2):
- teams[i].name = [row[i]]
- teams[i].known_teams = 1
- if fetch_scores:
- teams[0].score = row[3] + row[5]
- teams[1].score = row[4] + row[6]
- if row[2] > 0:
- 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):
- html_content = p_remote.fetch(url)
- 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):
- if self.info.link is None:
- raise ValueError('link not set')
- row = self.__find_table_row(self.info.link)
- if row is None:
- raise ValueError('table row not found')
- try:
- scores = [
- float(text) for text
- in row.select('td.bdc')[-1].contents
- if isinstance(text, unicode)]
- except ValueError:
- # single-segment match
- try:
- # running single-segment
- scores = [
- float(text.strip()) for text
- in row.select('td.bdcg a')[-1].contents
- if isinstance(text, unicode)]
- except IndexError:
- 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
- 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
- if isinstance(text, unicode)][0].strip(u'\xa0')
- for link in row.select('a[onmouseover]')]
- for i in range(0, 2):
- teams[i].name = [team_names[i]]
- teams[i].known_teams = 1
- 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):
- for i in range(0, 2):
- match_teams = []
- possible_teams = []
- if isinstance(self.config['teams'][i], basestring):
- match_teams = [self.config['teams'][i]]
- elif isinstance(self.config['teams'][i], list):
- match_teams = self.config['teams'][i]
- else:
- if 'winner' in self.config['teams'][i]:
- match_teams += [
- MatchInfo.matches[winner_match].winner
- for winner_match in self.config['teams'][i]['winner']]
- possible_teams += [
- MatchInfo.matches[winner_match].possible_winner
- for winner_match in self.config['teams'][i]['winner']]
- if 'loser' in self.config['teams'][i]:
- match_teams += [
- MatchInfo.matches[loser_match].loser
- for loser_match in self.config['teams'][i]['loser']]
- possible_teams += [
- MatchInfo.matches[loser_match].possible_loser
- for loser_match in self.config['teams'][i]['loser']]
- if 'place' in self.config['teams'][i]:
- match_teams += [
- self.teams[place-1][0]
- for place in self.config['teams'][i]['place']]
- teams[i].name = match_teams
- teams[i].possible_name = possible_teams
- teams[i].known_teams = len([team for team in match_teams if team is not None])
- teams[i].selected_team = self.config['selected_teams'][i] if 'selected_teams' in self.config else -1
- PlayoffLogger.get('matchinfo').info(
- 'config scores for match #%d: %s',
- self.info.id, teams)
- return teams
-
- def __resolve_team_aliases(self, teams):
- return [self.aliases[team] if team in self.aliases else team for team in 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)
- self.info.running = int(self.config.get('running', -1))
- if not teams_fetched:
- try:
- try:
- if self.database is None:
- raise KeyError('database not configured')
- if 'database' not in self.config:
- raise KeyError('database not configured')
- self.info.teams = self.__get_db_teams(
- self.info.teams, not scores_fetched)
- 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__, 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__, str(e))
- self.info.teams = self.__get_config_teams(self.info.teams)
- for team in range(0, len(self.info.teams)):
- if isinstance(self.config['teams'][team], dict):
- self.info.teams[team].place = self.config['teams'][team].get(
- 'place', self.info.teams[team].place)
- self.info.teams[team].name = self.__resolve_team_aliases(self.info.teams[team].name)
- PlayoffLogger.get('matchinfo').info('team list after resolving aliases: %s', self.info.teams[team].name)
- self.info.teams[team].possible_name = self.__resolve_team_aliases(self.info.teams[team].possible_name)
- PlayoffLogger.get('matchinfo').info('predicted team list after resolving aliases: %s', self.info.teams[team].possible_name)
-
-
- def __get_db_board_count(self):
- towels = self.database.fetch(
- self.config['database'], p_sql.TOWEL_COUNT,
- (self.config['table'], self.config['round']))
- row = [0 if r is None
- else r for r in
- self.database.fetch(
- self.config['database'], p_sql.BOARD_COUNT,
- (self.config['table'], self.config['round']))]
- boards_to_play = int(row[0])
- 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):
- links = [link for link in cell.select('a[href]')
- if re.match(r'^.*\d+t\d+-\d+\.htm$', link['href'])]
- return len(links) > 0
-
- def __has_towel_image(self, cell):
- return len(cell.select('img[alt="towel"]')) > 0
-
- 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:
- segment_url = re.sub(
- r'\.htm$', '.html',
- urljoin(self.info.link, segment_link[0]['href']))
- try:
- 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)
- return board_count, played_boards >= board_count
- except IOError as e:
- PlayoffLogger.get('matchinfo').info(
- 'cannot fetch HTML played boards count for segment: %s(%s)',
- self.info.id, type(e).__name__, str(e))
- return 0, False
- return 0, False
-
- def __get_html_board_count(self):
- if self.info.link is None:
- raise ValueError('link not set')
- row = self.__find_table_row(self.info.link)
- if row is None:
- raise ValueError('table row not found')
- 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:
- # 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')
- running_segments = row.select('td.bdca')
- running_boards = sum([self.__get_html_running_boards(segment) for segment in running_segments])
- finished_segments = []
- boards_in_segment = None
- for segment in segments:
- if segment not in running_segments:
- boards, is_finished = self.__get_finished_info(segment)
- if is_finished:
- finished_segments.append(segment)
- if boards_in_segment is None and boards > 0:
- boards_in_segment = boards
- 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',
- self.info.id, played_boards, total_boards)
- return played_boards, total_boards
-
- def __fetch_board_count(self):
- boards_played = 0
- boards_to_play = 0
- try:
- 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) as e:
- PlayoffLogger.get('matchinfo').warning(
- 'fetching board count from DB for match #%d failed: %s(%s)',
- 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__, str(e))
- pass
- if boards_played > 0:
- self.info.running = -1 \
- if boards_played >= boards_to_play \
- else boards_played
-
- def __determine_outcome(self):
- if (self.info.teams[0].known_teams == 1) \
- and (self.info.teams[1].known_teams == 1):
- if self.info.running == -1:
- if self.info.teams[0].score > self.info.teams[1].score:
- self.info.winner = self.info.teams[0].name[0]
- self.info.loser = self.info.teams[1].name[0]
- else:
- self.info.loser = self.info.teams[0].name[0]
- self.info.winner = self.info.teams[1].name[0]
- elif self.info.running > 0:
- if self.info.teams[0].score > self.info.teams[1].score:
- self.info.possible_winner = self.info.teams[0].name[0]
- self.info.possible_loser = self.info.teams[1].name[0]
- elif self.info.teams[0].score < self.info.teams[1].score:
- self.info.possible_loser = self.info.teams[0].name[0]
- self.info.possible_winner = self.info.teams[1].name[0]
-
- def __get_db_running_link(self, prefix, round_no):
- 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)
-
- def __get_html_running_link(self):
- if self.info.link is None:
- raise ValueError('link not set')
- row = self.__find_table_row(self.info.link)
- 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):
- 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:
- if self.database is None:
- 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) as e:
- PlayoffLogger.get('matchinfo').warning(
- 'cannot determine running link from DB for match #%d: %s(%s)',
- 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__, 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:
- self.info.link = phase_link
- 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()
- self.__fetch_board_count()
- self.__determine_outcome()
- if self.info.running > 0:
- self.__determine_running_link()
- return self.info