Skip to content

Commit 3bb128b

Browse files
committed
Add Bloom v0 order specimen references
1 parent 7296ee7 commit 3bb128b

6 files changed

Lines changed: 151 additions & 0 deletions

File tree

bloom_lims/api/v1/external_specimens.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ async def find_external_specimens_by_reference(
127127
trf_euid: str | None = Query(None),
128128
patient_id: str | None = Query(None),
129129
order_euid: str | None = Query(None),
130+
order_test_euid: str | None = Query(None),
130131
shipment_euid: str | None = Query(None),
131132
kit_barcode: str | None = Query(None),
132133
atlas_tenant_id: str | None = Query(None),
@@ -138,6 +139,7 @@ async def find_external_specimens_by_reference(
138139
trf_euid=trf_euid,
139140
patient_id=patient_id,
140141
order_euid=order_euid,
142+
order_test_euid=order_test_euid,
141143
shipment_euid=shipment_euid,
142144
kit_barcode=kit_barcode,
143145
atlas_tenant_id=atlas_tenant_id,

bloom_lims/domain/beta_lab_refs.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,9 +330,28 @@ def _replace_container_entity_references(
330330
atlas_context: dict[str, Any],
331331
) -> None:
332332
atlas_tenant_id = str(atlas_context.get("atlas_tenant_id") or "").strip()
333+
atlas_order_euid = str(
334+
atlas_context.get("atlas_order_euid") or atlas_context.get("order_euid") or ""
335+
).strip()
336+
atlas_order_test_euid = str(
337+
atlas_context.get("atlas_order_test_euid")
338+
or atlas_context.get("order_test_euid")
339+
or ""
340+
).strip()
333341
atlas_trf_euid = str(atlas_context.get("atlas_trf_euid") or "").strip()
334342
atlas_test_euid = str(atlas_context.get("atlas_test_euid") or "").strip()
335343
atlas_test_euids: list[str] = []
344+
atlas_order_test_euids: list[str] = []
345+
seen_order_tests: set[str] = set()
346+
if atlas_order_test_euid:
347+
seen_order_tests.add(atlas_order_test_euid)
348+
atlas_order_test_euids.append(atlas_order_test_euid)
349+
for value in list(atlas_context.get("atlas_order_test_euids") or []):
350+
clean_value = str(value or "").strip()
351+
if not clean_value or clean_value in seen_order_tests:
352+
continue
353+
seen_order_tests.add(clean_value)
354+
atlas_order_test_euids.append(clean_value)
336355
seen_tests: set[str] = set()
337356
if atlas_test_euid:
338357
seen_tests.add(atlas_test_euid)
@@ -345,6 +364,14 @@ def _replace_container_entity_references(
345364
atlas_test_euids.append(clean_value)
346365
fulfillment_items = list(atlas_context.get("fulfillment_items") or [])
347366
for fulfillment_item in fulfillment_items:
367+
candidate_order_test = str(
368+
fulfillment_item.get("atlas_order_test_euid")
369+
or fulfillment_item.get("order_test_euid")
370+
or ""
371+
).strip()
372+
if candidate_order_test and candidate_order_test not in seen_order_tests:
373+
seen_order_tests.add(candidate_order_test)
374+
atlas_order_test_euids.append(candidate_order_test)
348375
candidate = str(fulfillment_item.get("atlas_test_euid") or "").strip()
349376
if not candidate or candidate in seen_tests:
350377
continue
@@ -359,6 +386,54 @@ def _replace_container_entity_references(
359386
(self.ORGANIZATION_SITE_REFERENCE_TYPE, "atlas_organization_site_euid"),
360387
)
361388
created_payloads: list[dict[str, Any]] = []
389+
self._delete_reference_type(instance, reference_type="order_test_euid")
390+
self._delete_reference_type(instance, reference_type="order_euid")
391+
if atlas_tenant_id and atlas_order_euid:
392+
properties = {
393+
"provider": "atlas",
394+
"reference_type": "order_euid",
395+
"reference_value": atlas_order_euid,
396+
"foreign_reference": atlas_order_euid,
397+
"atlas_tenant_id": atlas_tenant_id,
398+
"order_euid": atlas_order_euid,
399+
"atlas_order_euid": atlas_order_euid,
400+
"validation": {},
401+
}
402+
created_payloads.append(properties)
403+
ref_obj = self.bobj.create_instance_by_code(
404+
self.EXTERNAL_REFERENCE_TEMPLATE_CODE,
405+
{"json_addl": {"properties": properties}},
406+
)
407+
self.bobj.create_generic_instance_lineage_by_euids(
408+
instance.euid,
409+
ref_obj.euid,
410+
relationship_type=self.EXTERNAL_REFERENCE_RELATIONSHIP,
411+
)
412+
if atlas_tenant_id:
413+
for reference_value in atlas_order_test_euids:
414+
properties = {
415+
"provider": "atlas",
416+
"reference_type": "order_test_euid",
417+
"reference_value": reference_value,
418+
"foreign_reference": reference_value,
419+
"atlas_tenant_id": atlas_tenant_id,
420+
"order_test_euid": reference_value,
421+
"atlas_order_test_euid": reference_value,
422+
"validation": {},
423+
}
424+
if atlas_order_euid:
425+
properties["order_euid"] = atlas_order_euid
426+
properties["atlas_order_euid"] = atlas_order_euid
427+
created_payloads.append(properties)
428+
ref_obj = self.bobj.create_instance_by_code(
429+
self.EXTERNAL_REFERENCE_TEMPLATE_CODE,
430+
{"json_addl": {"properties": properties}},
431+
)
432+
self.bobj.create_generic_instance_lineage_by_euids(
433+
instance.euid,
434+
ref_obj.euid,
435+
relationship_type=self.EXTERNAL_REFERENCE_RELATIONSHIP,
436+
)
362437
self._delete_reference_type(instance, reference_type=self.TEST_REFERENCE_TYPE)
363438
if atlas_tenant_id:
364439
for reference_value in atlas_test_euids:

bloom_lims/domain/external_specimens.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class ExternalSpecimenService:
3434
"trf_euid",
3535
"patient_id",
3636
"order_euid",
37+
"order_test_euid",
3738
"shipment_euid",
3839
"kit_barcode",
3940
"atlas_tenant_id",
@@ -53,6 +54,10 @@ class ExternalSpecimenService:
5354
"reference_types": ("order_euid",),
5455
"value_field": "reference_value",
5556
},
57+
"order_test_euid": {
58+
"reference_types": ("order_test_euid",),
59+
"value_field": "reference_value",
60+
},
5661
"shipment_euid": {
5762
"reference_types": ("shipment_euid",),
5863
"value_field": "reference_value",
@@ -77,6 +82,7 @@ class ExternalSpecimenService:
7782
_REFERENCE_RESPONSE_NORMALIZATION: dict[str, tuple[str, str]] = {
7883
"atlas_trf": ("atlas_trf_euid", "atlas_trf_euid"),
7984
"atlas_test": ("atlas_test_euid", "atlas_test_euid"),
85+
"order_test_euid": ("order_test_euid", "reference_value"),
8086
"atlas_patient": ("atlas_patient_euid", "atlas_patient_euid"),
8187
"atlas_testkit": ("atlas_testkit_euid", "atlas_testkit_euid"),
8288
"atlas_shipment": ("atlas_shipment_euid", "atlas_shipment_euid"),
@@ -351,6 +357,7 @@ def _validate_atlas_refs(
351357
"trf_euid": refs.trf_euid,
352358
"patient_id": refs.patient_id,
353359
"order_euid": refs.order_euid,
360+
"order_test_euid": refs.order_test_euid,
354361
"shipment_euid": refs.shipment_euid,
355362
"kit_barcode": refs.kit_barcode,
356363
"atlas_tenant_id": refs.atlas_tenant_id,

bloom_lims/schemas/external_specimens.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class AtlasReferences(BaseModel):
1111
trf_euid: str | None = None
1212
patient_id: str | None = None
1313
order_euid: str | None = None
14+
order_test_euid: str | None = None
1415
shipment_euid: str | None = None
1516
kit_barcode: str | None = None
1617
atlas_tenant_id: str | None = None
@@ -33,6 +34,7 @@ def validate_references(self) -> "ExternalSpecimenCreateRequest":
3334
if not any(
3435
[
3536
refs.order_euid,
37+
refs.order_test_euid,
3638
refs.patient_id,
3739
refs.shipment_euid,
3840
refs.kit_barcode,

tests/test_api_auth_rbac.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,43 @@ def _unexpected_atlas_service():
121121
assert service._atlas is None
122122

123123

124+
def test_external_specimen_lookup_uses_order_test_reference_filter(monkeypatch):
125+
def _unexpected_atlas_service():
126+
raise AssertionError(
127+
"AtlasService should not be constructed for lookup-only queries"
128+
)
129+
130+
monkeypatch.setattr(
131+
"bloom_lims.domain.external_specimens.AtlasService",
132+
_unexpected_atlas_service,
133+
)
134+
135+
calls: list[tuple[str, str]] = []
136+
137+
def _find_parent_uids_for_reference(*, reference_type: str, reference_value: str):
138+
calls.append((reference_type, reference_value))
139+
if reference_type == "order_euid":
140+
return {123}
141+
if reference_type == "order_test_euid":
142+
return set()
143+
raise AssertionError(f"unexpected reference filter: {reference_type}")
144+
145+
service = object.__new__(ExternalSpecimenService)
146+
service._atlas = None
147+
service.bdb = SimpleNamespace()
148+
service.bobj = SimpleNamespace()
149+
service._find_parent_uids_for_reference = _find_parent_uids_for_reference
150+
151+
result = ExternalSpecimenService.find_by_references(
152+
service,
153+
AtlasReferences(order_euid="ORDER-1", order_test_euid="ORDERTEST-1"),
154+
)
155+
156+
assert result == []
157+
assert calls == [("order_euid", "ORDER-1"), ("order_test_euid", "ORDERTEST-1")]
158+
assert service._atlas is None
159+
160+
124161
def test_user_tokens_endpoints_create_list_usage_revoke(client):
125162
token_id = _create_token(client)
126163

tests/test_queue_flow.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,13 @@ def _assert_no_uuid_keys(payload):
6767

6868

6969
def _atlas_context_payload() -> dict[str, object]:
70+
order_euid = _opaque("order")
71+
primary_order_test_euid = _opaque("order-test-primary")
7072
return {
7173
"atlas_tenant_id": _opaque("tenant"),
74+
"atlas_order_euid": order_euid,
75+
"atlas_order_test_euid": primary_order_test_euid,
76+
"atlas_order_test_euids": [primary_order_test_euid, _opaque("order-test-secondary")],
7277
"atlas_trf_euid": _opaque("trf"),
7378
"atlas_test_euid": _opaque("test-primary"),
7479
"atlas_test_euids": [_opaque("test-secondary")],
@@ -746,3 +751,26 @@ def test_external_specimen_lookup_finds_queue_ready_material_by_container_refs()
746751
and item["container_euid"] == material["container_euid"]
747752
for item in payload["items"]
748753
)
754+
755+
756+
def test_external_specimen_lookup_finds_queue_ready_material_by_order_refs():
757+
app.dependency_overrides[require_external_token_auth] = _external_rw_user
758+
759+
with TestClient(app) as client:
760+
material, atlas_context = _create_material_and_queue(client)
761+
lookup = client.get(
762+
"/api/v1/external/specimens/by-reference",
763+
params={
764+
"atlas_tenant_id": atlas_context["atlas_tenant_id"],
765+
"order_euid": atlas_context["atlas_order_euid"],
766+
"order_test_euid": atlas_context["atlas_order_test_euid"],
767+
},
768+
)
769+
assert lookup.status_code == 200, lookup.text
770+
payload = lookup.json()
771+
assert payload["total"] >= 1
772+
assert any(
773+
item["specimen_euid"] == material["specimen_euid"]
774+
and item["container_euid"] == material["container_euid"]
775+
for item in payload["items"]
776+
)

0 commit comments

Comments
 (0)