ruff + submissions validation

This commit is contained in:
2025-10-30 14:43:19 +01:00
parent 15de496501
commit 0e1e77c2dd
36 changed files with 1025 additions and 392 deletions
@@ -146,6 +146,33 @@
</div>
</div>
<!-- Manual Puzzle Selection (when OCR confidence is low) -->
<div v-if="file.needsManualPuzzleSelection" class="mt-2">
<div class="alert alert-warning alert-sm">
<i class="mdi mdi-alert-circle text-lg"></i>
<div class="flex-1">
<div class="font-medium">Low OCR Confidence</div>
<div class="text-xs">Please select the correct puzzle manually</div>
</div>
</div>
<div class="mt-2">
<select
v-model="file.manualPuzzleSelection"
class="select select-bordered select-sm w-full"
@change="onManualPuzzleSelection(file)"
>
<option value="">Select puzzle...</option>
<option
v-for="puzzle in puzzlesStore.puzzles"
:key="puzzle.id"
:value="puzzle.title"
>
{{ puzzle.title }}
</option>
</select>
</div>
</div>
<!-- Manual OCR trigger for non-auto detected files -->
<div v-else-if="!file.ocrProcessing && !file.ocrError && !file.ocrData" class="mt-1">
<button
@@ -335,6 +362,15 @@ const processOCR = async (submissionFile: SubmissionFile) => {
// Force reactivity update
await nextTick()
files.value[fileIndex].ocrData = ocrData
// Check if puzzle confidence is below 80% and needs manual selection
if (ocrData.confidence.puzzle < 0.8) {
files.value[fileIndex].needsManualPuzzleSelection = true
console.log(`Low puzzle confidence (${Math.round(ocrData.confidence.puzzle * 100)}%) for ${submissionFile.file.name}, requiring manual selection`)
} else {
files.value[fileIndex].needsManualPuzzleSelection = false
}
await nextTick()
} catch (error) {
console.error('OCR processing failed:', error)
@@ -353,4 +389,16 @@ const getConfidenceBadgeClass = (confidence: number): string => {
if (confidence >= 0.6) return 'badge-warning'
return 'badge-error'
}
const onManualPuzzleSelection = (submissionFile: SubmissionFile) => {
// Find the file in the reactive array
const fileIndex = files.value.findIndex(f => f.file === submissionFile.file)
if (fileIndex === -1) return
// Clear the manual selection requirement once user has selected
if (files.value[fileIndex].manualPuzzleSelection) {
files.value[fileIndex].needsManualPuzzleSelection = false
console.log(`Manual puzzle selection: ${submissionFile.file.name} -> ${files.value[fileIndex].manualPuzzleSelection}`)
}
}
</script>
@@ -23,6 +23,18 @@
<!-- File Upload -->
<FileUpload v-model="submissionFiles" :puzzles="puzzles" />
<!-- Manual Selection Warning -->
<div v-if="filesNeedingManualSelection.length > 0" class="alert alert-warning">
<i class="mdi mdi-alert-circle text-xl"></i>
<div class="flex-1">
<div class="font-bold">Manual Puzzle Selection Required</div>
<div class="text-sm">
{{ filesNeedingManualSelection.length }} file(s) have low OCR confidence for puzzle names.
Please select the correct puzzle for each file before submitting.
</div>
</div>
</div>
<!-- Notes -->
<div class="form-control">
@@ -64,10 +76,14 @@
<button
type="submit"
class="btn btn-primary"
:disabled="isSubmitting"
:disabled="!canSubmit"
>
<span v-if="isSubmitting" class="loading loading-spinner loading-sm"></span>
{{ isSubmitting ? 'Submitting...' : 'Submit Solution' }}
<span v-if="isSubmitting">Submitting...</span>
<span v-else-if="filesNeedingManualSelection.length > 0">
Select Puzzles ({{ filesNeedingManualSelection.length }} remaining)
</span>
<span v-else>Submit Solution</span>
</button>
</div>
</form>
@@ -100,8 +116,12 @@ const isSubmitting = ref(false)
const notesLength = computed(() => notes.value.length)
const canSubmit = computed(() => {
return submissionFiles.value.length > 0 &&
!isSubmitting.value
const hasFiles = submissionFiles.value.length > 0
const noManualSelectionNeeded = !submissionFiles.value.some(file => file.needsManualPuzzleSelection)
return hasFiles &&
!isSubmitting.value &&
noManualSelectionNeeded
})
// Group files by detected puzzle
@@ -109,8 +129,10 @@ const responsesByPuzzle = computed(() => {
const grouped: Record<string, { puzzle: SteamCollectionItem | null, files: SubmissionFile[] }> = {}
submissionFiles.value.forEach(file => {
if (file.ocrData?.puzzle) {
const puzzleName = file.ocrData.puzzle
// Use manual puzzle selection if available, otherwise fall back to OCR
const puzzleName = file.manualPuzzleSelection || file.ocrData?.puzzle
if (puzzleName) {
if (!grouped[puzzleName]) {
grouped[puzzleName] = {
puzzle: props.findPuzzleByName(puzzleName),
@@ -124,6 +146,11 @@ const responsesByPuzzle = computed(() => {
return grouped
})
// Count files that need manual puzzle selection
const filesNeedingManualSelection = computed(() => {
return submissionFiles.value.filter(file => file.needsManualPuzzleSelection)
})
// Check if any OCR confidence is below 50%
const hasLowConfidence = computed(() => {
return submissionFiles.value.some(file => {
+4 -2
View File
@@ -238,8 +238,10 @@ export const submissionHelpers = {
}> = {}
files.forEach(file => {
if (file.ocrData?.puzzle) {
const puzzleName = file.ocrData.puzzle
// Use manual puzzle selection if available, otherwise fall back to OCR
const puzzleName = file.manualPuzzleSelection || file.ocrData?.puzzle
if (puzzleName) {
if (!responsesByPuzzle[puzzleName]) {
responsesByPuzzle[puzzleName] = {
puzzle: puzzleHelpers.findPuzzleByName(puzzles, puzzleName),
+2
View File
@@ -46,6 +46,8 @@ export interface SubmissionFile {
ocrProcessing?: boolean
ocrError?: string
original_filename?: string
manualPuzzleSelection?: string
needsManualPuzzleSelection?: boolean
}
export interface PuzzleResponse {