补齐 Autocal 实时反馈与稳定窗口复核

显示实时力值、曲线及 UV 图层,并保存人工确认窗口和零载检查基准。定向测试、真实视频预览与已录会话复核闭环验证通过。
This commit is contained in:
ykcheungab
2026-10-11 18:08:09 +08:00
parent 04adf17f60
commit 34a1d9b1f1
12 changed files with 731 additions and 39 deletions
+88
View File
@@ -122,3 +122,91 @@ def test_review_dialog_shows_saved_windows_charts_and_new_export(qt_app, session
qt_app.processEvents()
assert dialog.grab().save(str(tmp_path / "review.png"))
dialog.close()
def test_reviewed_windows_roundtrip_and_reject_changed_inputs(session):
from orisys_autocal.session_review import load_review
selections = {f"trial-001:{index}": {
"t_start_host_mono_ns": (index * 20 + 1) * 1_000_000_000,
"t_end_host_mono_ns": (index * 20 + 3) * 1_000_000_000,
"stability_verified": True,
} for index in (0, 1, 2)}
before = (session / "trials/trial-001/reference.csv").read_bytes()
result = review_session(session, selections=selections)
first = result["sites"][0]
assert first["confirmed_fit"]["gain_n_per_raw"] == pytest.approx(.5)
assert all(w["duration_s"] == 2 for w in first["windows"][:3])
saved = save_review(result)
assert load_review(session, saved)["sites"] == result["sites"]
document = json.loads(saved.read_text(encoding="utf-8"))
assert document["baselines"][0]["status"] == "unavailable"
assert document["baselines"][0]["tracker_reference"] is False
assert document["analysis_versions"]["numpy"]
assert (session / "trials/trial-001/reference.csv").read_bytes() == before
with (session / "trials/trial-001/forces.csv").open("a", encoding="utf-8") as handle:
handle.write("\n")
with pytest.raises(ValueError, match="match"):
load_review(session, saved)
with pytest.raises(ValueError, match="changed"):
save_review(result)
def test_invalid_review_selection_does_not_become_verified(session):
for start, end in ((0, 2), (1, 1), (1, 12), (3, 4)):
with pytest.raises(ValueError):
review_session(session, selections={"trial-001:0": {
"t_start_host_mono_ns": start * 1_000_000_000,
"t_end_host_mono_ns": end * 1_000_000_000,
"stability_verified": True,
}})
def test_operator_confirms_windows_in_dialog_and_reloads_them(qt_app, session, monkeypatch):
from orisys_autocal.ui import session_review_dialog
dialog = session_review_dialog.SessionReviewDialog(session)
for row in (0, 1):
dialog.table.selectRow(row)
dialog.end_s.setValue(2)
dialog.verified.setChecked(True)
dialog.apply_button.click()
assert dialog.review["sites"][0]["windows"][row]["stability_verified"]
assert dialog.review["sites"][0]["confirmed_fit"] is not None
dialog.save_button.click()
saved = next(session.glob("derived/*/review.json"))
fresh = session_review_dialog.SessionReviewDialog(session)
monkeypatch.setattr(session_review_dialog.QFileDialog, "getOpenFileName",
lambda *args: (str(saved), ""))
fresh.load_button.click()
assert fresh.review["sites"] == dialog.review["sites"]
dialog.close()
fresh.close()
def test_baseline_image_matches_first_reviewed_zero_frame(session):
import cv2
import numpy as np
path = session / "trials/trial-001"
frames = list(csv.DictReader((path / "frames.csv").open(encoding="utf-8")))
writer = cv2.VideoWriter(str(path / "raw_0001.mp4"), cv2.VideoWriter_fourcc(*"mp4v"),
30, (32, 24))
assert writer.isOpened()
try:
for index, row in enumerate(frames):
writer.write(np.full((24, 32, 3), 20 if index == 0 else 220, np.uint8))
row.update(segment=1, segment_frame=index + 1, in_video=1)
finally:
writer.release()
_csv(path / "frames.csv", frames)
result = review_session(session, selections={"trial-001:0": {
"t_start_host_mono_ns": 1_000_000_000,
"t_end_host_mono_ns": 3_000_000_000, "stability_verified": True,
}})
saved = save_review(result)
document = json.loads(saved.read_text(encoding="utf-8"))
baseline = document["baselines"][0]
assert baseline["status"] == "saved" and baseline["segment_frame"] == 1
image = cv2.imread(str(saved.parent / baseline["image"]))
assert 10 < image.mean() < 30