"""GridScanDecision wire contract: wraps the analysis result verbatim.""" import json import numpy as np from aarecommon.math.jfjoch_gridscan_union import Thresholds, analyse from aarecommon.models.gridscan_decision import GridScanDecision from aarecommon.models.raster_grid import RasterPayloadModel def _synthetic_scan(ny_n=20, nx_n=20): """A gaussian protein blob on a flat loop, as plain dicts (jfjoch-free).""" rng = np.random.default_rng(0) yy, xx = np.mgrid[0:ny_n, 0:nx_n] blob = 80 * np.exp(-(((xx - 7) ** 2 + (yy - 12) ** 2) / 8.0)) images = [ { "number": int(y * nx_n + x), "nx": int(x), "ny": int(y), "spots": int(blob[y, x] + 10 + rng.integers(0, 3)), "spots_low_res": int(4 + rng.integers(0, 2)), "spots_ice": 0, "bkg": float(30 + 40 * np.exp(-(((x - 9) ** 2 + (y - 10) ** 2) / 90.0))), "res": 2.0, } for y in range(ny_n) for x in range(nx_n) ] return {"file_prefix": "decision-test", "images": images} def test_decision_round_trips_without_pictures(request): th = Thresholds.model_validate({}) r = analyse(_synthetic_scan(), {"step_x_um": 10.0, "step_y_um": 10.0}, thresholds=th) assert r.found assert r.contour_cells, "the chosen 50 % blob must ship its cells" assert all(0 <= x < r.n_fast and 0 <= y < r.n_slow for x, y in r.contour_cells) # the centre lies within the contour's bounding box assert r.centre is not None xs = [x for x, _ in r.contour_cells] ys = [y for _, y in r.contour_cells] assert min(xs) - 1 <= r.centre.nx <= max(xs) + 1 assert min(ys) - 1 <= r.centre.ny <= max(ys) + 1 d = GridScanDecision( algorithm="jfjoch_gridscan_union", algorithm_version="test", thresholds=th.model_dump(), result=r, ) wire = json.loads(d.model_dump_json()) assert wire["contract"] == 1 assert "jpeg" not in wire["result"], "pictures never ride the JSON" assert wire["result"]["centre"]["image_number"] == r.centre.image_number assert wire["thresholds"]["object_union_spots"] is True back = GridScanDecision.model_validate(wire) assert back.result.centre is not None assert back.result.contour_cells is not None assert back.result.centre.nx == r.centre.nx assert [tuple(c) for c in back.result.contour_cells] == r.contour_cells def test_payload_decision_is_optional(): fields = RasterPayloadModel.model_fields assert "decision" in fields assert fields["decision"].default is None