Auto-create athlete in startlist for unrecognized bib numbers
Build & Deploy / build-and-deploy (push) Successful in 45s

When a bib number is detected (via OCR or manual entry during review)
but not found in the start list, it is now automatically added with
the placeholder name "Ukjent #<nr>" instead of being left without a
profile_id (which would exclude it from results).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 17:51:03 +01:00
parent 99565daafb
commit 018f84efd8
4 changed files with 18 additions and 12 deletions
+5 -1
View File
@@ -22,6 +22,7 @@ from profile_db import (
clear_startlist,
delete_athlete,
get_db,
get_or_create_athlete,
import_startlist_csv,
list_athletes,
)
@@ -213,8 +214,11 @@ class ResolveRequest(BaseModel):
@app.post("/api/passages/{passage_id}/resolve")
async def resolve(passage_id: str, body: ResolveRequest, db=Depends(get_connection)):
profile_id = body.profile_id
if body.bib_number and not profile_id:
profile_id = await get_or_create_athlete(db, body.bib_number)
ok = await resolve_passage(db, passage_id,
profile_id=body.profile_id,
profile_id=profile_id,
bib_number=body.bib_number,
review_note=body.review_note)
if not ok:
+4 -10
View File
@@ -23,7 +23,7 @@ from watchdog.observers import Observer
from exif_parser import ExifError, parse_image
from ocr import read_bib
from passage_log import log_passage
from profile_db import get_athlete_by_bib, init_db
from profile_db import get_or_create_athlete, init_db
logger = logging.getLogger(__name__)
@@ -98,11 +98,7 @@ async def process_image(path: Path) -> None:
await init_db(db)
if bib_number and not needs_review:
athlete = await get_athlete_by_bib(db, bib_number)
if athlete:
profile_id = athlete["profile_id"]
else:
logger.debug("Ukjent startnummer: %s", bib_number)
profile_id = await get_or_create_athlete(db, bib_number)
await log_passage(
db,
@@ -141,7 +137,7 @@ async def process_image_with_override(
EXIF-tid brukes hvis tilgjengelig, ellers nåværende tidspunkt.
"""
from datetime import datetime, timezone
from profile_db import get_athlete_by_bib
from profile_db import get_or_create_athlete as _get_or_create
logger.info("Web-opplasting: %s → stasjon=%s", path.name, station_name)
@@ -165,9 +161,7 @@ async def process_image_with_override(
profile_id = None
if ocr.digits and not needs_review:
athlete = await get_athlete_by_bib(db, ocr.digits)
if athlete:
profile_id = athlete["profile_id"]
profile_id = await _get_or_create(db, ocr.digits)
await log_passage(
db,
+8
View File
@@ -150,6 +150,14 @@ async def import_startlist_csv(db: aiosqlite.Connection, csv_content: str) -> di
return {"imported": imported, "errors": errors}
async def get_or_create_athlete(db: aiosqlite.Connection, bib_number: str) -> str:
"""Hent eller opprett utøver basert på startnummer. Returnerer profile_id."""
athlete = await get_athlete_by_bib(db, bib_number)
if athlete:
return athlete["profile_id"]
return await upsert_athlete(db, bib_number, f"Ukjent #{bib_number}")
async def get_athlete_by_bib(db: aiosqlite.Connection, bib: str) -> Optional[dict]:
async with db.execute(
"SELECT * FROM athletes WHERE bib_number = ?", (bib,)
+1 -1
View File
@@ -122,7 +122,7 @@ function ReviewCard({ passage, athletes, onResolved, onDeleted }) {
)}
{bib && !matchedAthlete && (
<span style={{ fontSize: '0.8rem', color: '#e67e22', marginTop: 2 }}>
Startnummer ikke i startliste
Ikke i startliste legges til automatisk
</span>
)}
</div>