Skip to content

Commit 3e6ef26

Browse files
committed
allow embedded access of values
1 parent 823e961 commit 3e6ef26

File tree

2 files changed

+72
-9
lines changed

2 files changed

+72
-9
lines changed

store/backend/neurostore/resources/data.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,30 @@ def _extract_analysis_id(note):
599599
return analysis.get("id")
600600
return analysis
601601

602+
@staticmethod
603+
def _collect_column_values(data, column):
604+
collected = []
605+
606+
def _walk(obj):
607+
if isinstance(obj, dict):
608+
if column in obj and not isinstance(obj[column], (dict, list)):
609+
collected.append(obj[column])
610+
for value in obj.values():
611+
_walk(value)
612+
elif isinstance(obj, list):
613+
dict_items = [item for item in obj if isinstance(item, dict)]
614+
if dict_items and all(column in item for item in dict_items):
615+
for item in dict_items:
616+
value = item.get(column)
617+
if not isinstance(value, (dict, list)):
618+
collected.append(value)
619+
else:
620+
for item in obj:
621+
_walk(item)
622+
623+
_walk(data)
624+
return collected
625+
602626
def _normalize_pipeline_specs(self, pipelines):
603627
specs = []
604628
column_counter = Counter()
@@ -803,6 +827,7 @@ def _fetch_pipeline_data(self, spec, base_study_ids):
803827
)
804828
per_base[row.base_study_id] = {
805829
"flat": flattened,
830+
"raw": result_data if isinstance(result_data, dict) else {},
806831
"config_id": row.config_id,
807832
"version": row.version,
808833
"timestamp": timestamp,
@@ -881,6 +906,24 @@ def _apply_pipeline_columns(self, annotation, data, specs, column_counter):
881906
entry = pipeline_data.get(base_study_id)
882907
flat_values = entry["flat"] if entry else {}
883908
value = flat_values.get(column)
909+
if isinstance(value, list):
910+
flattened_value = ",".join(
911+
str(item) for item in value if item is not None
912+
)
913+
value = flattened_value if flattened_value else None
914+
if value is None and entry:
915+
raw_values = [
916+
v
917+
for v in self._collect_column_values(
918+
entry.get("raw", {}), column
919+
)
920+
if v is not None
921+
]
922+
if raw_values:
923+
if len(raw_values) == 1:
924+
value = str(raw_values[0])
925+
else:
926+
value = ",".join(str(v) for v in raw_values)
884927

885928
payload.setdefault("note", {})
886929
payload["note"][key_name] = value

store/backend/neurostore/tests/api/test_annotations.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,14 @@ def test_put_annotation_applies_pipeline_columns(auth_client, session):
488488
config=config,
489489
status="SUCCESS",
490490
date_executed=datetime(2024, 1, 1, tzinfo=timezone.utc),
491-
result_data={"string_field": "demo", "numeric_field": 42},
491+
result_data={
492+
"string_field": "demo",
493+
"numeric_field": 42,
494+
"array_field": [
495+
{"name": "a"},
496+
{"name": "b"},
497+
],
498+
},
492499
file_inputs={},
493500
)
494501
session.add_all([pipeline, config, result])
@@ -498,7 +505,7 @@ def test_put_annotation_applies_pipeline_columns(auth_client, session):
498505
"pipelines": [
499506
{
500507
"name": pipeline.name,
501-
"columns": ["string_field", "numeric_field"],
508+
"columns": ["string_field", "numeric_field", "name"],
502509
}
503510
]
504511
}
@@ -510,6 +517,7 @@ def test_put_annotation_applies_pipeline_columns(auth_client, session):
510517
assert body["note_keys"]["existing"] == "string"
511518
assert body["note_keys"]["string_field"] == "string"
512519
assert body["note_keys"]["numeric_field"] == "number"
520+
assert body["note_keys"]["name"] == "string"
513521

514522
notes = body["notes"]
515523
assert len(notes) == 2
@@ -518,6 +526,7 @@ def test_put_annotation_applies_pipeline_columns(auth_client, session):
518526
assert note["existing"] in {"A1", "A2"}
519527
assert note["string_field"] == "demo"
520528
assert note["numeric_field"] == 42
529+
assert note["name"] == "a,b"
521530

522531

523532
def test_put_annotation_pipeline_column_conflict_suffix(auth_client, session):
@@ -536,7 +545,13 @@ def test_put_annotation_pipeline_column_conflict_suffix(auth_client, session):
536545
config=config_one,
537546
status="SUCCESS",
538547
date_executed=datetime(2024, 1, 1, tzinfo=timezone.utc),
539-
result_data={"string_field": "primary"},
548+
result_data={
549+
"string_field": "primary",
550+
"array_field": [
551+
{"name": "a"},
552+
{"name": "b"},
553+
],
554+
},
540555
file_inputs={},
541556
)
542557

@@ -570,7 +585,7 @@ def test_put_annotation_pipeline_column_conflict_suffix(auth_client, session):
570585

571586
payload = {
572587
"pipelines": [
573-
{"name": pipeline_one.name, "columns": ["string_field"]},
588+
{"name": pipeline_one.name, "columns": ["string_field", "name"]},
574589
{
575590
"name": pipeline_two.name,
576591
"columns": ["string_field"],
@@ -590,11 +605,13 @@ def test_put_annotation_pipeline_column_conflict_suffix(auth_client, session):
590605
assert key_two in body["note_keys"]
591606
assert body["note_keys"][key_one] == "string"
592607
assert body["note_keys"][key_two] == "string"
608+
assert body["note_keys"]["name"] == "string"
593609

594610
for entry in body["notes"]:
595611
note = entry["note"]
596612
assert note[key_one] == "primary"
597613
assert note[key_two] == "secondary"
614+
assert note["name"] == "a,b"
598615

599616

600617
def test_annotation_analyses_post(auth_client, ingest_neurosynth, session):
@@ -629,8 +646,11 @@ def test_annotation_analyses_post(auth_client, ingest_neurosynth, session):
629646
get_resp = auth_client.get(f"/api/annotations/{annot.json()['id']}")
630647

631648
assert len(post_resp.json()) == 2 # third input did not have proper id
632-
assert (
633-
get_resp.json()["notes"][1]["note"]["doo"]
634-
== post_resp.json()[1]["note"]["doo"]
635-
== new_value
636-
)
649+
updated_by_analysis = {
650+
note["analysis"]: note["note"]["doo"] for note in post_resp.json()
651+
}
652+
current_by_analysis = {
653+
note["analysis"]: note["note"]["doo"] for note in get_resp.json()["notes"]
654+
}
655+
for analysis_id, value in updated_by_analysis.items():
656+
assert current_by_analysis[analysis_id] == value == new_value

0 commit comments

Comments
 (0)