From a769d1a0b3af0dca6fd142ecba6bd08e1e0405e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gremaud?= Date: Mon, 25 May 2026 02:00:34 +0200 Subject: [PATCH] fix: display ocr data submissions --- polylan_submitter/src/Submissions.vue | 161 ++++++++++++++++++++++- polylan_submitter/src/api/index.ts | 2 +- polylan_submitter/src/api/sdk.gen.ts | 12 +- polylan_submitter/src/api/types.gen.ts | 96 ++++++++------ polylan_submitter/submissions/schemas.py | 3 +- 5 files changed, 220 insertions(+), 54 deletions(-) diff --git a/polylan_submitter/src/Submissions.vue b/polylan_submitter/src/Submissions.vue index 602fd28..ad05e10 100644 --- a/polylan_submitter/src/Submissions.vue +++ b/polylan_submitter/src/Submissions.vue @@ -30,6 +30,10 @@ const filePreviewUrl = ref(""); const isModalOpen = ref(false); const userSubmissions = ref([]); const isLoadingSubmissions = ref(false); +const selectedSubmissionFile = ref(null); +const isSubmissionFileModalOpen = ref(false); +const imageAspectRatio = ref<"portrait" | "landscape">("landscape"); +const copyFeedback = ref(""); const openFilePreview = async (file: File) => { selectedFile.value = file; @@ -58,6 +62,42 @@ const closeModal = () => { filePreviewUrl.value = ""; }; +const openSubmissionFileModal = (file: any) => { + selectedSubmissionFile.value = file; + isSubmissionFileModalOpen.value = true; + + // Check image dimensions for aspect ratio + if (file?.content_type?.startsWith("image/")) { + const img = new Image(); + img.onload = () => { + imageAspectRatio.value = img.height > img.width ? "portrait" : "landscape"; + }; + img.src = file.file_url; + } +}; + +const closeSubmissionFileModal = () => { + isSubmissionFileModalOpen.value = false; + selectedSubmissionFile.value = null; +}; + +const copyOCRText = async () => { + if (selectedSubmissionFile.value?.ocr_raw_data) { + try { + await navigator.clipboard.writeText(selectedSubmissionFile.value.ocr_raw_data); + copyFeedback.value = "Copied!"; + setTimeout(() => { + copyFeedback.value = ""; + }, 2000); + } catch (err) { + copyFeedback.value = "Failed to copy"; + setTimeout(() => { + copyFeedback.value = ""; + }, 2000); + } + } +}; + const getGameHeaderImage = () => { if (props.gameImage) { return props.gameImage; @@ -218,7 +258,8 @@ onMounted(() => {
-
+
@@ -265,7 +306,8 @@ onMounted(() => {

{{ file.name }}

{{ formatFileSize(file.size) }}

- @@ -407,9 +449,10 @@ onMounted(() => {
- + ({{ formatFileSize(file.file_size) }})
@@ -487,5 +530,115 @@ onMounted(() => { + + +
+ + + +
diff --git a/polylan_submitter/src/api/index.ts b/polylan_submitter/src/api/index.ts index ca084c4..7a73327 100644 --- a/polylan_submitter/src/api/index.ts +++ b/polylan_submitter/src/api/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts export { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, gamesApiListGames, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck, submissionsApiCreateSubmission, submissionsApiGetSubmission, submissionsApiListSubmissionsByGame, submissionsApiListUserSubmissions } from './sdk.gen'; -export type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, AnimationsApiTopSubmissionsResponses, ClientOptions, GameOut, GamesApiListGamesData, GamesApiListGamesResponse, GamesApiListGamesResponses, Input, LeaderboardEntryOut, LeaderboardOut, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponse, NoitaApiSubmitLogFileResponses, NoitaSubmissionOut, ObjectivResultOut, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionResponse, OpusMagnumApiValidateSubmissionResponses, PagedSubmissionOut, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses, PuzzlePointsFactorOut, PuzzleResponseIn, PuzzleResponseOut, PuzzleResponseRankingOut, PuzzleResultsOut, PuzzleSubmissionsOut, PuzzleSubmissionWithRankOut, RankingSchema, ResultsOut, SteamCollectionItemOut, SteamCollectionOut, SubmissionFileOut, SubmissionIn, SubmissionOut, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionResponse, SubmissionsApiCreateSubmissionResponses, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionResponse, SubmissionsApiGetSubmissionResponses, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameResponses, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsResponses, TournamentPuzzleResultsOut, TournamentSubmissionsOut, UserDisplayOut, UserInfoOut, ValidationIn, WinnerFileOut, WinnerResponseOut } from './types.gen'; +export type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiPuzzleResultsResponses, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiResultsResponses, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, AnimationsApiTopSubmissionsResponses, ClientOptions, GameOut, GamesApiListGamesData, GamesApiListGamesResponse, GamesApiListGamesResponses, Input, LeaderboardEntryOut, LeaderboardOut, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetLeaderboardResponses, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiGetResultsResponses, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileErrors, NoitaApiSubmitLogFileResponse, NoitaApiSubmitLogFileResponses, NoitaSubmissionOut, ObjectivResultOut, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiCreateSubmissionResponses, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiDeleteSubmissionResponses, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetCollectionResponses, OpusMagnumApiGetStatsData, OpusMagnumApiGetStatsResponses, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiGetSubmissionResponses, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListPuzzlesResponses, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListResponsesNeedingValidationResponses, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiListSubmissionsResponses, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateAutoResponses, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateResponseResponses, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionResponse, OpusMagnumApiValidateSubmissionResponses, PagedSubmissionOut, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiClearCacheResponses, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiGetUserInfoResponses, PolylanSubmitterApiHealthCheckData, PolylanSubmitterApiHealthCheckResponses, PuzzlePointsFactorOut, PuzzleResponseIn, PuzzleResponseOut, PuzzleResponseRankingOut, PuzzleResultsOut, PuzzleSubmissionsOut, PuzzleSubmissionWithRankOut, RankingSchema, ResultsOut, SteamCollectionItemOut, SteamCollectionOut, SubmissionFileOut, SubmissionIn, SubmissionOut, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionResponse, SubmissionsApiCreateSubmissionResponses, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionResponse, SubmissionsApiGetSubmissionResponses, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameResponse, SubmissionsApiListSubmissionsByGameResponses, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsResponse, SubmissionsApiListUserSubmissionsResponses, TournamentPuzzleResultsOut, TournamentSubmissionsOut, UserDisplayOut, UserInfoOut, ValidationIn, WinnerFileOut, WinnerResponseOut } from './types.gen'; diff --git a/polylan_submitter/src/api/sdk.gen.ts b/polylan_submitter/src/api/sdk.gen.ts index 72c6c79..1da849e 100644 --- a/polylan_submitter/src/api/sdk.gen.ts +++ b/polylan_submitter/src/api/sdk.gen.ts @@ -204,11 +204,6 @@ export const submissionsApiCreateSubmission = (options: Options) => (options.client ?? client).get({ url: '/api/submissions/{submission_id}', ...options }); - /** * List User Submissions */ @@ -217,4 +212,9 @@ export const submissionsApiListUserSubmissions = (options: Options) => (options.client ?? client).get({ url: '/api/submissions/game/{game_path}', ...options }); +export const submissionsApiListSubmissionsByGame = (options: Options) => (options.client ?? client).get({ url: '/api/submissions/game', ...options }); + +/** + * Get Submission + */ +export const submissionsApiGetSubmission = (options: Options) => (options.client ?? client).get({ url: '/api/submissions/{submission_id}', ...options }); diff --git a/polylan_submitter/src/api/types.gen.ts b/polylan_submitter/src/api/types.gen.ts index 61fdc0f..4a546e1 100644 --- a/polylan_submitter/src/api/types.gen.ts +++ b/polylan_submitter/src/api/types.gen.ts @@ -351,14 +351,14 @@ export type SubmissionFileOut = { * Original Filename */ original_filename: string; - /** - * File Size - */ - file_size: number; /** * Content Type */ content_type: string; + /** + * File Size + */ + file_size: number; /** * File Url */ @@ -367,6 +367,10 @@ export type SubmissionFileOut = { * Ocr Status */ ocr_status: string; + /** + * Ocr Raw Data + */ + ocr_raw_data: string; }; /** @@ -1349,6 +1353,52 @@ export type SubmissionsApiCreateSubmissionResponses = { export type SubmissionsApiCreateSubmissionResponse = SubmissionsApiCreateSubmissionResponses[keyof SubmissionsApiCreateSubmissionResponses]; +export type SubmissionsApiListUserSubmissionsData = { + body?: never; + path?: never; + query: { + /** + * Game Path + */ + game_path: string; + }; + url: '/api/submissions/user'; +}; + +export type SubmissionsApiListUserSubmissionsResponses = { + /** + * Response + * + * OK + */ + 200: Array; +}; + +export type SubmissionsApiListUserSubmissionsResponse = SubmissionsApiListUserSubmissionsResponses[keyof SubmissionsApiListUserSubmissionsResponses]; + +export type SubmissionsApiListSubmissionsByGameData = { + body?: never; + path?: never; + query: { + /** + * Game Path + */ + game_path: string; + }; + url: '/api/submissions/game'; +}; + +export type SubmissionsApiListSubmissionsByGameResponses = { + /** + * Response + * + * OK + */ + 200: Array; +}; + +export type SubmissionsApiListSubmissionsByGameResponse = SubmissionsApiListSubmissionsByGameResponses[keyof SubmissionsApiListSubmissionsByGameResponses]; + export type SubmissionsApiGetSubmissionData = { body?: never; path: { @@ -1369,41 +1419,3 @@ export type SubmissionsApiGetSubmissionResponses = { }; export type SubmissionsApiGetSubmissionResponse = SubmissionsApiGetSubmissionResponses[keyof SubmissionsApiGetSubmissionResponses]; - -export type SubmissionsApiListUserSubmissionsData = { - body?: never; - path?: never; - query: { - /** - * Game Path - */ - game_path: string; - }; - url: '/api/submissions/user'; -}; - -export type SubmissionsApiListUserSubmissionsResponses = { - /** - * OK - */ - 200: unknown; -}; - -export type SubmissionsApiListSubmissionsByGameData = { - body?: never; - path: { - /** - * Game Path - */ - game_path: string; - }; - query?: never; - url: '/api/submissions/game/{game_path}'; -}; - -export type SubmissionsApiListSubmissionsByGameResponses = { - /** - * OK - */ - 200: unknown; -}; diff --git a/polylan_submitter/submissions/schemas.py b/polylan_submitter/submissions/schemas.py index 5c27063..bf8080f 100644 --- a/polylan_submitter/submissions/schemas.py +++ b/polylan_submitter/submissions/schemas.py @@ -7,10 +7,11 @@ from ninja import Schema class SubmissionFileOut(Schema): id: UUID original_filename: str - file_size: int content_type: str + file_size: int file_url: Optional[str] = None ocr_status: str + ocr_raw_data: str class SubmissionOut(Schema):