from django.contrib import admin, messages from django_object_actions import ( DjangoObjectActions, action, takes_instance_or_queryset, ) from submissions.tasks import schedule_scope_sync, stop_scope_sync from .models import EventGame, Game, PlanningSlot from .tasks import refresh_steam_games_task class EventGameInline(admin.TabularInline): model = EventGame extra = 0 @admin.register(Game) class GameAdmin(admin.ModelAdmin): list_display = ["name", "steam_app_id", "updated_at"] search_fields = ["name", "steam_app_id"] readonly_fields = [ "created_at", "updated_at", "steam_price_initial", "steam_price_final", "steam_discount_percent", "steam_currency", "steam_is_free", "steam_short_description", "steam_header_image", "steam_info_updated_at", ] inlines = [EventGameInline] actions = ["refresh_steam_info"] @admin.action(description="Refresh Steam store info") def refresh_steam_info(self, request, queryset): game_ids = list( queryset.filter(steam_app_id__isnull=False).values_list("id", flat=True) ) refresh_steam_games_task.enqueue(game_ids=game_ids) self.message_user( request, f"Queued Steam store refresh for {len(game_ids)} game(s)." ) @admin.register(EventGame) class EventGameAdmin(DjangoObjectActions, admin.ModelAdmin): list_display = ["game", "event", "enabled", "has_apibot_token"] list_filter = ["event", "enabled"] search_fields = ["game__name", "event__name"] actions = ["enable", "disable", "sync_scopes", "stop_scope_sync"] change_actions = ["sync_scopes", "stop_scope_sync"] @admin.display(description="AAndT token", boolean=True) def has_apibot_token(self, obj): return bool(obj.apibot_token) def _set_enabled(self, request, queryset, enabled: bool): # save() per row (not update()) so post_save fires, e.g. the clock heartbeat changed = 0 for event_game in queryset.select_related("game").filter(enabled=not enabled): event_game.enabled = enabled event_game.save(update_fields=["enabled"]) changed += 1 self.message_user( request, f"{'Enabled' if enabled else 'Disabled'} {changed} EventGame(s)" f" ({queryset.count() - changed} already {'enabled' if enabled else 'disabled'}).", ) @admin.action(description="Enable selected EventGames") def enable(self, request, queryset): self._set_enabled(request, queryset, enabled=True) @admin.action(description="Disable selected EventGames") def disable(self, request, queryset): self._set_enabled(request, queryset, enabled=False) @action( label="Sync scopes", description="Sync scopes from AAndT for selected EventGames", ) @takes_instance_or_queryset def sync_scopes(self, request, queryset): tokened = queryset.exclude(apibot_token__isnull=True).exclude(apibot_token="") skipped = queryset.count() - tokened.count() started = sum(schedule_scope_sync(event_game.id) for event_game in tokened) self.message_user( request, f"Started scope sync for {started} EventGame(s)" f" ({tokened.count() - started} already running).", ) if skipped: self.message_user( request, f"Skipped {skipped} EventGame(s) with no AAndT token configured.", level=messages.WARNING, ) @action( label="Stop scope sync", description="Stop the scope sync loop of selected EventGames", ) @takes_instance_or_queryset def stop_scope_sync(self, request, queryset): stopped = sum(stop_scope_sync(event_game.id) for event_game in queryset) self.message_user(request, f"Stopped scope sync for {stopped} EventGame(s).") @admin.register(PlanningSlot) class PlanningSlotAdmin(admin.ModelAdmin): list_display = ["title", "event", "begin", "end", "column", "is_multi_anims"] list_filter = ["event", "is_multi_anims"] search_fields = ["title"]