add re-validation in python

This commit is contained in:
2025-10-31 01:56:26 +01:00
parent f98145d6db
commit e0ada1e26d
5 changed files with 310 additions and 10 deletions
+4 -7
View File
@@ -3,7 +3,6 @@ from ninja.files import UploadedFile
from ninja.pagination import paginate
from django.db import transaction
from django.core.files.base import ContentFile
from django.http import Http404
from django.utils import timezone
from django.shortcuts import get_object_or_404
from typing import List
@@ -97,12 +96,9 @@ def create_submission(
file_index = 0
for response_data in data.responses:
# Get the puzzle
try:
puzzle = SteamCollectionItem.objects.get(id=response_data.puzzle_id)
except SteamCollectionItem.DoesNotExist:
return 400, {
"detail": f"Puzzle with id {response_data.puzzle_id} not found"
}
puzzle = get_object_or_404(
SteamCollectionItem, id=response_data.puzzle_id
)
# Create the puzzle response
response = PuzzleResponse.objects.create(
@@ -121,6 +117,7 @@ def create_submission(
# Process files for this response
# For simplicity, we'll take one file per response
# In a real implementation, you'd need better file-to-response mapping
print("FI", file_index, files)
if file_index < len(files):
uploaded_file = files[file_index]
@@ -0,0 +1,20 @@
from django.core.management.base import BaseCommand
from submissions.utils import verify_and_validate_ocr_date_for_submission
from submissions.models import SubmissionFile
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument("--force", action="store_true", help="Force redo the OCR")
def handle(self, *args, **options):
for file in SubmissionFile.objects.prefetch_related(
"response__puzzle",
"response__submission__user",
):
if not file.response.needs_manual_validation and not options.get(
"force", False
):
continue
verify_and_validate_ocr_date_for_submission(file)
+90 -1
View File
@@ -4,12 +4,16 @@ Utilities for fetching Steam Workshop collection data using Steam Web API
import re
import requests
from submissions.models import SteamCollection
from submissions.models import SteamCollection, SubmissionFile
from datetime import datetime
from django.utils import timezone
from django.conf import settings
from typing import Dict, List, Optional, Tuple
import logging
from PIL import Image
import cv2
import pytesseract
import os
logger = logging.getLogger(__name__)
@@ -425,3 +429,88 @@ def create_or_update_collection(url: str) -> Tuple[SteamCollection, bool]:
)
return collection, created
def verify_ocr_data_for_file(file: str) -> tuple[str, int, int, int]:
# Convert GIF to JPG
with Image.open(file) as img:
# width, height = img.size
img.seek(0)
rgb_img = img.convert("RGB")
rgb_img.save("temp.jpg", "JPEG")
# Read image from which text needs to be extracted
img = cv2.imread("temp.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Optional: resize for better OCR
gray = cv2.resize(gray, None, fx=1, fy=1, interpolation=cv2.INTER_CUBIC)
# Manually crop regions based on known layout (x, y, w, h)
regions = [
(15, 600, 330, 28), # PUZZLE NAME
(412, 603, 65, 22), # COST
(577, 603, 65, 22), # CYCLES
(739, 603, 65, 22), # AREA
]
output = img.copy()
def find_text(dims, gray, output, content):
x, y, w, h = dims
roi = gray[y : y + h, x : x + w]
roi = cv2.bitwise_not(roi)
if content == "digits" or content == "digits_with_6":
config = "--oem 3 --psm 7 -c tessedit_char_whitelist=0123456789"
else:
config = "--oem 3 --psm 7"
text = pytesseract.image_to_string(roi, config=config).strip()
# Remove the extra 6 (actually the G for Gold) for cost value
if content == "digits_with_6":
text = text[:-1]
cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 2)
return text
puzzle = find_text(regions[0], gray, output, "letters")
cost = find_text(regions[1], gray, output, "digits_with_6")
cycles = find_text(regions[2], gray, output, "digits")
area = find_text(regions[3], gray, output, "digits")
# Save image with green rectangles around the considered zones, for debug purposes
# cv2.imwrite("output_debug.jpg", output)
os.remove("temp.jpg")
return puzzle, cost, cycles, area
def verify_and_validate_ocr_date_for_submission(file: SubmissionFile):
ocr_data = verify_ocr_data_for_file(file.file.path)
r = file.response
print(
f"{r.submission.user}: ({r.cost: >4} {r.cycles: >4} {r.area: >4}) -> ({ocr_data[1]: >4} {ocr_data[2]: >4} {ocr_data[3]: >4})"
)
valid_count = 0
if r.cost == ocr_data[1]:
r.validated_cost = r.cost
valid_count += 1
if r.cycles == ocr_data[2]:
r.validated_cycles = r.cycles
valid_count += 1
if r.area == ocr_data[3]:
r.validated_area = r.area
valid_count += 1
if valid_count == 3:
r.needs_manual_validation = False
if valid_count:
r.save()