Picker: identify the analyst (initials) per pick

Each annotation row now carries an `analyst` column. On first visit the
web picker shows a small login modal asking for initials, persists them
in localStorage, and shows the badge in the top-right. Click the badge
to change identities. Submissions without initials are rejected by the
backend (HTTP 400). Skip remains analyst-free.

Backfill: every existing barrier_opening.csv row marked as `GG` since
all current picks were done by Giorgio.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-01 14:23:57 +01:00
parent 12568b82cc
commit 2623df4172
3 changed files with 145 additions and 14 deletions

View file

@ -54,7 +54,7 @@ DB_NAME_RE = re.compile(
)
OUT_COLS = ["machine_name", "session_date", "session_time",
"opening_s", "trim_first_s", "bad_rois", "notes"]
"opening_s", "trim_first_s", "bad_rois", "analyst", "notes"]
# ROI numbering in the HD mating arena (verified via tracking_geometry):
# upper row = ROIs 1, 3, 5 (y ≈ 0.125)
@ -275,6 +275,7 @@ class Submission(BaseModel):
idx: int
time_s: float | None # None when marking unusable
mode: str # "all" | "upper" | "lower" | "unusable" | "skip"
analyst: str = "" # initials of the human picker (required, non-skip)
notes: str = ""
@ -288,6 +289,10 @@ async def submit(payload: Submission) -> dict:
if payload.mode == "skip":
return {"status": "skipped"}
analyst = payload.analyst.strip().upper()
if not analyst:
raise HTTPException(status_code=400, detail="analyst initials required")
if payload.mode == "unusable":
row = {
"machine_name": item.machine_name,
@ -296,6 +301,7 @@ async def submit(payload: Submission) -> dict:
"opening_s": float("nan"),
"trim_first_s": 0,
"bad_rois": "",
"analyst": analyst,
"notes": payload.notes or "unusable",
}
else:
@ -315,6 +321,7 @@ async def submit(payload: Submission) -> dict:
"opening_s": round(payload.time_s, 1),
"trim_first_s": 0,
"bad_rois": bad_rois,
"analyst": analyst,
"notes": payload.notes,
}