Files
RosterChief/events/services/competitions.py
Bernard Siebens 98b8002a04 Add billing-ending banner, events CRUD, RBIHF import, public API, team photos, and sponsors
A large batch of club-management features built up over one session:

- Club dashboard banner warning admins 1 month before billing ends
- Full Events/EventSeries CRUD (recurrence builder, occurrence lifecycle,
  per-team permissions), with match->game rename and game-specific fields
  (score, competition, live status, external game ID)
- Django-admin competition dropdown, gated per-club by feature flag
- Auto-import of RBIHF fixtures (scrape -> diff -> preview -> confirm),
  with location/opponent dropdowns suggested from existing club data
- Feature-flag-gated Shop/Forms nav sections, reusing the same flag
  machinery for the RBIHF import button
- Team roster now scoped to members active this season or next, sorted and
  grouped by position
- Club sport type (ice hockey / other), shown in the control panel's club
  subtitle
- Per-season team photo upload from the team page
- New public read-only API (Django Ninja) at /api/v1/: news, team rosters,
  upcoming/live/per-team games, and sponsors -- auto-documented via Swagger
  UI, CORS-enabled for a club's own external website
- Club sponsors: admin-only CRUD (logo, URL, active date window) plus a
  date-windowed, optionally randomized API endpoint
- Assorted fixes: NullBooleanField dropdown rendering, cross-club event
  validation timing, searchable-select chip placement, btn-neutral ->
  default button style sweep, calendar-month chart windows

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1gj3J1QPfP38XWpnpbFpy
2026-08-06 17:36:04 +02:00

47 lines
2.0 KiB
Python

"""Refreshing a game's score/status from its competition's own data source.
Event.competition (free text) and Event.external_game_id (that source's id for this
fixture) exist so a real integration has somewhere to key off later -- nothing
fetches anything yet. This is that integration's landing spot: the button and view
that call it already exist (management/views.py::EventFetchGameInfoView), so wiring
up a real data source later is a matter of replacing fetch_game_info's body, not
building the UI around it.
"""
import importlib
from events.models import Competition
class CompetitionFetchError(Exception):
"""A game's info could not be fetched from its competition's data source."""
def fetch_game_info(event) -> bool:
"""Refresh `event`'s score/live status from its competition's data source.
Gated on that competition's own feature flag being active for the event's
club: the management form's competition dropdown deliberately shows every
competition regardless of flag (see management.forms.EventForm), so this is
where per-club access actually gets enforced. No flag, an inactive one, or
a competition name that matches nothing -- there's no data source this club
is allowed to use, so this quietly does nothing and returns False rather
than erroring: missing access isn't a failure to report.
Returns whether a fetch was actually attempted.
"""
competition = Competition.objects.filter(name=event.competition).select_related("flag").first()
if competition is None or competition.flag is None or not competition.flag.is_active_for_club(event.club):
return False
try:
module = importlib.import_module(competition.module)
competition = getattr(module, competition.name)
competition().update_game_information(event=event)
return True
except Exception as error:
raise CompetitionFetchError("No competition data source is configured yet -- there's nothing to fetch from.") from error