Files
SportstimeAPI/templates/dashboard/export.html
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

177 lines
9.1 KiB
HTML

{% extends 'base.html' %}
{% block content %}
<div class="page-header">
<h2>Export Data</h2>
</div>
<!-- Overview Stats -->
<div class="stat-grid mb-2">
<div class="stat-card">
<div class="stat-value">{{ counts.teams }}</div>
<div class="stat-label">Teams</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ counts.stadiums }}</div>
<div class="stat-label">Stadiums</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ counts.games|floatformat:0 }}</div>
<div class="stat-label">Games</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ counts.team_aliases }}</div>
<div class="stat-label">Team Aliases</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ counts.stadium_aliases }}</div>
<div class="stat-label">Stadium Aliases</div>
</div>
</div>
<div class="grid-2">
<!-- Export Options -->
<div class="card">
<div class="card-header">
<h3>Export Options</h3>
</div>
<form id="exportForm" method="get" action="{% url 'dashboard:export_download' %}">
<div style="margin-bottom: 1.5rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Data Types</label>
<div style="display: grid; gap: 0.5rem;">
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" name="type" value="sports" checked>
<span>Sports</span>
<span class="text-muted" style="font-size: 0.85rem;">({{ counts.sports }} sports)</span>
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" name="type" value="league_structure" checked>
<span>League Structure</span>
<span class="text-muted" style="font-size: 0.85rem;">({{ counts.conferences }} conferences, {{ counts.divisions }} divisions)</span>
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" name="type" value="teams" checked>
<span>Teams</span>
<span class="text-muted" style="font-size: 0.85rem;">({{ counts.teams }} teams)</span>
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" name="type" value="stadiums" checked>
<span>Stadiums</span>
<span class="text-muted" style="font-size: 0.85rem;">({{ counts.stadiums }} stadiums)</span>
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" name="type" value="games" checked>
<span>Games</span>
<span class="text-muted" style="font-size: 0.85rem;">({{ counts.games }} games)</span>
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" name="type" value="team_aliases" checked>
<span>Team Aliases</span>
<span class="text-muted" style="font-size: 0.85rem;">({{ counts.team_aliases }} aliases)</span>
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" name="type" value="stadium_aliases" checked>
<span>Stadium Aliases</span>
<span class="text-muted" style="font-size: 0.85rem;">({{ counts.stadium_aliases }} aliases)</span>
</label>
</div>
</div>
<div style="margin-bottom: 1.5rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Filter by Sport</label>
<select name="sport" style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 6px;">
<option value="">All Sports</option>
{% for sport in sports %}
<option value="{{ sport.code }}">{{ sport.short_name }} - {{ sport.name }}</option>
{% endfor %}
</select>
</div>
<div style="margin-bottom: 1.5rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Filter Games by Year</label>
<select name="year" style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 6px;">
<option value="">All Years</option>
{% for year in years %}
<option value="{{ year }}">{{ year }}</option>
{% endfor %}
</select>
<span class="text-muted" style="font-size: 0.85rem; display: block; margin-top: 0.25rem;">All games played in this calendar year, regardless of season</span>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; padding: 0.75rem;">
Download Export
</button>
</form>
</div>
<!-- Export Files Info -->
<div class="card">
<div class="card-header">
<h3>Output Files</h3>
</div>
<div style="display: grid; gap: 1rem;">
<div style="padding: 1rem; background: #f8f9fa; border-radius: 8px;">
<strong style="color: #1a1a2e;">sports_canonical.json</strong>
<p class="text-muted mb-0" style="font-size: 0.85rem; margin-top: 0.25rem;">
Sport metadata: display names, SF Symbol icons, brand colors, and season month ranges.
</p>
</div>
<div style="padding: 1rem; background: #f8f9fa; border-radius: 8px;">
<strong style="color: #1a1a2e;">league_structure.json</strong>
<p class="text-muted mb-0" style="font-size: 0.85rem; margin-top: 0.25rem;">
League hierarchy: sports as leagues, conferences, and divisions with parent_id references.
</p>
</div>
<div style="padding: 1rem; background: #f8f9fa; border-radius: 8px;">
<strong style="color: #1a1a2e;">teams_canonical.json</strong>
<p class="text-muted mb-0" style="font-size: 0.85rem; margin-top: 0.25rem;">
Teams with canonical IDs, abbreviations, stadium references, and conference/division IDs.
</p>
</div>
<div style="padding: 1rem; background: #f8f9fa; border-radius: 8px;">
<strong style="color: #1a1a2e;">stadiums_canonical.json</strong>
<p class="text-muted mb-0" style="font-size: 0.85rem; margin-top: 0.25rem;">
Stadiums with coordinates, capacity, timezone, and primary team abbreviations.
</p>
</div>
<div style="padding: 1rem; background: #f8f9fa; border-radius: 8px;">
<strong style="color: #1a1a2e;">games_canonical.json</strong>
<p class="text-muted mb-0" style="font-size: 0.85rem; margin-top: 0.25rem;">
Games with local date/time (converted from UTC), team names, abbreviations, and canonical IDs.
</p>
</div>
<div style="padding: 1rem; background: #f8f9fa; border-radius: 8px;">
<strong style="color: #1a1a2e;">team_aliases.json</strong>
<p class="text-muted mb-0" style="font-size: 0.85rem; margin-top: 0.25rem;">
Historical team names and abbreviations with validity date ranges.
</p>
</div>
<div style="padding: 1rem; background: #f8f9fa; border-radius: 8px;">
<strong style="color: #1a1a2e;">stadium_aliases.json</strong>
<p class="text-muted mb-0" style="font-size: 0.85rem; margin-top: 0.25rem;">
Stadium name aliases (e.g., former naming rights) with validity dates.
</p>
</div>
</div>
</div>
</div>
<!-- Quick Export Buttons -->
<div class="card mt-2">
<div class="card-header">
<h3>Quick Export</h3>
</div>
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
<a href="{% url 'dashboard:export_download' %}?type=sports&type=league_structure&type=teams&type=stadiums&type=games&type=team_aliases&type=stadium_aliases" class="btn btn-success">
Export All Data
</a>
<a href="{% url 'dashboard:export_download' %}?type=sports&type=league_structure&type=teams&type=stadiums" class="btn btn-primary">
Export Structure Only (No Games)
</a>
<a href="{% url 'dashboard:export_download' %}?type=games" class="btn btn-secondary">
Export Games Only
</a>
</div>
</div>
{% endblock %}