from datetime import datetime, timedelta import math import json import sys from lib.tools import real_round, url_sanitize, unity, n2v from lib.team import Team if 'lib.league' not in sys.modules: from lib.league import League, Sport if 'lib.user' not in sys.modules: from lib.user import User class Scheduler: def __init__(self, url, recursive_id=0): self.url = url self.recursive_id = recursive_id self.matches = list() self.previous_url = None self.next_url = None class Event: def __init__(self, type_): self.type = type_ self.side = None self.minute = None self.player = None class Squad: def __init__(self, role=None, side=None, name=None): self.role = role self.side = side self.name = name self.lastname = None self.events = list() class Stat: def __init__(self, home=0, away=0): self.home = home self.away = away class Comm: def __init__(self, minute=None, type_=None, text=None): self.type = type_ self.text = text self.minute = minute class Match: COMING = 0 FIRST_TIME = 1 HALF_TIME = 2 SECOND_TIME = 3 OVER = 4 POSTPONED = 5 ET_FIRST_TIME = 6 ET_HALF_TIME = 7 ET_SECOND_TIME = 8 SHOOTOUT = 9 WAITING_SCORERS = 10 CANCELLED = 11 def __init__(self, idt): self.id = idt self.idof10 = 0 self.idof4 = 0 self.id_month = 0 self.mday = 0 self.round = None self.leg = 0 self.status = 0 self.start_date = None self.new_start_date = None self.end_date = None self.minute = '' self.extra_time = '' self.error = None self.task_done = False self.tv_channels = list() self.json_parser = False self.score_home = 0 self.score_away = 0 self.score_sets = dict() self.shootout_home = 0 self.shootout_away = 0 self.winner = None self.url = None self.url_score = None self.url_comms = None self.new_url = None self.coeffs = dict() self.events = list() self.squad = list() self.stats = dict() self.comms = list() self.last_event = None self.last_event_date = None self.home = None self.away = None self.league = None self.sport = None def set_winner(self): self.winner = None if self.sport.id == 2: if self.score_sets['home'] and self.score_sets['away']: if self.score_sets['home'][-1] == 'A': self.winner = 'away' elif self.score_sets['away'][-1] == 'A': self.winner = 'home' elif int(self.score_sets['home'][-1]) > int(self.score_sets['away'][-1]): self.winner = 'home' elif int(self.score_sets['home'][-1]) < int(self.score_sets['away'][-1]): self.winner = 'away' elif int(self.score_sets['home'][-1]) == int(self.score_sets['away'][-1]): self.winner = 'tie' elif self.score_sets['home'] and self.score_sets['home'][-1] == 'A': self.winner = 'away' elif self.score_sets['away'] and self.score_sets['away'][-1] == 'A': self.winner = 'home' else: if self.score_home > self.score_away: self.winner = 'home' elif self.score_home < self.score_away: self.winner = 'away' elif self.shootout_home > self.shootout_away: self.winner = 'home' elif self.shootout_home < self.shootout_away: self.winner = 'away' else: self.winner = 'tie' def get_new_status(self): # tennis case if self.sport.id == 2: if not self.minute: self.status = 0 self.minute = '' elif 'terminé' in self.minute.lower() or 'ab.' in self.minute.lower(): self.status = 4 self.minute = 'FINI' elif 'commencé' in self.minute.lower(): self.status = 1 self.minute = 'LIVE' else: self.minute = '' # match has not started elif 'envoi' in self.minute or not self.minute: self.status = self.COMING self.minute = '' # match in half time elif 'mi-temps' in self.minute: self.status = self.HALF_TIME self.minute = 'MT' # match is finished elif 'terminé' in self.minute: nb_scorers_home = 0 nb_scorers_away = 0 for event in self.events: if event.type == 'goal': if event.side == 'home': nb_scorers_home += 1 else: nb_scorers_away += 1 if self.end_date is None or datetime.now() - self.end_date < timedelta(minutes=30): self.status = self.WAITING_SCORERS self.end_date = datetime.now() else: self.status = self.OVER self.minute = 'FINI' # match is in extra time elif 'prolongation' in self.minute: self.status = self.ET_FIRST_TIME self.extra_time = 'extratime' self.minute = 'PROL' # match is in shootout elif 'tirs au but' in self.minute: self.status = self.SHOOTOUT self.extra_time = 'shootout' self.minute = 'TAB' # match is postponed elif any([word in self.minute for word in ('reporté', 'suspendu')]): self.status = self.POSTPONED self.minute = 'REP' # match is cancelled elif 'annulé' in self.minute: self.status = self.CANCELLED self.minute = 'ANN' # match is in progress else: if self.status == self.COMING: self.status = self.FIRST_TIME elif self.status == self.HALF_TIME or datetime.now() - self.start_date > timedelta(hours=1): self.status = self.SECOND_TIME minute = self.minute.rstrip("'") if minute.isnumeric(): if self.status == self.FIRST_TIME and int(minute) > 45: self.minute = "45'+{}".format(int(minute) - 45) elif self.status == self.SECOND_TIME and int(minute) > 90: self.minute = "90'+{}".format(int(minute) - 90) else: self.minute = '' def get_users_league_bets(self, db): stmt = """ SELECT id_month, id_league, mday FROM bets WHERE league_id = :id_league AND league_mday = :mday AND open = 1 """ args = {'id_league': self.league.id, 'mday': self.mday, 'league_mday': self.mday} for res in db.query(stmt, args): self.id_month = res['id_month'] stmt = """ SELECT id, id_team, league_bets_{} FROM users_month WHERE id_league = :id_league AND id_month = :id_month """.format(res['mday']) args = {'id_league': res['id_league'], 'id_month': res['id_month']} for row in db.query(stmt, args): yield row['id'], row['id_team'], res['mday'], row['league_bets_{}'.format(res['mday'])] def get_users_europe_bets(self, db): stmt = """ SELECT id_month, europe_round, europe_matches FROM bets WHERE europe_matches REGEXP :id_match AND open = 1 """ args = {'id_match': '(^{id},|,{id},|,{id}$)'.format(id=self.id)} for res in db.query(stmt, args): id_ = 0 for id_match in res['europe_matches'].split(','): if int(id_match) == self.id: self.idof4 = id_ break id_ += 1 self.id_month = res['id_month'] europe_round = res['europe_round'] stmt = """ SELECT id, europe_scores_{}, europe_scorers_{} FROM users_month WHERE id_month = :id_month AND europe_round = :round """.format(europe_round, europe_round) args = {'id_month': res['id_month'], 'round': europe_round} for row in db.query(stmt, args): bet_scores = json.loads(row['europe_scores_{}'.format(europe_round)]) bet_scorers = json.loads(row['europe_scorers_{}'.format(europe_round)]) yield row['id'], res['europe_round'], bet_scores, bet_scorers def update_coeffs(self, db): numbers = {'total': 0, 'home': 0, 'tie': 0, 'away': 0} for id_, id_team, bet_mday, bets in self.get_users_league_bets(db): bet = bets[self.idof10] numbers['total'] += 1 if bet == '1': numbers['home'] += 1 elif bet == 'X': numbers['tie'] += 1 elif bet == '2': numbers['away'] += 1 else: numbers['total'] -= 1 if numbers['total'] > 0: self.coeffs['home'] = real_round(1 + numbers['total'] * math.sin(math.pi / (4 * (1 + numbers['home']))), 1) self.coeffs['tie'] = real_round(1 + numbers['total'] * math.sin(math.pi / (4 * (1 + numbers['tie']))), 1) self.coeffs['away'] = real_round(1 + numbers['total'] * math.sin(math.pi / (4 * (1 + numbers['away']))), 1) self.store_coeffs(db) def update_users_league(self, db): # check if bets are active if not self.league.bets or self.coeffs['home'] == 0: return None for id_, id_team, bet_mday, bets in self.get_users_league_bets(db): user = User(idt=id_) bet = bets[self.idof10] own_team = 0 points = 0 if self.home.id == id_team or self.away.id == id_team: own_team = 1 if bet == '1' and self.score_home > self.score_away: points = (1 + own_team) * self.coeffs['home'] elif bet == 'X' and self.score_home == self.score_away: points = (1 + own_team) * self.coeffs['tie'] elif bet == '2' and self.score_home < self.score_away: points = (1 + own_team) * self.coeffs['away'] if points > 0: user.set_league_points(bet_mday, points, db) self.update_users_league_ranking(db) def update_users_league_ranking(self, db): stmt = """ SELECT COUNT(*) FROM matches WHERE id_league = :id_league AND mday = :mday AND status = :over """ args = {'id_league': self.league.id, 'mday': self.mday, 'over': self.OVER} for res in db.query(stmt, args): stmt = """ SELECT users_month.id, users_month.league_rank, users_month.id_league FROM users_month INNER JOIN bets ON bets.id_league = users_month.id_league AND bets.id_month = users_month.id_month WHERE bets.league_id = :id_league AND bets.league_mday = :mday AND bets.open = 1 AND users_month.id_month = :id_month ORDER BY users_month.league_points_total DESC """ args = {'id_league': self.league.id, 'mday': self.mday, 'id_month': self.id_month} ranking = dict() for row in db.query(stmt, args): if row['id_league'] in ranking: ranking[row['id_league']] += 1 else: ranking[row['id_league']] = 1 new_rank = ranking[row['id_league']] if res['COUNT(*)'] == 0: db.exec( 'UPDATE users_month SET league_rank = :rank, league_rank_old = :rank_old WHERE id = :id', {'rank': new_rank, 'rank_old': row['league_rank'], 'id': row['id']} ) elif new_rank != row['league_rank']: db.exec( 'UPDATE users_month SET league_rank = :rank WHERE id = :id', {'rank': new_rank, 'id': row['id']} ) def update_users_europe(self, db): match_scorers = {'home': list(), 'away': list()} for side in ('home', 'away'): for event in self.events: if event.type == 'goal' and event.side == side: exp_scorer = event.player.split(' ') last_word = url_sanitize(exp_scorer[-1]) if last_word == '-csc-': last_word = 'csc' elif last_word == '-p-': last_word = url_sanitize(exp_scorer[-2]) match_scorers[side].append(last_word) for id_, europe_round, bet_scores, bet_scorers in self.get_users_europe_bets(db): user = User(idt=id_) if bet_scores[self.idof4]['home'] is None or bet_scores[self.idof4]['away'] is None: continue points = 0 if bet_scores[self.idof4]['home'] == self.score_home and bet_scores[self.idof4]['away'] == self.score_away: points += 5 elif unity(bet_scores[self.idof4]['home'] - bet_scores[self.idof4]['away']) == \ unity(self.score_home - self.score_away): points += 2 if bet_scores[self.idof4]['home'] + bet_scores[self.idof4]['away'] > 0: scorer_points = 0 tmp_scorers = {'home': match_scorers['home'][:], 'away': match_scorers['away'][:]} for side in ('home', 'away'): bet_scorers_side = bet_scorers[self.idof4][side] if isinstance(bet_scorers_side, dict): bet_scorers_side = list(bet_scorers_side.values()) for bet_scorer in bet_scorers_side[:bet_scores[self.idof4][side]]: for tmp_scorer in tmp_scorers[side]: if url_sanitize(tmp_scorer) in url_sanitize(bet_scorer): scorer_points += 1 tmp_scorers[side].remove(tmp_scorer) break points += real_round(scorer_points/(bet_scores[self.idof4]['home'] + bet_scores[self.idof4]['away']), 1) if points > 0: user.set_europe_points(europe_round, points, db) def update_teams_ranking(self, db): # if no mday then no ranking to update if self.mday == 0: return None # set home and away stats self.home.played = 1 self.away.played = 1 if self.score_home > self.score_away: self.home.points = self.league.points['winner'] self.away.points = self.league.points['loser'] self.home.wins = 1 self.away.loss = 1 elif self.score_away > self.score_home: self.home.points = self.league.points['loser'] self.away.points = self.league.points['winner'] self.home.loss = 1 self.away.wins = 1 else: self.home.points = self.league.points['tie'] self.away.points = self.league.points['tie'] self.home.ties = 1 self.away.ties = 1 self.home.g_for = self.score_home self.home.g_against = self.score_away self.home.g_diff = self.score_home - self.score_away self.away.g_for = self.score_away self.away.g_against = self.score_home self.away.g_diff = self.score_away - self.score_home # if match is postponed then reset live stats if self.status == self.POSTPONED: self.home.reset_live_stats(db) self.away.reset_live_stats(db) # else update stats else: # in all cases update live stats self.home.update_live_stats(db) self.away.update_live_stats(db) self.league.update_live_ranking(self.home.group, db) if self.away.group != self.home.group: self.league.update_live_ranking(self.away.group, db) # if match is finished then update final stats if self.status in (self.OVER, self.WAITING_SCORERS): self.home.update_final_stats(db) self.away.update_final_stats(db) self.league.update_final_ranking(self.home.group, db) if self.away.group != self.home.group: self.league.update_final_ranking(self.away.group, db) def store(self, db): stmt = """ INSERT IGNORE INTO matches (idof10, id_league, mday, round, leg, id_home, id_away, start_date, url, url_score, url_comms, comms) VALUES (:idof10, :id_league, :mday, :round, :leg, :id_home, :id_away, :start_date, :url, :url_score, :url_comms, '[]') """ args = { 'idof10': self.idof10, 'id_league': self.league.id, 'mday': self.mday, 'round': n2v(self.round), 'leg': self.leg, 'id_home': self.home.id, 'id_away': self.away.id, 'start_date': self.start_date.strftime('%Y-%m-%d %H:%M:%S'), 'url': self.url, 'url_score': self.url_score, 'url_comms': self.url_comms } self.id = db.exec(stmt, args) def store_score(self, db): stmt = """ UPDATE matches SET start_date = :start_date, score_home = :score_home, score_away = :score_away, minute = :minute, extra_time = :extra_time, shootout_home = :shootout_home, shootout_away = :shootout_away, events = :events, squad = :squad, status = :status, error = NULL, score_sets = :score_sets, winner = :winner, stats = :stats, comms = :comms {last_event} WHERE id = :id """ args = { 'start_date': self.start_date.strftime('%Y-%m-%d %H:%M:%S'), 'score_home': self.score_home, 'score_away': self.score_away, 'minute': self.minute, 'extra_time': self.extra_time, 'shootout_home': self.shootout_home, 'shootout_away': self.shootout_away, 'events': json.dumps([event.__dict__ for event in self.events], separators=(',', ':')), 'squad': json.dumps([squad.__dict__ for squad in self.squad], separators=(',', ':')), 'status': self.status, 'id': self.id, 'score_sets': json.dumps(self.score_sets, separators=(',', ':')), 'winner': n2v(self.winner), 'stats': json.dumps({key: stat.__dict__ for key, stat in self.stats.items()}, separators=(',', ':')), 'comms': json.dumps([comm.__dict__ for comm in self.comms], separators=(',', ':')) } if self.last_event is not None: stmt = stmt.format(last_event=', last_event = :last_event, last_event_date = NOW()') args['last_event'] = json.dumps(self.last_event.__dict__, separators=(',', ':')) else: stmt = stmt.format(last_event='') db.exec(stmt, args) def store_comms(self, db): db.exec( 'UPDATE matches SET comms = :comms WHERE id = :id', {'comms': json.dumps([comm.__dict__ for comm in self.comms], separators=(',', ':')), 'id': self.id} ) def store_url(self, db): db.exec( 'UPDATE matches SET url = :url, status = :status WHERE id = :id', {'url': self.url, 'status': self.COMING, 'id': self.id} ) def store_start_date(self, db): db.exec( 'UPDATE matches SET start_date = :start_date, status = :status WHERE id = :id', {'start_date': self.start_date.strftime('%Y-%m-%d %H:%M:%S'), 'status': self.COMING, 'id': self.id} ) def store_end_date(self, db): db.exec( 'UPDATE matches SET end_date = :end_date, status = :status WHERE id = :id', {'end_date': self.end_date.strftime('%Y-%m-%d %H:%M:%S'), 'status': self.WAITING_SCORERS, 'id': self.id} ) def store_coeffs(self, db): db.exec( 'UPDATE matches SET coeffs = :coeffs WHERE id = :id', {'coeffs': json.dumps(self.coeffs, separators=(',', ':')), 'id': self.id} ) def store_error(self, db): db.exec( 'UPDATE matches SET error = :error WHERE id = :id', {'error': self.error, 'id': self.id} ) def store_minute(self, db): db.exec( 'UPDATE matches SET status = :status, minute = :minute WHERE id = :id', {'status': self.status, 'minute': self.minute, 'id': self.id} ) def store_tvchannels(self, db): if self.tv_channels: db.exec( 'UPDATE matches SET tv_channels = :tv_channels WHERE id = :id', {'tv_channels': ','.join([str(tv_channel.id) for tv_channel in self.tv_channels]), 'id': self.id} ) else: db.exec( 'UPDATE matches SET tv_channels = NULL WHERE id = :id', {'id': self.id} ) @classmethod def get_matches(cls, db, origin=None, interval_hour=2, interval_day=14, id_match=None, id_league=None): where_conditions = list() if origin == 'update_scores': where_conditions.append('matches.status NOT IN ({}, {})'.format(cls.OVER, cls.CANCELLED)) where_conditions.append('matches.start_date <= NOW() + INTERVAL {} HOUR'.format(interval_hour)) elif origin == 'update_schedule': where_conditions.append('matches.status IN ({}, {})'.format(cls.COMING, cls.POSTPONED)) where_conditions.append('matches.start_date <= NOW() + INTERVAL {} DAY'.format(interval_day)) where_conditions.append('leagues.auto_schedule = 0') elif origin == 'update_tvschedule': where_conditions.append('matches.status = {}'.format(cls.COMING)) where_conditions.append('matches.start_date <= NOW() + INTERVAL {} DAY'.format(interval_day)) if id_match is not None: where_conditions.append('matches.id = {}'.format(int(id_match))) if id_league is not None: where_conditions.append('matches.id_league = {}'.format(int(id_league))) where_clause = '' if len(where_conditions) > 0: where_clause = 'WHERE {}'.format(' AND '.join(where_conditions)) stmt = """ SELECT matches.id, matches.idof10, matches.id_league, matches.id_home, matches.id_away, matches.mday, matches.url, matches.status, matches.events, matches.squad, matches.coeffs, matches.start_date, matches.end_date, matches.url_score, matches.url_comms, home.name AS home_name, home.names AS home_names, away.name AS away_name, away.names AS away_names, leagues.name AS league_name, leagues.id_sport, leagues.bets, leagues.points, leagues.url_schedule AS league_url, home_lt.group AS home_group, away_lt.group AS away_group, sports.name AS sport_name, sports.display_sets FROM matches INNER JOIN teams AS home ON home.id = matches.id_home INNER JOIN teams AS away ON away.id = matches.id_away INNER JOIN leagues ON leagues.id = matches.id_league INNER JOIN league_teams AS home_lt ON home_lt.id_league = leagues.id AND home_lt.id_team = home.id INNER JOIN league_teams AS away_lt ON away_lt.id_league = leagues.id AND away_lt.id_team = away.id INNER JOIN sports ON sports.id = leagues.id_sport {} """.format(where_clause) for row in db.query(stmt): match = Match(idt=row['id']) match.idof10 = row['idof10'] match.start_date = row['start_date'] match.end_date = row['end_date'] match.mday = row['mday'] match.status = row['status'] match.url = row['url'] match.url_score = row['url_score'] match.url_comms = row['url_comms'] match.coeffs = json.loads(row['coeffs']) match.events = json.loads(row['events']) match.squad = json.loads(row['squad']) match.league = League(idt=row['id_league']) match.league.name = row['league_name'] match.league.bets = bool(row['bets']) match.league.url = row['league_url'] match.league.points = json.loads(row['points']) match.home = Team(idt=row['id_home']) match.home.name = row['home_name'] match.home.names = json.loads(row['home_names']) match.home.group = row['home_group'] match.home.league = match.league match.away = Team(idt=row['id_away']) match.away.name = row['away_name'] match.away.names = json.loads(row['away_names']) match.away.group = row['away_group'] match.away.league = match.league match.sport = Sport(idt=row['id_sport']) match.sport.name = row['sport_name'] match.sport.display_sets = bool(row['display_sets']) yield match