补齐 Autocal 实时反馈与稳定窗口复核
显示实时力值、曲线及 UV 图层,并保存人工确认窗口和零载检查基准。定向测试、真实视频预览与已录会话复核闭环验证通过。
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user