96 lines
3.6 KiB
Python
96 lines
3.6 KiB
Python
import csv
|
|
import io
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
|
|
import review_export
|
|
|
|
|
|
def _make_db(rows):
|
|
db = MagicMock()
|
|
db.fetchall.return_value = rows
|
|
return db
|
|
|
|
|
|
class Story07ReviewExportTests(unittest.TestCase):
|
|
_ROWS = [
|
|
{
|
|
"video_id": 1, "file_path": "/data/input/a.mp4",
|
|
"confidence_score": 0.62, "routing_decision": "REVIEW",
|
|
"model_version": "v1.0", "ground_truth": True,
|
|
"annotated_at": None, "notes": "ok",
|
|
"confidence_scores": json.dumps([0.60, 0.62, 0.65]),
|
|
},
|
|
]
|
|
|
|
def test_fetch_annotated_passes_correct_where_clause(self):
|
|
db = _make_db(self._ROWS)
|
|
review_export.fetch_annotated(db, annotated_only=True, model_version="v1.0")
|
|
call_args = db.fetchall.call_args
|
|
sql = call_args[0][0]
|
|
self.assertIn("rq.annotated = TRUE", sql)
|
|
self.assertIn("v.model_version = %s", sql)
|
|
|
|
def test_fetch_annotated_normalises_confidence_scores(self):
|
|
db = _make_db(self._ROWS)
|
|
records = review_export.fetch_annotated(db)
|
|
self.assertIsInstance(records[0]["contributing_frames"], list)
|
|
self.assertEqual(records[0]["contributing_frames"], [0.60, 0.62, 0.65])
|
|
|
|
def test_fetch_annotated_handles_null_confidence_scores(self):
|
|
rows = [{**self._ROWS[0], "confidence_scores": None}]
|
|
db = _make_db(rows)
|
|
records = review_export.fetch_annotated(db)
|
|
self.assertEqual(records[0]["contributing_frames"], [])
|
|
|
|
def test_export_json_writes_valid_utf8_file(self):
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
records = review_export.fetch_annotated(_make_db(self._ROWS))
|
|
path = review_export.export_json(records, f"{tmpdir}/out.json")
|
|
loaded = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
self.assertEqual(len(loaded), 1)
|
|
self.assertEqual(loaded[0]["video_id"], 1)
|
|
self.assertTrue(loaded[0]["ground_truth"])
|
|
|
|
def test_export_csv_writes_valid_csv(self):
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
records = review_export.fetch_annotated(_make_db(self._ROWS))
|
|
path = review_export.export_csv(records, f"{tmpdir}/out.csv")
|
|
content = Path(path).read_text(encoding="utf-8")
|
|
reader = csv.DictReader(io.StringIO(content))
|
|
rows = list(reader)
|
|
self.assertEqual(len(rows), 1)
|
|
self.assertEqual(rows[0]["routing_decision"], "REVIEW")
|
|
# contributing_frames should be a JSON string in CSV
|
|
frames = json.loads(rows[0]["contributing_frames"])
|
|
self.assertEqual(frames, [0.60, 0.62, 0.65])
|
|
|
|
def test_export_csv_empty_returns_empty_file(self):
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = review_export.export_csv([], f"{tmpdir}/empty.csv")
|
|
self.assertEqual(Path(path).read_text(), "")
|
|
|
|
def test_ground_truth_filter_appears_in_query(self):
|
|
db = _make_db([])
|
|
review_export.fetch_annotated(db, ground_truth=False)
|
|
sql = db.fetchall.call_args[0][0]
|
|
self.assertIn("rq.ground_truth = %s", sql)
|
|
|
|
|
|
class Story07AppSyntaxTest(unittest.TestCase):
|
|
def test_app_module_compiles(self):
|
|
"""Ensure ui/app.py has no syntax errors."""
|
|
app_path = Path(__file__).resolve().parents[1] / "ui" / "app.py"
|
|
source = app_path.read_text(encoding="utf-8")
|
|
compile(source, str(app_path), "exec")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|