Exemple #1
0
    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,
            ],
        )
Exemple #2
0
    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
Exemple #5
0
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()}
Exemple #6
0
 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)
Exemple #7
0
            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
Exemple #8
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
Exemple #9
0
    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)
Exemple #10
0
        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)
Exemple #11
0
            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)
Exemple #12
0
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
Exemple #13
0
    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)
Exemple #14
0
        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)
Exemple #15
0
    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, )
Exemple #17
0
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
Exemple #18
0
    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)
Exemple #19
0
    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)
Exemple #20
0
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
Exemple #21
0
    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
Exemple #23
0
 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
Exemple #24
0
    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
Exemple #25
0
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
Exemple #26
0
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)
Exemple #27
0
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()}
Exemple #28
0
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()}
Exemple #29
0
    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)
Exemple #30
0
 def create_topic(self, topic_name: str):
     # PubSubPublisher(self.project_id).create_topic(topic_name)
     Logger.info(f"Created virtual topic '{topic_name}'")