From ff27053aa9a6d85980c88f5e2aefd84cce083c79 Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Tue, 3 Mar 2026 15:14:04 -0800 Subject: [PATCH] feat(avocet): POST /api/label endpoint --- app/api.py | 25 ++++++++++++++++++++++++- tests/test_api.py | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/app/api.py b/app/api.py index 5e9f97d..077da14 100644 --- a/app/api.py +++ b/app/api.py @@ -8,7 +8,10 @@ from __future__ import annotations import json from pathlib import Path -from fastapi import FastAPI, Query +from datetime import datetime, timezone + +from fastapi import FastAPI, HTTPException, Query +from pydantic import BaseModel _ROOT = Path(__file__).parent.parent _DATA_DIR: Path = _ROOT / "data" # overridable in tests via set_data_dir() @@ -67,3 +70,23 @@ _last_action: dict | None = None def get_queue(limit: int = Query(default=10, ge=1, le=50)): items = _read_jsonl(_queue_file()) return {"items": items[:limit], "total": len(items)} + + +class LabelRequest(BaseModel): + id: str + label: str + + +@app.post("/api/label") +def post_label(req: LabelRequest): + global _last_action + items = _read_jsonl(_queue_file()) + match = next((x for x in items if x["id"] == req.id), None) + if not match: + raise HTTPException(404, f"Item {req.id!r} not found in queue") + record = {**match, "label": req.label, + "labeled_at": datetime.now(timezone.utc).isoformat()} + _append_jsonl(_score_file(), record) + _write_jsonl(_queue_file(), [x for x in items if x["id"] != req.id]) + _last_action = {"type": "label", "item": record} + return {"ok": True} diff --git a/tests/test_api.py b/tests/test_api.py index ac6ecb1..ad6275a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -52,3 +52,24 @@ def test_queue_empty_when_no_file(client): r = client.get("/api/queue") assert r.status_code == 200 assert r.json() == {"items": [], "total": 0} + + +def test_label_appends_to_score(client, queue_with_items): + from app import api as api_module + r = client.post("/api/label", json={"id": "id0", "label": "interview_scheduled"}) + assert r.status_code == 200 + records = api_module._read_jsonl(api_module._score_file()) + assert len(records) == 1 + assert records[0]["id"] == "id0" + assert records[0]["label"] == "interview_scheduled" + assert "labeled_at" in records[0] + +def test_label_removes_from_queue(client, queue_with_items): + from app import api as api_module + client.post("/api/label", json={"id": "id0", "label": "rejected"}) + queue = api_module._read_jsonl(api_module._queue_file()) + assert not any(x["id"] == "id0" for x in queue) + +def test_label_unknown_id_returns_404(client, queue_with_items): + r = client.post("/api/label", json={"id": "unknown", "label": "neutral"}) + assert r.status_code == 404