330ba7a93d
- Backend: FastAPI, EXIF-parser, EasyOCR, SQLite - Frontend: React admin (startliste, passeringer, gjennomgang, resultater) - Docker: docker-compose med depot/processed/data-volumer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
120 lines
3.1 KiB
Python
120 lines
3.1 KiB
Python
"""
|
|
Skriv og query passeringslogg i SQLite.
|
|
"""
|
|
|
|
import uuid
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
import aiosqlite
|
|
|
|
|
|
async def log_passage(
|
|
db: aiosqlite.Connection,
|
|
*,
|
|
profile_id: Optional[str],
|
|
bib_number: Optional[str],
|
|
station: str,
|
|
timestamp_utc: datetime,
|
|
gps_lat: float,
|
|
gps_lon: float,
|
|
gps_alt: Optional[float],
|
|
confidence: float,
|
|
id_method: str,
|
|
source_image: str,
|
|
needs_review: bool = False,
|
|
review_note: Optional[str] = None,
|
|
) -> str:
|
|
"""Logg én passering. Returnerer passage_id."""
|
|
passage_id = str(uuid.uuid4())
|
|
await db.execute(
|
|
"""
|
|
INSERT INTO passages (
|
|
passage_id, profile_id, bib_number, station,
|
|
timestamp_utc, gps_lat, gps_lon, gps_alt,
|
|
confidence, id_method, source_image,
|
|
needs_review, review_note
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
""",
|
|
(
|
|
passage_id,
|
|
profile_id,
|
|
bib_number,
|
|
station,
|
|
timestamp_utc.isoformat(),
|
|
gps_lat,
|
|
gps_lon,
|
|
gps_alt,
|
|
confidence,
|
|
id_method,
|
|
source_image,
|
|
int(needs_review),
|
|
review_note,
|
|
),
|
|
)
|
|
await db.commit()
|
|
return passage_id
|
|
|
|
|
|
async def get_passages(
|
|
db: aiosqlite.Connection,
|
|
profile_id: Optional[str] = None,
|
|
station: Optional[str] = None,
|
|
needs_review: Optional[bool] = None,
|
|
) -> list[dict]:
|
|
"""Hent passeringer med valgfrie filtre."""
|
|
clauses = []
|
|
params = []
|
|
|
|
if profile_id is not None:
|
|
clauses.append("p.profile_id = ?")
|
|
params.append(profile_id)
|
|
if station is not None:
|
|
clauses.append("p.station = ?")
|
|
params.append(station)
|
|
if needs_review is not None:
|
|
clauses.append("p.needs_review = ?")
|
|
params.append(int(needs_review))
|
|
|
|
where = ("WHERE " + " AND ".join(clauses)) if clauses else ""
|
|
|
|
query = f"""
|
|
SELECT p.*, a.name, a.club
|
|
FROM passages p
|
|
LEFT JOIN athletes a ON a.profile_id = p.profile_id
|
|
{where}
|
|
ORDER BY p.timestamp_utc
|
|
"""
|
|
async with db.execute(query, params) as cur:
|
|
rows = await cur.fetchall()
|
|
return [dict(r) for r in rows]
|
|
|
|
|
|
async def resolve_passage(
|
|
db: aiosqlite.Connection,
|
|
passage_id: str,
|
|
profile_id: Optional[str],
|
|
bib_number: Optional[str],
|
|
review_note: Optional[str] = None,
|
|
) -> bool:
|
|
"""Manuell oppdatering av en passering etter gjennomgang."""
|
|
cur = await db.execute(
|
|
"""
|
|
UPDATE passages
|
|
SET profile_id = ?, bib_number = ?, needs_review = 0,
|
|
review_note = ?, id_method = 'manual'
|
|
WHERE passage_id = ?
|
|
""",
|
|
(profile_id, bib_number, review_note, passage_id),
|
|
)
|
|
await db.commit()
|
|
return cur.rowcount > 0
|
|
|
|
|
|
async def delete_passage(db: aiosqlite.Connection, passage_id: str) -> bool:
|
|
cur = await db.execute(
|
|
"DELETE FROM passages WHERE passage_id = ?", (passage_id,)
|
|
)
|
|
await db.commit()
|
|
return cur.rowcount > 0
|