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>
97 lines
3.5 KiB
HTML
97 lines
3.5 KiB
HTML
{% extends 'base.html' %}
|
|
|
|
{% block content %}
|
|
<div class="page-header">
|
|
<h2>Review Queue</h2>
|
|
<span class="badge {% if total_pending > 0 %}badge-warning{% else %}badge-success{% endif %}" style="font-size: 1rem; padding: 0.5rem 1rem;">
|
|
{{ total_pending }} Pending
|
|
</span>
|
|
</div>
|
|
|
|
{% if total_pending == 0 %}
|
|
<div class="alert alert-success">
|
|
No items pending review. All data has been validated.
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Summary by Sport and Type -->
|
|
{% if review_summary %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3>Review Summary</h3>
|
|
</div>
|
|
<div class="stat-grid">
|
|
{% for item in review_summary %}
|
|
<div class="stat-card warning">
|
|
<div class="stat-value">{{ item.count }}</div>
|
|
<div class="stat-label">{{ item.sport__short_name }} - {{ item.item_type|title }}</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Pending Items -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3>Pending Reviews</h3>
|
|
<a href="{% url 'admin:scraper_manualreviewitem_changelist' %}?status__exact=pending" class="btn btn-primary">View in Admin</a>
|
|
</div>
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Type</th>
|
|
<th>Sport</th>
|
|
<th>Raw Value</th>
|
|
<th>Matched To</th>
|
|
<th>Confidence</th>
|
|
<th>Created</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in pending_items %}
|
|
<tr>
|
|
<td>
|
|
{% if item.item_type == 'team' %}
|
|
<span class="badge badge-info">Team</span>
|
|
{% elif item.item_type == 'stadium' %}
|
|
<span class="badge badge-secondary">Stadium</span>
|
|
{% elif item.item_type == 'game' %}
|
|
<span class="badge badge-warning">Game</span>
|
|
{% else %}
|
|
<span class="badge badge-secondary">{{ item.item_type }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ item.sport.short_name }}</td>
|
|
<td><strong>{{ item.raw_value }}</strong></td>
|
|
<td>
|
|
{% if item.matched_value %}
|
|
{{ item.matched_value }}
|
|
{% else %}
|
|
<span class="text-muted">No match</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="progress-bar" style="width: 80px;">
|
|
<div class="fill" style="width: {{ item.confidence }}%; background: {% if item.confidence >= 85 %}#28a745{% elif item.confidence >= 70 %}#ffc107{% else %}#dc3545{% endif %};"></div>
|
|
</div>
|
|
<small class="text-muted">{{ item.confidence }}%</small>
|
|
</td>
|
|
<td class="text-muted">{{ item.created_at|timesince }} ago</td>
|
|
<td>
|
|
<a href="{% url 'admin:scraper_manualreviewitem_change' item.id %}" class="btn btn-primary" style="padding: 0.25rem 0.5rem; font-size: 0.85rem;">
|
|
Review
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="7" class="text-muted">No pending reviews</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endblock %}
|