Files
SportstimeAPI/core/models/alias.py
Trey t 63acf7accb feat: add Django web app, CloudKit sync, dashboard, and game_datetime_utc export
Adds the full Django application layer on top of sportstime_parser:
- core: Sport, Team, Stadium, Game models with aliases and league structure
- scraper: orchestration engine, adapter, job management, Celery tasks
- cloudkit: CloudKit sync client, sync state tracking, sync jobs
- dashboard: staff dashboard for monitoring scrapers, sync, review queue
- notifications: email reports for scrape/sync results
- Docker setup for deployment (Dockerfile, docker-compose, entrypoint)

Game exports now use game_datetime_utc (ISO 8601 UTC) instead of
venue-local date+time strings, matching the canonical format used
by the iOS app.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 14:04:27 -06:00

170 lines
5.1 KiB
Python

from django.db import models
from simple_history.models import HistoricalRecords
class TeamAlias(models.Model):
"""
Historical team name aliases for resolution.
Handles team renames, relocations, and alternate names.
"""
ALIAS_TYPE_CHOICES = [
('full_name', 'Full Name'),
('city_name', 'City + Name'),
('abbreviation', 'Abbreviation'),
('nickname', 'Nickname'),
('historical', 'Historical Name'),
]
team = models.ForeignKey(
'core.Team',
on_delete=models.CASCADE,
related_name='aliases'
)
alias = models.CharField(
max_length=200,
help_text='The alias text to match against'
)
alias_type = models.CharField(
max_length=20,
choices=ALIAS_TYPE_CHOICES,
default='full_name'
)
valid_from = models.DateField(
null=True,
blank=True,
help_text='Date from which this alias is valid (inclusive)'
)
valid_until = models.DateField(
null=True,
blank=True,
help_text='Date until which this alias is valid (inclusive)'
)
is_primary = models.BooleanField(
default=False,
help_text='Whether this is a primary/preferred alias'
)
source = models.CharField(
max_length=200,
blank=True,
help_text='Source of this alias (e.g., ESPN, Basketball-Reference)'
)
notes = models.TextField(
blank=True,
help_text='Notes about this alias (e.g., relocation details)'
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# Audit trail
history = HistoricalRecords()
class Meta:
ordering = ['team', '-valid_from']
verbose_name = 'Team Alias'
verbose_name_plural = 'Team Aliases'
indexes = [
models.Index(fields=['alias']),
models.Index(fields=['team', 'valid_from', 'valid_until']),
]
def __str__(self):
date_range = ""
if self.valid_from or self.valid_until:
start = self.valid_from.strftime('%Y') if self.valid_from else '...'
end = self.valid_until.strftime('%Y') if self.valid_until else 'present'
date_range = f" ({start}-{end})"
return f"{self.alias}{self.team.abbreviation}{date_range}"
def is_valid_for_date(self, check_date):
"""Check if this alias is valid for a given date."""
if self.valid_from and check_date < self.valid_from:
return False
if self.valid_until and check_date > self.valid_until:
return False
return True
class StadiumAlias(models.Model):
"""
Historical stadium name aliases for resolution.
Handles naming rights changes and alternate names.
"""
ALIAS_TYPE_CHOICES = [
('official', 'Official Name'),
('former', 'Former Name'),
('nickname', 'Nickname'),
('abbreviation', 'Abbreviation'),
]
stadium = models.ForeignKey(
'core.Stadium',
on_delete=models.CASCADE,
related_name='aliases'
)
alias = models.CharField(
max_length=200,
help_text='The alias text to match against'
)
alias_type = models.CharField(
max_length=20,
choices=ALIAS_TYPE_CHOICES,
default='official'
)
valid_from = models.DateField(
null=True,
blank=True,
help_text='Date from which this alias is valid (inclusive)'
)
valid_until = models.DateField(
null=True,
blank=True,
help_text='Date until which this alias is valid (inclusive)'
)
is_primary = models.BooleanField(
default=False,
help_text='Whether this is the current/primary name'
)
source = models.CharField(
max_length=200,
blank=True,
help_text='Source of this alias'
)
notes = models.TextField(
blank=True,
help_text='Notes about this alias (e.g., naming rights deal)'
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# Audit trail
history = HistoricalRecords()
class Meta:
ordering = ['stadium', '-valid_from']
verbose_name = 'Stadium Alias'
verbose_name_plural = 'Stadium Aliases'
indexes = [
models.Index(fields=['alias']),
models.Index(fields=['stadium', 'valid_from', 'valid_until']),
]
def __str__(self):
date_range = ""
if self.valid_from or self.valid_until:
start = self.valid_from.strftime('%Y') if self.valid_from else '...'
end = self.valid_until.strftime('%Y') if self.valid_until else 'present'
date_range = f" ({start}-{end})"
return f"{self.alias}{self.stadium.name}{date_range}"
def is_valid_for_date(self, check_date):
"""Check if this alias is valid for a given date."""
if self.valid_from and check_date < self.valid_from:
return False
if self.valid_until and check_date > self.valid_until:
return False
return True