Skip to content

Commit dbb0ef4

Browse files
authored
Merge pull request #43 from napakalas/issue-#42
Expose map–SCKAN node mappings; add a query and a test query.
2 parents b65b830 + 33637e7 commit dbb0ef4

4 files changed

Lines changed: 233 additions & 5 deletions

File tree

mapserver/competency/flatmap.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def anatomical_map_knowledge(map_uuid: str, competency_db: CompetencyKnowledge)
109109
'nerves': path_knowledge.get('node-nerves', []),
110110
'phenotypes': path_phenotypes.get(path_id, []),
111111
'references': path_evidence.get(path_id, []),
112+
'node-mappings': path_knowledge.get('node-mappings', []),
112113
}
113114
if 'alert' in properties:
114115
knowledge_terms[path_id]['alert'] = properties['alert']
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
queries:
2+
- id: 27
3+
label: Node mapping from sckan to map
4+
sql: >
5+
WITH node_mappings AS (
6+
SELECT DISTINCT
7+
sckan.sckan_id,
8+
sckan.path_id,
9+
sckan.sckan_node_id,
10+
map.source_id,
11+
map.node_id
12+
FROM (
13+
SELECT
14+
source_id AS sckan_id,
15+
path_id,
16+
node_id AS sckan_node_id
17+
FROM path_nodes
18+
WHERE source_id IN (
19+
SELECT sckan_id
20+
FROM path_node_mappings
21+
WHERE %CONDITION_1%
22+
)
23+
AND %CONDITION_0%
24+
) AS sckan
25+
LEFT JOIN (
26+
SELECT
27+
source_id,
28+
path_id,
29+
node_id,
30+
sckan_id,
31+
sckan_node_id
32+
FROM path_node_mappings
33+
WHERE %CONDITION_1%
34+
AND %CONDITION_0%
35+
) AS map
36+
ON sckan.path_id = map.path_id
37+
AND sckan.sckan_node_id = map.sckan_node_id
38+
),
39+
40+
sckan_labels AS (
41+
SELECT
42+
nm.sckan_id,
43+
nm.sckan_node_id,
44+
string_agg(pt.label, ', ' ORDER BY seq.pos) AS sckan_node_label
45+
FROM node_mappings nm
46+
JOIN path_node_features pnf
47+
ON nm.sckan_id = pnf.source_id
48+
AND nm.path_id = pnf.path_id
49+
AND nm.sckan_node_id = pnf.node_id
50+
JOIN feature_terms pt
51+
ON pnf.source_id = pt.source_id
52+
AND pnf.feature_id = pt.term_id
53+
CROSS JOIN LATERAL (
54+
SELECT pos
55+
FROM (
56+
SELECT pnf.node_id::jsonb->>0 AS value, 1 AS pos
57+
UNION ALL
58+
SELECT value, ordinality + 1 AS pos
59+
FROM jsonb_array_elements_text(pnf.node_id::jsonb->1) WITH ORDINALITY
60+
) s
61+
WHERE s.value = pt.term_id
62+
) AS seq
63+
GROUP BY nm.sckan_id, nm.sckan_node_id
64+
),
65+
66+
map_labels AS (
67+
SELECT
68+
nm.source_id,
69+
nm.node_id,
70+
string_agg(pt.label, ', ' ORDER BY seq.pos) AS node_label
71+
FROM node_mappings nm
72+
JOIN path_node_features pnf
73+
ON nm.source_id = pnf.source_id
74+
AND nm.path_id = pnf.path_id
75+
AND nm.node_id = pnf.node_id
76+
JOIN feature_terms pt
77+
ON pnf.source_id = pt.source_id
78+
AND pnf.feature_id = pt.term_id
79+
CROSS JOIN LATERAL (
80+
SELECT pos
81+
FROM (
82+
SELECT pnf.node_id::jsonb->>0 AS value, 1 AS pos
83+
UNION ALL
84+
SELECT value, ordinality + 1 AS pos
85+
FROM jsonb_array_elements_text(pnf.node_id::jsonb->1) WITH ORDINALITY
86+
) s
87+
WHERE s.value = pt.term_id
88+
) AS seq
89+
GROUP BY nm.source_id, nm.node_id
90+
)
91+
92+
SELECT
93+
nm.sckan_id,
94+
nm.path_id,
95+
nm.sckan_node_id,
96+
UPPER(SUBSTRING(sl.sckan_node_label, 1, 1)) ||
97+
SUBSTRING(sl.sckan_node_label, 2) AS sckan_node_label,
98+
COALESCE(nm.source_id, '') AS source_id,
99+
COALESCE(nm.node_id, '') AS node_id,
100+
COALESCE(UPPER(SUBSTRING(ml.node_label, 1, 1)) ||
101+
SUBSTRING(ml.node_label, 2), '') AS node_label
102+
FROM node_mappings nm
103+
NATURAL JOIN sckan_labels sl
104+
LEFT JOIN map_labels ml
105+
ON nm.source_id = ml.source_id
106+
AND nm.node_id = ml.node_id;
107+
parameters:
108+
- id: path_id
109+
column: path_id
110+
label: Neuron population
111+
type: string
112+
multiple: true
113+
condition: CONDITION_0
114+
- id: source_id
115+
column: source_id
116+
label: Knowledge source
117+
type: string
118+
multiple: true
119+
condition: CONDITION_1
120+
default_msg: the latest source is used
121+
default_sql: >
122+
select source_id from knowledge_sources where source_id like 'sckan%'
123+
order by source_id desc limit 1
124+
order: ''
125+
results:
126+
- key: sckan_id
127+
label: SKCAN Knowledge source
128+
type: string
129+
- key: path_id
130+
label: Neuron population
131+
type: string
132+
- key: sckan_node_id
133+
label: SKCAN Node ID
134+
type: string
135+
- key: sckan_node_label
136+
label: SKCAN Node Label
137+
type: string
138+
- key: source_id
139+
label: Knowledge source
140+
type: string
141+
- key: node_id
142+
label: Map Node ID
143+
type: string
144+
- key: node_label
145+
label: Map Node Label
146+
type: string

