supercontest.core.picks

Logic to handle all user picks.

Module Attributes

MAX_PICKS

The most teams a user may pick in one week.

Functions

check_changes

The rules on the picks themselves: nothing started moves.

check_one_side_per_game

Never both sides of one game.

check_window

The rules that do not depend on which teams are picked: when, for which week, how many.

commit_picks

Wrapper to write picks to the database.

email_all_picks

Emails the week's all-picks image to the users who want it, once picks close.

email_unfinished_pickers

Helper function to query the db and find the people who have not submitted all 5 picks for the week, then email them a reminder.

picks_open

Whether picks may be placed for a given week, right now.

split_picks

Which existing picks stay, and which teams are new.

unfinished_pickers

Everyone who has not made all their picks this week.

supercontest.core.picks.MAX_PICKS = 5

The most teams a user may pick in one week.

supercontest.core.picks.check_changes(dropped: set[Pick], new_contestants: list[Contestant]) → None

The rules on the picks themselves: nothing started moves.

Parameters:
  • dropped – Existing picks being removed or swapped out.

  • new_contestants – The contestants being picked anew.

Raises:

InvalidPicksError – If any rule refuses.

supercontest.core.picks.check_one_side_per_game(teams: list[str], new_contestants: list[Contestant]) → None

Never both sides of one game. Applied to every write, rules waived or not.

The database refuses it too (UNIQUE (user_id, game_id) on picks); this is checked first so the answer names the game instead of being a 500.

Parameters:
  • teams – The teams they want, kept and new.

  • new_contestants – The contestants being picked anew.

Raises:

InvalidPicksError – If a new pick’s opponent is also picked.

supercontest.core.picks.check_window(season: int, week: int, teams: list[str], is_regseason: bool, picks_are_open: bool, current: tuple[int | None, int | None]) → None

The rules that do not depend on which teams are picked: when, for which week, how many.

Parameters:
  • season – The season picked for.

  • week – The week picked for.

  • teams – The picks from the client.

  • is_regseason – Whether a regular-season week is current.

  • picks_are_open – Whether the current week’s pick window is open.

  • current – The current season and week.

Raises:

InvalidPicksError – If any rule refuses.

async supercontest.core.picks.commit_picks(session: AsyncSession, user_id: int, season: int, week: int, teams: list[str], emails: list[str] | None = None, verify: bool = True, settings: Settings | None = None) → None

Wrapper to write picks to the database.

Reads what the rules need, applies them (check_window(), check_changes(), and check_one_side_per_game() even with verify off), and writes the difference.

Parameters:
  • session – The session.

  • user_id – The ID of the user submitting picks.

  • season – The season to pick for.

  • week – The week to pick for.

  • teams – The picks from the client.

  • emails – The email addresses to send confirmation to.

  • verify – Whether or not to check conditions like max5, which weekday, etc.

  • settings – The app’s settings. Needed only to send emails.

Raises:

InvalidPicksError – If verifications fail.

async supercontest.core.picks.email_all_picks(session: AsyncSession, settings: Settings, render: Callable[[int, int], Awaitable[bytes]], defer: Callable[[...], Any] | None = None) → bool

Emails the week’s all-picks image to the users who want it, once picks close.

This function handles the subscription check. The image is drawn in-process – there is no screenshot to fetch – by render, which the caller supplies because drawing reads the API’s cached table, a layer core does not import.

Refuses while the week’s picks are still open. The old screenshot could not capture a table the page was hiding; an in-process drawing can, so a schedule that fires early would otherwise mail everyone’s picks.

Parameters:
  • session – The session.

  • settings – The app’s settings.

  • render – Draws a week’s table in the free league: await render(season, week) returns PNG bytes.

  • defer – Send after the response through this (BackgroundTasks.add_task).

Returns:

Whether or not the email was sent.

async supercontest.core.picks.email_unfinished_pickers(session: AsyncSession, settings: Settings, defer: Callable[[...], Any] | None = None) → bool

Helper function to query the db and find the people who have not submitted all 5 picks for the week, then email them a reminder.

Parameters:
  • session – The session.

  • settings – The app’s settings.

  • defer – Send after the response through this (BackgroundTasks.add_task).

Returns:

Whether or not the email was sent.

async supercontest.core.picks.picks_open(session: AsyncSession, season: int, week: int) → bool

Whether picks may be placed for a given week, right now.

Only the current week is ever open, and only during its pick window.

Parameters:
  • session – The session.

  • season – Season year.

  • week – Week number.

Returns:

Whether the week is the current one and its window is open.

supercontest.core.picks.split_picks(old_picks: set[Pick], teams: list[str]) → tuple[set[Pick], set[str]]

Which existing picks stay, and which teams are new.

Parameters:
  • old_picks – The user’s picks for the week, as stored.

  • teams – The teams they want, with blanks removed.

Returns:

The picks to leave alone, and the team names to add.

supercontest.core.picks.unfinished_pickers(all_emails: list[str], picks: list[Pick], emails_by_id: dict[int, str]) → list[str]

Everyone who has not made all their picks this week.

Parameters:
  • all_emails – Every user’s address.

  • picks – The week’s picks.

  • emails_by_id – Every user’s address, by id.

Returns:

The addresses of those with fewer than MAX_PICKS.