""" Admin configuration for scraper models. """ from django.contrib import admin from django.utils.html import format_html from import_export.admin import ImportExportMixin, ImportExportModelAdmin from simple_history.admin import SimpleHistoryAdmin from .models import ScraperConfig, ScrapeJob, ManualReviewItem from .resources import ScraperConfigResource, ScrapeJobResource, ManualReviewItemResource @admin.register(ScraperConfig) class ScraperConfigAdmin(ImportExportMixin, SimpleHistoryAdmin): resource_class = ScraperConfigResource list_display = [ '__str__', 'sport', 'season', 'is_active', 'last_scrape_at', 'next_scrape_at', 'scrape_interval_hours', ] list_filter = ['sport', 'is_active', 'season'] search_fields = ['sport__name', 'sport__short_name'] ordering = ['-season', 'sport'] readonly_fields = ['created_at', 'updated_at'] @admin.register(ScrapeJob) class ScrapeJobAdmin(ImportExportModelAdmin): resource_class = ScrapeJobResource list_display = [ '__str__', 'status_badge', 'games_found', 'games_created', 'games_updated', 'duration_display', 'created_at', ] list_filter = ['status', 'config__sport', ('created_at', admin.DateFieldListFilter)] search_fields = ['config__sport__name', 'errors'] ordering = ['-created_at'] readonly_fields = ['created_at', 'updated_at', 'duration_display'] @admin.display(description='Status') def status_badge(self, obj): colors = { 'pending': '#ffc107', 'running': '#17a2b8', 'completed': '#28a745', 'failed': '#dc3545', 'cancelled': '#6c757d', } color = colors.get(obj.status, '#6c757d') return format_html( '{}', color, obj.get_status_display() ) @admin.display(description='Duration') def duration_display(self, obj): duration = obj.duration if duration is not None: if duration < 60: return f"{duration:.1f}s" elif duration < 3600: return f"{duration/60:.1f}m" else: return f"{duration/3600:.1f}h" return '-' @admin.register(ManualReviewItem) class ManualReviewItemAdmin(ImportExportModelAdmin): resource_class = ManualReviewItemResource list_display = [ 'raw_value', 'item_type', 'sport', 'status_badge', 'confidence_bar', 'matched_value', 'created_at', ] list_filter = ['status', 'item_type', 'sport'] search_fields = ['raw_value', 'matched_value'] ordering = ['-confidence', '-created_at'] readonly_fields = ['created_at', 'updated_at', 'resolved_at', 'resolved_by'] actions = ['approve_items', 'reject_items'] @admin.display(description='Status') def status_badge(self, obj): colors = { 'pending': '#ffc107', 'approved': '#28a745', 'rejected': '#dc3545', 'resolved': '#17a2b8', } color = colors.get(obj.status, '#6c757d') return format_html( '{}', color, obj.get_status_display() ) @admin.display(description='Confidence') def confidence_bar(self, obj): color = '#28a745' if obj.confidence >= 85 else '#ffc107' if obj.confidence >= 70 else '#dc3545' return format_html( '
' '
' '{}%
', obj.confidence, color, obj.confidence ) @admin.action(description='Approve selected items') def approve_items(self, request, queryset): from django.utils import timezone updated = queryset.update( status='approved', resolved_at=timezone.now(), resolved_by=request.user ) self.message_user(request, f'{updated} items approved.') @admin.action(description='Reject selected items') def reject_items(self, request, queryset): from django.utils import timezone updated = queryset.update( status='rejected', resolved_at=timezone.now(), resolved_by=request.user ) self.message_user(request, f'{updated} items rejected.')