feat: add final.png export
This commit is contained in:
@@ -10,6 +10,7 @@ from ninja.security import django_auth_is_staff
|
|||||||
from games.decorators import require_game_enabled
|
from games.decorators import require_game_enabled
|
||||||
|
|
||||||
from .canvas import read_canvas
|
from .canvas import read_canvas
|
||||||
|
from .export import build_final_png
|
||||||
from .gif import build_evolution_gif
|
from .gif import build_evolution_gif
|
||||||
from .heatmap import (
|
from .heatmap import (
|
||||||
DecayMode,
|
DecayMode,
|
||||||
@@ -59,6 +60,14 @@ def evolution_gif(request: HttpRequest, event_path: str, players: str = ""):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(f"{PREFIX}/final.png", auth=django_auth_is_staff)
|
||||||
|
@decorate_view(require_game_enabled(path="place"))
|
||||||
|
def final_png(request: HttpRequest, event_path: str):
|
||||||
|
"""Admin-only: the real final board as a PNG, even once placement is
|
||||||
|
over and everyone else sees it wiped."""
|
||||||
|
return HttpResponse(build_final_png(request.event), content_type="image/png")
|
||||||
|
|
||||||
|
|
||||||
@router.get(f"{PREFIX}/heatmap.png", auth=django_auth_is_staff)
|
@router.get(f"{PREFIX}/heatmap.png", auth=django_auth_is_staff)
|
||||||
@decorate_view(require_game_enabled(path="place"))
|
@decorate_view(require_game_enabled(path="place"))
|
||||||
def heatmap_png(request: HttpRequest, event_path: str, players: str = ""):
|
def heatmap_png(request: HttpRequest, event_path: str, players: str = ""):
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
"""Exports one event's final board as a PNG.
|
||||||
|
|
||||||
|
Reads the live Redis canvas rather than replaying the history like gif.py -
|
||||||
|
once placement is over the canvas endpoint serves a wiped board, but Redis
|
||||||
|
still holds the real final one (see tasks.py).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
from events.models import Event
|
||||||
|
|
||||||
|
from .canvas import read_canvas
|
||||||
|
from .gif import _render_frame
|
||||||
|
from .models import PlaceSettings
|
||||||
|
|
||||||
|
|
||||||
|
def build_final_png(event: Event) -> bytes:
|
||||||
|
"""Renders `event`'s current board as a PNG, upscaled like gif.py.
|
||||||
|
|
||||||
|
Redis only pads up to the last written pixel, so a short canvas is
|
||||||
|
padded with white (and a longer one, e.g. from before a shrink, cut)
|
||||||
|
to the current grid dimensions."""
|
||||||
|
place_settings = PlaceSettings.current(event)
|
||||||
|
width, height = place_settings.width, place_settings.height
|
||||||
|
size = width * height
|
||||||
|
|
||||||
|
canvas = read_canvas(event.path, width, height)[:size].ljust(size, b"\0")
|
||||||
|
|
||||||
|
buffer = BytesIO()
|
||||||
|
_render_frame(bytearray(canvas), width, height).save(buffer, format="PNG")
|
||||||
|
return buffer.getvalue()
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from io import BytesIO
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
from accounts.models import CustomUser
|
from accounts.models import CustomUser
|
||||||
from events.models import Event
|
from events.models import Event
|
||||||
@@ -15,6 +17,7 @@ from .wire import to_epoch_ms
|
|||||||
|
|
||||||
URL = "/api/events/event/place/stats"
|
URL = "/api/events/event/place/stats"
|
||||||
CANVAS_URL = "/api/events/event/place/canvas"
|
CANVAS_URL = "/api/events/event/place/canvas"
|
||||||
|
FINAL_URL = "/api/events/event/place/final.png"
|
||||||
LOCMEM_CACHE = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
|
LOCMEM_CACHE = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
|
||||||
|
|
||||||
|
|
||||||
@@ -94,6 +97,45 @@ class PlaceStatsTests(TestCase):
|
|||||||
self.assertEqual(sum(row["placements"] for row in data["per_hour"]), 5)
|
self.assertEqual(sum(row["placements"] for row in data["per_hour"]), 5)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("place.export.read_canvas")
|
||||||
|
class FinalPngTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.event = Event.objects.create(name="Event", path="event")
|
||||||
|
place = Game.objects.create(name="Place", path="place", custom_path=True)
|
||||||
|
EventGame.objects.create(event=self.event, game=place)
|
||||||
|
PlaceSettings.objects.create(
|
||||||
|
event=self.event,
|
||||||
|
width=2,
|
||||||
|
height=1,
|
||||||
|
# Ended, the real board is still exported
|
||||||
|
placement_ends_at=timezone.now() - timedelta(hours=1),
|
||||||
|
)
|
||||||
|
self.alice = CustomUser.objects.create_user(username="alice", password="x")
|
||||||
|
self.admin = CustomUser.objects.create_user(
|
||||||
|
username="admin", password="x", is_staff=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_requires_staff(self, _):
|
||||||
|
self.client.force_login(self.alice)
|
||||||
|
|
||||||
|
response = self.client.get(FINAL_URL)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
||||||
|
def test_final_png(self, read_canvas):
|
||||||
|
# Short canvas, Redis only padded up to the last written pixel
|
||||||
|
read_canvas.return_value = bytes([5])
|
||||||
|
self.client.force_login(self.admin)
|
||||||
|
|
||||||
|
response = self.client.get(FINAL_URL)
|
||||||
|
|
||||||
|
self.assertEqual(response["Content-Type"], "image/png")
|
||||||
|
image = Image.open(BytesIO(response.content))
|
||||||
|
self.assertEqual(image.size, (16, 8))
|
||||||
|
self.assertEqual(image.getpixel((0, 0)), (229, 0, 0))
|
||||||
|
self.assertEqual(image.getpixel((15, 0)), (255, 255, 255))
|
||||||
|
|
||||||
|
|
||||||
@override_settings(CACHES=LOCMEM_CACHE)
|
@override_settings(CACHES=LOCMEM_CACHE)
|
||||||
@mock.patch("place.signals.rebuild_canvas_from_history")
|
@mock.patch("place.signals.rebuild_canvas_from_history")
|
||||||
class PlacementEndTests(TestCase):
|
class PlacementEndTests(TestCase):
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import { type DefaultError, type InfiniteData, infiniteQueryOptions, queryOptions, type UseMutationOptions } from '@tanstack/vue-query';
|
import { type DefaultError, type InfiniteData, infiniteQueryOptions, queryOptions, type UseMutationOptions } from '@tanstack/vue-query';
|
||||||
|
|
||||||
import { client } from '../client.gen';
|
import { client } from '../client.gen';
|
||||||
import { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, clockApiCurrentState, clockApiMyStats, gamesApiListEventGames, gamesApiListEventPlanning, gamesApiListEventSteamGames, gamesApiListGames, marketApiBetHistory, marketApiCloseMarket, marketApiCreateBet, marketApiGetUserPoints, marketApiListMarkets, marketApiListUserBets, marketApiResolveMarket, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponseFiles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, placeApiCanvas, placeApiEvolutionGif, placeApiHeatmapDecayGif, placeApiHeatmapEvolutionGif, placeApiHeatmapPng, placeApiPlacementsCsv, placeApiStats, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck, submissionsApiCreateImageCrop, submissionsApiCreateSubmission, submissionsApiDeleteImageCrop, submissionsApiGetSubmission, submissionsApiListAvailableScopes, submissionsApiListMyScopes, submissionsApiListSubmissionsByGame, submissionsApiListUserSubmissions, submissionsApiProcessCropOcr, submissionsApiProcessFileOcr, submissionsApiStopSyncScopes, submissionsApiSyncScopes, submissionsApiSyncScopesStatus } from '../sdk.gen';
|
import { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, clockApiCurrentState, clockApiMyStats, gamesApiListEventGames, gamesApiListEventPlanning, gamesApiListEventSteamGames, gamesApiListGames, marketApiBetHistory, marketApiCloseMarket, marketApiCreateBet, marketApiGetUserPoints, marketApiListMarkets, marketApiListUserBets, marketApiResolveMarket, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponseFiles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, placeApiCanvas, placeApiEvolutionGif, placeApiFinalPng, placeApiHeatmapDecayGif, placeApiHeatmapEvolutionGif, placeApiHeatmapPng, placeApiPlacementsCsv, placeApiStats, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck, submissionsApiCreateImageCrop, submissionsApiCreateSubmission, submissionsApiDeleteImageCrop, submissionsApiGetSubmission, submissionsApiListAvailableScopes, submissionsApiListMyScopes, submissionsApiListSubmissionsByGame, submissionsApiListUserSubmissions, submissionsApiProcessCropOcr, submissionsApiProcessFileOcr, submissionsApiStopSyncScopes, submissionsApiSyncScopes, submissionsApiSyncScopesStatus } from '../sdk.gen';
|
||||||
import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, ClockApiCurrentStateData, ClockApiCurrentStateResponse, ClockApiMyStatsData, ClockApiMyStatsResponse, GamesApiListEventGamesData, GamesApiListEventGamesResponse, GamesApiListEventPlanningData, GamesApiListEventPlanningResponse, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponse, GamesApiListGamesData, GamesApiListGamesResponse, MarketApiBetHistoryData, MarketApiBetHistoryResponse, MarketApiCloseMarketData, MarketApiCreateBetData, MarketApiCreateBetResponse, MarketApiGetUserPointsData, MarketApiGetUserPointsResponse, MarketApiListMarketsData, MarketApiListMarketsResponse, MarketApiListUserBetsData, MarketApiListUserBetsResponse, MarketApiResolveMarketData, MarketApiResolveMarketResponse, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileResponse, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionError, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionError, OpusMagnumApiDeleteSubmissionResponse, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetStatsData, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListResponseFilesData, OpusMagnumApiListResponseFilesError, OpusMagnumApiListResponseFilesResponse, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationError, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoError, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseError, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionError, OpusMagnumApiValidateSubmissionResponse, PlaceApiCanvasData, PlaceApiEvolutionGifData, PlaceApiHeatmapDecayGifData, PlaceApiHeatmapEvolutionGifData, PlaceApiHeatmapPngData, PlaceApiPlacementsCsvData, PlaceApiStatsData, PlaceApiStatsResponse, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheError, PolylanSubmitterApiClearCacheResponse, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponse, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropError, SubmissionsApiCreateImageCropResponse, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionError, SubmissionsApiCreateSubmissionResponse, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropError, SubmissionsApiDeleteImageCropResponse, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionError, SubmissionsApiGetSubmissionResponse, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesError, SubmissionsApiListAvailableScopesResponse, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesError, SubmissionsApiListMyScopesResponse, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameError, SubmissionsApiListSubmissionsByGameResponse, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsError, SubmissionsApiListUserSubmissionsResponse, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrError, SubmissionsApiProcessCropOcrResponse, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrError, SubmissionsApiProcessFileOcrResponse, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesError, SubmissionsApiStopSyncScopesResponse, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesError, SubmissionsApiSyncScopesResponse, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusError, SubmissionsApiSyncScopesStatusResponse } from '../types.gen';
|
import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, ClockApiCurrentStateData, ClockApiCurrentStateResponse, ClockApiMyStatsData, ClockApiMyStatsResponse, GamesApiListEventGamesData, GamesApiListEventGamesResponse, GamesApiListEventPlanningData, GamesApiListEventPlanningResponse, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponse, GamesApiListGamesData, GamesApiListGamesResponse, MarketApiBetHistoryData, MarketApiBetHistoryResponse, MarketApiCloseMarketData, MarketApiCreateBetData, MarketApiCreateBetResponse, MarketApiGetUserPointsData, MarketApiGetUserPointsResponse, MarketApiListMarketsData, MarketApiListMarketsResponse, MarketApiListUserBetsData, MarketApiListUserBetsResponse, MarketApiResolveMarketData, MarketApiResolveMarketResponse, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileResponse, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionError, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionError, OpusMagnumApiDeleteSubmissionResponse, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetStatsData, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListResponseFilesData, OpusMagnumApiListResponseFilesError, OpusMagnumApiListResponseFilesResponse, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationError, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoError, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseError, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionError, OpusMagnumApiValidateSubmissionResponse, PlaceApiCanvasData, PlaceApiEvolutionGifData, PlaceApiFinalPngData, PlaceApiHeatmapDecayGifData, PlaceApiHeatmapEvolutionGifData, PlaceApiHeatmapPngData, PlaceApiPlacementsCsvData, PlaceApiStatsData, PlaceApiStatsResponse, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheError, PolylanSubmitterApiClearCacheResponse, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponse, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropError, SubmissionsApiCreateImageCropResponse, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionError, SubmissionsApiCreateSubmissionResponse, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropError, SubmissionsApiDeleteImageCropResponse, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionError, SubmissionsApiGetSubmissionResponse, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesError, SubmissionsApiListAvailableScopesResponse, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesError, SubmissionsApiListMyScopesResponse, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameError, SubmissionsApiListSubmissionsByGameResponse, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsError, SubmissionsApiListUserSubmissionsResponse, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrError, SubmissionsApiProcessCropOcrResponse, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrError, SubmissionsApiProcessFileOcrResponse, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesError, SubmissionsApiStopSyncScopesResponse, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesError, SubmissionsApiSyncScopesResponse, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusError, SubmissionsApiSyncScopesStatusResponse } from '../types.gen';
|
||||||
|
|
||||||
export type QueryKey<TOptions extends Options> = [
|
export type QueryKey<TOptions extends Options> = [
|
||||||
Pick<TOptions, 'baseUrl' | 'body' | 'headers' | 'path' | 'query'> & {
|
Pick<TOptions, 'baseUrl' | 'body' | 'headers' | 'path' | 'query'> & {
|
||||||
@@ -876,7 +876,8 @@ export const placeApiCanvasQueryKey = (options: Options<PlaceApiCanvasData>) =>
|
|||||||
* Canvas
|
* Canvas
|
||||||
*
|
*
|
||||||
* The one-time full-grid load. Every placement after that arrives as a
|
* The one-time full-grid load. Every placement after that arrives as a
|
||||||
* single websocket message, never a re-fetch.
|
* single websocket message, never a re-fetch. Once placement is over the
|
||||||
|
* board is shown wiped (Redis keeps the real final board, see tasks.py).
|
||||||
*/
|
*/
|
||||||
export const placeApiCanvasOptions = (options: Options<PlaceApiCanvasData>) => queryOptions<unknown, DefaultError, unknown, ReturnType<typeof placeApiCanvasQueryKey>>({
|
export const placeApiCanvasOptions = (options: Options<PlaceApiCanvasData>) => queryOptions<unknown, DefaultError, unknown, ReturnType<typeof placeApiCanvasQueryKey>>({
|
||||||
queryFn: async ({ queryKey, signal }) => {
|
queryFn: async ({ queryKey, signal }) => {
|
||||||
@@ -914,6 +915,27 @@ export const placeApiEvolutionGifOptions = (options: Options<PlaceApiEvolutionGi
|
|||||||
queryKey: placeApiEvolutionGifQueryKey(options)
|
queryKey: placeApiEvolutionGifQueryKey(options)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const placeApiFinalPngQueryKey = (options: Options<PlaceApiFinalPngData>) => createQueryKey('placeApiFinalPng', options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final Png
|
||||||
|
*
|
||||||
|
* Admin-only: the real final board as a PNG, even once placement is
|
||||||
|
* over and everyone else sees it wiped.
|
||||||
|
*/
|
||||||
|
export const placeApiFinalPngOptions = (options: Options<PlaceApiFinalPngData>) => queryOptions<unknown, DefaultError, unknown, ReturnType<typeof placeApiFinalPngQueryKey>>({
|
||||||
|
queryFn: async ({ queryKey, signal }) => {
|
||||||
|
const { data } = await placeApiFinalPng({
|
||||||
|
...options,
|
||||||
|
...queryKey[0],
|
||||||
|
signal,
|
||||||
|
throwOnError: true
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
queryKey: placeApiFinalPngQueryKey(options)
|
||||||
|
});
|
||||||
|
|
||||||
export const placeApiHeatmapPngQueryKey = (options: Options<PlaceApiHeatmapPngData>) => createQueryKey('placeApiHeatmapPng', options);
|
export const placeApiHeatmapPngQueryKey = (options: Options<PlaceApiHeatmapPngData>) => createQueryKey('placeApiHeatmapPng', options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { type Client, formDataBodySerializer, type Options as Options2, type TDataShape } from './client';
|
import { type Client, formDataBodySerializer, type Options as Options2, type TDataShape } from './client';
|
||||||
import { client } from './client.gen';
|
import { client } from './client.gen';
|
||||||
import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponses, ClockApiCurrentStateData, ClockApiCurrentStateResponses, ClockApiMyStatsData, ClockApiMyStatsResponses, GamesApiListEventGamesData, GamesApiListEventGamesResponses, GamesApiListEventPlanningData, GamesApiListEventPlanningResponses, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponses, GamesApiListGamesData, GamesApiListGamesResponses, MarketApiBetHistoryData, MarketApiBetHistoryResponses, MarketApiCloseMarketData, MarketApiCloseMarketResponses, MarketApiCreateBetData, MarketApiCreateBetResponses, MarketApiGetUserPointsData, MarketApiGetUserPointsResponses, MarketApiListMarketsData, MarketApiListMarketsResponses, MarketApiListUserBetsData, MarketApiListUserBetsResponses, MarketApiResolveMarketData, MarketApiResolveMarketResponses, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponses, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionErrors, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionErrors, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponseFilesData, OpusMagnumApiListResponseFilesErrors, OpusMagnumApiListResponseFilesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationErrors, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoErrors, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseErrors, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionErrors, OpusMagnumApiValidateSubmissionResponses, PlaceApiCanvasData, PlaceApiCanvasResponses, PlaceApiEvolutionGifData, PlaceApiEvolutionGifResponses, PlaceApiHeatmapDecayGifData, PlaceApiHeatmapDecayGifResponses, PlaceApiHeatmapEvolutionGifData, PlaceApiHeatmapEvolutionGifResponses, PlaceApiHeatmapPngData, PlaceApiHeatmapPngResponses, PlaceApiPlacementsCsvData, PlaceApiPlacementsCsvResponses, PlaceApiStatsData, PlaceApiStatsResponses, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheErrors, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropErrors, SubmissionsApiCreateImageCropResponses, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionErrors, SubmissionsApiCreateSubmissionResponses, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropErrors, SubmissionsApiDeleteImageCropResponses, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionErrors, SubmissionsApiGetSubmissionResponses, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesErrors, SubmissionsApiListAvailableScopesResponses, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesErrors, SubmissionsApiListMyScopesResponses, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameErrors, SubmissionsApiListSubmissionsByGameResponses, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsErrors, SubmissionsApiListUserSubmissionsResponses, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrErrors, SubmissionsApiProcessCropOcrResponses, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrErrors, SubmissionsApiProcessFileOcrResponses, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesErrors, SubmissionsApiStopSyncScopesResponses, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesErrors, SubmissionsApiSyncScopesResponses, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusErrors, SubmissionsApiSyncScopesStatusResponses } from './types.gen';
|
import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponses, ClockApiCurrentStateData, ClockApiCurrentStateResponses, ClockApiMyStatsData, ClockApiMyStatsResponses, GamesApiListEventGamesData, GamesApiListEventGamesResponses, GamesApiListEventPlanningData, GamesApiListEventPlanningResponses, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponses, GamesApiListGamesData, GamesApiListGamesResponses, MarketApiBetHistoryData, MarketApiBetHistoryResponses, MarketApiCloseMarketData, MarketApiCloseMarketResponses, MarketApiCreateBetData, MarketApiCreateBetResponses, MarketApiGetUserPointsData, MarketApiGetUserPointsResponses, MarketApiListMarketsData, MarketApiListMarketsResponses, MarketApiListUserBetsData, MarketApiListUserBetsResponses, MarketApiResolveMarketData, MarketApiResolveMarketResponses, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponses, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionErrors, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionErrors, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponseFilesData, OpusMagnumApiListResponseFilesErrors, OpusMagnumApiListResponseFilesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationErrors, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoErrors, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseErrors, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionErrors, OpusMagnumApiValidateSubmissionResponses, PlaceApiCanvasData, PlaceApiCanvasResponses, PlaceApiEvolutionGifData, PlaceApiEvolutionGifResponses, PlaceApiFinalPngData, PlaceApiFinalPngResponses, PlaceApiHeatmapDecayGifData, PlaceApiHeatmapDecayGifResponses, PlaceApiHeatmapEvolutionGifData, PlaceApiHeatmapEvolutionGifResponses, PlaceApiHeatmapPngData, PlaceApiHeatmapPngResponses, PlaceApiPlacementsCsvData, PlaceApiPlacementsCsvResponses, PlaceApiStatsData, PlaceApiStatsResponses, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheErrors, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropErrors, SubmissionsApiCreateImageCropResponses, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionErrors, SubmissionsApiCreateSubmissionResponses, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropErrors, SubmissionsApiDeleteImageCropResponses, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionErrors, SubmissionsApiGetSubmissionResponses, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesErrors, SubmissionsApiListAvailableScopesResponses, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesErrors, SubmissionsApiListMyScopesResponses, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameErrors, SubmissionsApiListSubmissionsByGameResponses, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsErrors, SubmissionsApiListUserSubmissionsResponses, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrErrors, SubmissionsApiProcessCropOcrResponses, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrErrors, SubmissionsApiProcessFileOcrResponses, SubmissionsApiStopSyncScopesData, SubmissionsApiStopSyncScopesErrors, SubmissionsApiStopSyncScopesResponses, SubmissionsApiSyncScopesData, SubmissionsApiSyncScopesErrors, SubmissionsApiSyncScopesResponses, SubmissionsApiSyncScopesStatusData, SubmissionsApiSyncScopesStatusErrors, SubmissionsApiSyncScopesStatusResponses } from './types.gen';
|
||||||
|
|
||||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean, TResponse = unknown> = Options2<TData, ThrowOnError, TResponse> & {
|
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean, TResponse = unknown> = Options2<TData, ThrowOnError, TResponse> & {
|
||||||
/**
|
/**
|
||||||
@@ -334,7 +334,8 @@ export const clockApiMyStats = <ThrowOnError extends boolean = false>(options: O
|
|||||||
* Canvas
|
* Canvas
|
||||||
*
|
*
|
||||||
* The one-time full-grid load. Every placement after that arrives as a
|
* The one-time full-grid load. Every placement after that arrives as a
|
||||||
* single websocket message, never a re-fetch.
|
* single websocket message, never a re-fetch. Once placement is over the
|
||||||
|
* board is shown wiped (Redis keeps the real final board, see tasks.py).
|
||||||
*/
|
*/
|
||||||
export const placeApiCanvas = <ThrowOnError extends boolean = false>(options: Options<PlaceApiCanvasData, ThrowOnError>) => (options.client ?? client).get<PlaceApiCanvasResponses, unknown, ThrowOnError>({ url: '/api/events/{event_path}/place/canvas', ...options });
|
export const placeApiCanvas = <ThrowOnError extends boolean = false>(options: Options<PlaceApiCanvasData, ThrowOnError>) => (options.client ?? client).get<PlaceApiCanvasResponses, unknown, ThrowOnError>({ url: '/api/events/{event_path}/place/canvas', ...options });
|
||||||
|
|
||||||
@@ -356,6 +357,22 @@ export const placeApiEvolutionGif = <ThrowOnError extends boolean = false>(optio
|
|||||||
...options
|
...options
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final Png
|
||||||
|
*
|
||||||
|
* Admin-only: the real final board as a PNG, even once placement is
|
||||||
|
* over and everyone else sees it wiped.
|
||||||
|
*/
|
||||||
|
export const placeApiFinalPng = <ThrowOnError extends boolean = false>(options: Options<PlaceApiFinalPngData, ThrowOnError>) => (options.client ?? client).get<PlaceApiFinalPngResponses, unknown, ThrowOnError>({
|
||||||
|
security: [{
|
||||||
|
in: 'cookie',
|
||||||
|
name: 'sessionid',
|
||||||
|
type: 'apiKey'
|
||||||
|
}],
|
||||||
|
url: '/api/events/{event_path}/place/final.png',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Heatmap Png
|
* Heatmap Png
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -2881,6 +2881,25 @@ export type PlaceApiEvolutionGifResponses = {
|
|||||||
200: unknown;
|
200: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PlaceApiFinalPngData = {
|
||||||
|
body?: never;
|
||||||
|
path: {
|
||||||
|
/**
|
||||||
|
* Event Path
|
||||||
|
*/
|
||||||
|
event_path: string;
|
||||||
|
};
|
||||||
|
query?: never;
|
||||||
|
url: '/api/events/{event_path}/place/final.png';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlaceApiFinalPngResponses = {
|
||||||
|
/**
|
||||||
|
* OK
|
||||||
|
*/
|
||||||
|
200: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
export type PlaceApiHeatmapPngData = {
|
export type PlaceApiHeatmapPngData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path: {
|
path: {
|
||||||
|
|||||||
Reference in New Issue
Block a user