tests/test_query_27.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import pytest
2+
from utility import cq_request, assert_valid_query_response, MALE_UUID, FEMALE_UUID, RAT_UUID
3+
4+
base_query = {
5+
'query_id': '27',
6+
'parameters': [
7+
{'column': 'path_id','value': 'ilxtr:neuron-type-bolew-unbranched-15'}
8+
]
9+
}
10+
11+
expected_sckan_node_ids = [
12+
'["ILX:0738293", []]',
13+
'["ILX:0738305", ["UBERON:0001532"]]',
14+
'["ILX:0793621", []]',
15+
'["UBERON:0001989", []]',
16+
'["UBERON:0003708", []]'
17+
]
18+
expected_node_ids = [
19+
'["ILX:0738293", []]',
20+
'["ILX:0738305", []]',
21+
'["ILX:0793621", []]',
22+
'["UBERON:0001989", []]',
23+
'["UBERON:0003708", []]'
24+
]
25+
26+
def test_human_male_map():
27+
query = {**base_query, 'parameters': base_query['parameters'] + [{'column': 'source_id', 'value': MALE_UUID}]}
28+
response = cq_request(query)
29+
30+
assert_valid_query_response(
31+
response,
32+
expected_num_keys=7,
33+
expected_num_values=5,
34+
expected_column_values={
35+
'sckan_node_id': expected_sckan_node_ids,
36+
'node_id': expected_node_ids
37+
}
38+
)
39+
40+
def test_human_female_map():
41+
query = {**base_query, 'parameters': base_query['parameters'] + [{'column': 'source_id', 'value': FEMALE_UUID}]}
42+
response = cq_request(query)
43+
44+
assert_valid_query_response(
45+
response,
46+
expected_num_keys=7,
47+
expected_num_values=5,
48+
expected_column_values={
49+
'sckan_node_id': expected_sckan_node_ids,
50+
'node_id': expected_node_ids
51+
}
52+
)
53+
54+
def test_human_rat_map():
55+
query = {**base_query, 'parameters': base_query['parameters'] + [{'column': 'source_id', 'value': RAT_UUID}]}
56+
response = cq_request(query)
57+
58+
assert_valid_query_response(
59+
response,
60+
expected_num_keys=7,
61+
expected_num_values=5,
62+
expected_column_values={
63+
'sckan_node_id': expected_sckan_node_ids,
64+
'node_id': expected_node_ids
65+
}
66+
)
67+
#===============================================================================

tests/utility.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
import requests
2+
from tools.portal_maps import ENDPOINTS, latest_maps
23

3-
END_POINT = 'https://mapcore-demo.org/devel/flatmap/v4/competency/query'
4+
END_POINT = ENDPOINTS['development']
5+
CQ_END_POINT = f'{END_POINT}/competency/query'
46
HEADERS = {'Content-Type': 'application/json'}
57

6-
MALE_UUID = '2b76d336-5c56-55e3-ab1e-795d6c63f9c1'
7-
FEMALE_UUID = '91359a0f-9e32-5309-b365-145d9956817d'
8-
RAT_UUID = 'fb6d0345-cb70-5c7e-893c-d744a6313c95'
8+
# latest flatmap UUIDs for testing obtained dynamically
9+
maps = latest_maps(END_POINT)
10+
11+
MALE_KEY = ('NCBITaxon:9606', 'PATO:0000384')
12+
FEMALE_KEY = ('NCBITaxon:9606', 'PATO:0000383')
13+
RAT_KEY = ('NCBITaxon:10114', None)
14+
15+
MALE_FALLBACK_UUID = '2b76d336-5c56-55e3-ab1e-795d6c63f9c1'
16+
FEMALE_FALLBACK_UUID = '91359a0f-9e32-5309-b365-145d9956817d'
17+
RAT_FALLBACK_UUID = 'fb6d0345-cb70-5c7e-893c-d744a6313c95'
18+
19+
MALE_UUID = maps.get(MALE_KEY, {}).get('uuid', MALE_FALLBACK_UUID)
20+
FEMALE_UUID = maps.get(FEMALE_KEY, {}).get('uuid', FEMALE_FALLBACK_UUID)
21+
RAT_UUID = maps.get(RAT_KEY, {}).get('uuid', RAT_FALLBACK_UUID)
22+
923
SCKAN_VERSION = 'sckan-2024-09-21'
1024

1125
def cq_request(query: dict):
12-
response = requests.post(END_POINT, json=query, headers=HEADERS)
26+
response = requests.post(CQ_END_POINT, json=query, headers=HEADERS)
1327
return response
1428

1529
def assert_valid_query_response(

0 commit comments

Comments
 (0)