def on_execute(self, command: CreateLeagueSubscriptionsCommand) -> CreateLeagueSubscriptionsResult: league_id = command.league.id if command.league else command.league_id if not league_id: Logger.warn("CreateLeagueSubscriptions - No league ID") return CreateLeagueSubscriptionsResult(command=command, error="Missing league id") api_key = self.settings.api_key league_command_sub_id = f"league_{league_id}-commands" league_command_endpoint = f"{self.settings.endpoint}/system/league_command?league_id={league_id}&key={api_key}" league_command_subscription = self.publisher.create_push_subscription(league_command_sub_id, LEAGUE_COMMAND_TOPIC, league_command_endpoint) league_command_subscription = league_command_subscription.name transaction = self.league_repo.firestore.create_transaction() @firestore.transactional def update_in_transaction(transaction): league = self.league_repo.get(league_id, transaction) league.league_command_subscription = True self.league_repo.update(league, transaction) update_in_transaction(transaction) return CreateLeagueSubscriptionsResult( command=command, subscriptions=[ league_command_subscription, ], )
def update_status_from_game_rosters(self, rostered_players: Dict[str, Player]): current_week = self.state_repo.get().current_week games = self.game_repo.for_week(self.season, current_week) teams_with_rosters: List[int] = [] player_ids_on_game_roster: List[str] = [] for game in games: if game.away_roster: teams_with_rosters.append(game.teams.away.id) ids = [p.id for p in game.away_roster.values()] player_ids_on_game_roster.extend(ids) if game.home_roster: teams_with_rosters.append(game.teams.home.id) ids = [p.id for p in game.home_roster.values()] player_ids_on_game_roster.extend(ids) for rostered_player in rostered_players.values(): scratched = rostered_player.team.id in teams_with_rosters and rostered_player.id not in player_ids_on_game_roster if rostered_player.status_current == STATUS_ACTIVE and scratched: rostered_player.status_current = STATUS_INACTIVE rostered_player.hash = hash_dict(rostered_player.dict()) Logger.info(f"Scratched {rostered_player.display_name}")
def apply_fix(transaction): Logger.info(f"Processing league {league.id}") rosters = self.league_roster_repo.get_all( league.id, transaction) schedule = self.league_config_repo.get_schedule_config( league.id, transaction) previews: Dict[str, UserLeaguePreview] = {} for roster in rosters: preview = self.user_league_repo.get( roster.id, league.id, transaction) previews[roster.id] = (preview) for roster in rosters: week: ScheduleWeek = next( week for week in schedule.weeks if week.week_number == current_week) matchup = next((m for m in week.matchups if (m.away and m.away.id == roster.id) or ( m.home and m.home.id == roster.id)), None) preview = previews[roster.id] if not preview: return False preview.matchup = MatchupPreview.from_matchup( matchup) if matchup else None self.user_league_repo.set(roster.id, preview, transaction) return True
def apply_fix(transaction): Logger.info(f"Processing league {league.id}") schedule = self.league_config_repo.get_schedule_config( league.id, transaction) save_required = False for week in schedule.weeks: for matchup in week.matchups: if matchup.away and matchup.away.waiver_bids: matchup.away.waiver_bids = [] save_required = True if matchup.away and matchup.away.processed_waiver_bids: matchup.away.processed_waiver_bids = [] save_required = True if matchup.home and matchup.home.waiver_bids: matchup.home.waiver_bids = [] save_required = True if matchup.home and matchup.home.processed_waiver_bids: matchup.home.processed_waiver_bids = [] save_required = True if save_required: self.league_config_repo.set_schedule_config( league.id, schedule, transaction) return save_required
async def error(): Logger.error("This is a test error log entry") try: raise NotImplementedError() except Exception as ex: Logger.error("This is a test error log entry with extra info", ex) return {"logger": Logger.logger_type()}
def create_push_subscription(self, subscription_name: str, topic_name: str, endpoint: str, config: SubscriptionConfig = None): # return PubSubPublisher(self.project_id).create_push_subscription(subscription_name, topic_name, endpoint, config) Logger.info( f"Created virtual push subscription '{subscription_name}' on topic '{topic_name}' to {endpoint}" ) return Subscription(name=subscription_name)
def apply_fix(transaction): Logger.info(f"Processing league {league.id}") config = self.league_config_repo.get_scoring_config(league.id, transaction) if config.field_goal_returns_yards != config.kick_returns_yards: config.field_goal_returns_yards = config.kick_returns_yards self.league_config_repo.set_scoring_config(league.id, config, transaction) return 1 else: return 0
def __check_api_key(request: Request) -> dict: key = request.query_params.get("key", None) try: config = get_settings() return key == config.api_key except Exception as ex: Logger.error("Error occurred while checking api key for request", exc_info=ex) return None
def get_data(self) -> Dict: Logger.debug("Getting data from PubSubPush", extra=self.dict()) data = self.message.data if not data: abort_bad_request() if isinstance(data, dict): return data else: data = base64.b64decode(data) return json.loads(data)
def end_of_day(transaction: Transaction) -> EndOfDayResult: scoreboard = self.public_repo.get_scoreboard(transaction) state = self.state_repo.get(transaction) if not state.waivers_active: Logger.info("Waivers not active") week_complete = scoreboard.all_games_complete() if not week_complete: Logger.info("Week not yet complete") return EndOfDayResult(command=command) now = datetime.now(tz=pytz.UTC) hours_since_last_game = hours_since( scoreboard.last_game_start_time(), now) if hours_since_last_game < self.settings.min_stat_correction_hours: Logger.info( "Week complete but not enough time since last game", extra={ "hours_since": hours_since_last_game, "min_hours": self.settings.min_stat_correction_hours }) return EndOfDayResult(command=command) Logger.info( "Week is complete, enabling waivers and publishing end of week" ) state.waivers_active = True state.waivers_end = datetime.now().today() + timedelta(days=1) state.locks = Locks.reset() completed_week_number = scoreboard.week() state.current_week = completed_week_number + 1 self.state_repo.set(state, transaction) return EndOfDayResult( command=command, state=state, waivers_enabled=True, completed_week_number=completed_week_number) else: Logger.info( "Waivers are active, initializing waiver processing") state.waivers_active = False state.waivers_end = None self.state_repo.set(state, transaction) return EndOfDayResult(command=command, state=state, waivers_complete=True)
def apply_fix(transaction): Logger.info(f"Processing league {league.id}") rosters = self.league_roster_repo.get_all( league.id, transaction) for roster in rosters: roster.this_week_points_for = 0 roster.this_week_bench_points_for = 0 for position in roster.positions.values(): position.game_score = 0 self.league_roster_repo.set(league.id, roster, transaction)
def __get_token(request: Request): if "Authorization" not in request.headers: return None header = request.headers["Authorization"] try: parts = header.split(" ") if len(parts) < 2: Logger.warn("Invalid bearer token (less than 2 parts)") return None token = parts[1] # Logger.info(token) token = auth.verify_id_token(token, check_revoked=True) uid = token["uid"] request.state.uid = uid context.data["user_id"] = uid return token except auth.ExpiredIdTokenError: Logger.warn("Expired token encountered") except auth.InvalidIdTokenError: Logger.warn("Invalid token encountered") except BaseException as ex: Logger.error("Exception occurred while decoding token", exc_info=ex) return None
def on_execute(self, command: EndOfWeekCommand) -> EndOfWeekResult: # @firestore.transactional # def end_of_week(transaction: Transaction) -> EndOfWeekResult: season = self.settings.current_season # current_week = self.public_repo.get_state().current_week # completed_week = current_week - 1 # completed_games = self.game_repo.for_week(season, completed_week) # completed_game_ids = [g.id for g in completed_games] if self.public_repo.get_switches().enable_score_testing: season = 2019 Logger.warn("SCORE TESTING SWITCH IS ENABLED") players = self.player_repo.get_all(season) # players = [self.player_repo.get(season, "111244")] updated_players = [] for player in players: if not player.game_stats: continue # Not ideal - might need to be revisited, but this missed the players on bye. # Maybe can be restored after being run once? # player_games_ids = list(player.game_stats.keys()) # player_games_ids.sort() # last_player_game_id = player_games_ids[-1] # if last_player_game_id not in completed_game_ids: # continue player.recalc_season_stats() updated_players.append(player) self.player_repo.set(season, player) # return EndOfWeekResult(command=command, updated_players=updated_players) # transaction = self.player_repo.firestore.create_transaction() # result: EndOfWeekResult = end_of_week(transaction) # if result.success: command = CalculateSeasonScoreCommand() payload = LeagueCommandPushData( command_type=LeagueCommandType.CALCULATE_SEASON_SCORE, command_data=command.dict()) self.publisher.publish(payload, LEAGUE_COMMAND_TOPIC) return EndOfWeekResult(command=command, updated_players=updated_players)
def update(transaction): owner = self.league_owned_player_repo.get(command.league_id, command.player.id, transaction) if not owner: # ignore and ack message, no one owns this player return UpdateLeaguePlayerDetailsResult(command=command) roster = self.league_roster_repo.get(command.league_id, owner.owner_id, transaction) positions: List[LeaguePosition] = [] for position_id in roster.positions: position = roster.positions[position_id] if position.player and position.player.id == command.player.id: positions.append(position) if len(positions) == 0: # something is messed up, the owned players list says someone owns this guy, # but we couldn't find him in that roster Logger.error( "League owned player list / league roster mismatch", extra={ "league_id": command.league_id, "player_id": command.player.id, "owner_id": owner.id, }) # but, I don't want this message to stick around forever. return UpdateLeaguePlayerDetailsResult(command=command) if len(positions) > 1: # how is this guy in two spots at once? Logger.error( "League owned player list / duplicate on league roster", extra={ "league_id": command.league_id, "player_id": command.player.id, "owner_id": owner.id, }) # should probably make this a really visible error - the players may let me know too though. # I'll still update this player since I'll use a loop for position in positions: position.player = command.player self.league_roster_repo.set(command.league_id, roster, transaction) return UpdateLeaguePlayerDetailsResult(command=command)
def get_roster(self, roster_id): Logger.info(f"[CFL API] Fetching roster id {roster_id}") url = f"{self.endpoint}{roster_id}" response = requests.get(url) data = response.json() try: if data["success"] == 1: return data["players"] else: raise ApiException(data["message"]) except Exception as ex: Logger.exception( f"[CFL API] Failed to fetch team rosters for {roster_id}", exc_info=ex) raise ApiException("Failed to retrieve rosters")
def on_execute( self, command: UpdatePlayerStatsForWeekCommand ) -> UpdatePlayerStatsForWeekResult: Logger.info(f"Updating player stats for week {command.week}") season = command.season if self.public_repo.get_switches().enable_score_testing: season = 2019 Logger.warn("SCORE TESTING SWITCH IS ENABLED") current_games = self.get_current_games(season, command.week) player_updates = get_players(current_games) transaction = self.game_repo.firestore.create_transaction() @firestore.transactional def update(transaction, players: List[GamePlayerStats]): pending_player_updates = [] for player_update in players: player = self.player_repo.get(season, player_update.player.id, transaction) game_id = player_update.game_id if not player: player = from_game_player(player_update.player, player_update.team) if not player.game_stats: player.game_stats = {} player.game_stats[game_id] = PlayerGameStats( team=player.team, **player_update.stats.dict()) pending_player_updates.append(player) for player in pending_player_updates: self.player_repo.set(season, player, transaction) return pending_player_updates update(transaction, player_updates) return UpdatePlayerStatsForWeekResult(command=command, )
def get_message_data(push: Dict) -> Dict: Logger.info(f"[PUB/SUB] {push}") if not push: raise InvalidPushException() message = push["message"] if not message: raise InvalidPushException() data = message["data"] if not data: raise InvalidPushException() try: payload = base64.b64decode(data) return json.loads(payload) except Exception: return data
def get(self, path: str) -> dict: url = f"{self.settings.cfl_api_endpoint}/{path}" url_no_key = url if "?" in url: url += f"&key={self.settings.cfl_api_key}" else: url += f"?key={self.settings.cfl_api_key}" Logger.info(f"[CFL API] Fetching {url_no_key}") response = requests.get(url) if response.ok: return response.json() else: Logger.error( f"[CFL API] Error fetching data from {url_no_key}: {response.text}" ) raise CflApiException(response.text)
def on_execute(self, command: RegisterEmailCommand) -> RegisterEmailResult: existing = self.user_repository.get_by_email(command.email) if existing: return RegisterEmailResult(command=command, error="A user with that email already exists") try: result = auth.create_user(display_name=command.display_name, email=command.email) # type: UserRecord except auth.EmailAlreadyExistsError: return RegisterEmailResult(command=command, error="A user with that email already exists") except Exception as ex: Logger.error("Create user call failed", exc_info=ex) return RegisterEmailResult(command=command, error="Unable to create user account") if self.is_dev: # In live, this is handled by a background cloud function triggered after the registration user = User(id=result.uid, display_name=command.display_name, email=command.email, login_type=LoginType.EMAIL, confirmed=True) self.user_repository.create(user) return RegisterEmailResult(command=command)
def run_check(name: str, target: Callable): try: Logger.info(f"Starting test '{name}'") target() Logger.info(f"Test '{name}' completed successfully") return True except BaseException as ex: Logger.error(f"Test '{name}' failed", exc_info=ex) return False
def from_cfl_roster(abbreviation: str): if abbreviation in ["DE", "DT"]: abbreviation = "DL" if abbreviation in ["OL", "LS", "G", "T"]: abbreviation = "ol" if abbreviation in ["P"]: abbreviation = "K" if abbreviation in ["FB"]: abbreviation = "RB" if abbreviation in ["SB", "TE"]: abbreviation = "WR" if abbreviation in ["S", "CB"]: abbreviation = "DB" try: return PositionType(abbreviation.lower()) except Exception: Logger.warn(f"Encountered unknown position '{abbreviation}'") return PositionType.other
def execute(self, command: TCommand) -> TResult: name = type(self).__name__ extra = {"command": command.json()} Logger.info(f"{name} executing", extra=extra) try: result = self.on_execute(command) except Exception as ex: Logger.error(f"Error executing {name}", exc_info=ex, extra=extra) raise ex Logger.info(f"{name} completed") return result
def create_topic(self, topic_name: str): topic_path = self.get_topic_path(topic_name) try: try: self.publisher.get_topic(topic=topic_path) Logger.info("Topic exists", extra={"topic_name": topic_name}) except NotFound: self.publisher.create_topic(name=topic_path) Logger.info("Topic created", extra={"topic_name": topic_name}) except BaseException as ex: Logger.error("Failed to create topic", extra={"topic_name": topic_name}, exc_info=ex) raise ex
def create_push_subscription( self, subscription_name: str, topic_name: str, endpoint: str, config: SubscriptionConfig = None) -> Subscription: subscriber: SubscriberWrapper = SubscriberClient() subcription_path = subscriber.subscription_path( self.project_id, subscription_name) topic_path = self.get_topic_path(topic_name) if not config: config = SubscriptionConfig() logging_extra = { "subscription_name": subscription_name, "topic_name": topic_name, "config": config.dict(), } with subscriber: try: try: subscription = subscriber.get_subscription( request={"subscription": subcription_path}) Logger.info("Push subscription exists", logging_extra) return subscription except NotFound: request = Subscription( name=subcription_path, topic=topic_path, push_config=PushConfig(push_endpoint=endpoint), ack_deadline_seconds=60, expiration_policy=ExpirationPolicy(ttl=Duration( seconds=config.expiration_days * 86400) if config.expiration_days else None), retry_policy=RetryPolicy(), message_retention_duration=Duration( seconds=config.retention_days * 86400)) subscription = subscriber.create_subscription( request=request) Logger.info("Push subscription created", logging_extra) return subscription except BaseException as ex: Logger.error("Failed to create push subscription", exc_info=ex, extra=logging_extra) raise ex
def smoke_test(state_repo: StateRepository = Depends( create_state_repository)) -> Tuple[List[str], List[str]]: checks = { "firebase:state": state_repo.get, } Logger.info("Smoke test started") passes: List[str] = [] failures: List[str] = [] for name in checks: check = checks[name] passed = run_check(name, check) if passed: passes.append(name) else: failures.append(name) if failures: Logger.error("Smoke test completed with errors") else: Logger.info("Smoke test completed successfully") return passes, failures
from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.param_functions import Depends from api.app.config.settings import Settings, get_settings from api.app.core.logging import Logger from api.app.middleware.config import app_middleware from api.app.routers import (admin_router, league_draft_router, league_router, logging_router, login_router, migration_router, projection_router, roster_router, score_router, system_router, user_router) app = FastAPI(middleware=app_middleware) settings = get_settings() Logger.initialize(settings) origins = settings.origins.split(";") app.add_middleware(CORSMiddleware, allow_headers="*", allow_origins=origins, allow_credentials=True, allow_methods=["*"]) app.include_router(system_router.router) app.include_router(league_router.router) app.include_router(league_draft_router.router) app.include_router(login_router.router) app.include_router(user_router.router) app.include_router(roster_router.router) app.include_router(logging_router.router)
async def warn(): Logger.warn("This is a test warn log entry") Logger.warn("This is a test warn log entry with extra info", extra={"foo": "bar"}) return {"logger": Logger.logger_type()}
async def debug(): Logger.debug("This is a test debugging log entry") Logger.debug("This is a test debugging log entry with extra info", extra={"foo": "bar"}) return {"logger": Logger.logger_type()}
def publish(self, payload: BaseModel, topic_name: str): topic_path = self.get_topic_path(topic_name) data = payload.json() Logger.info(f"Published virtual pub/sub message to '{topic_path}") Logger.info(data)
def create_topic(self, topic_name: str): # PubSubPublisher(self.project_id).create_topic(topic_name) Logger.info(f"Created virtual topic '{topic_name}'")