Skip to content

Commit

Permalink
Addressing invalid merge from dev-integrate. Importing into new branc…
Browse files Browse the repository at this point in the history
…h of main source files changed in branch jas/assayclasses_optional_hierarchy.
  • Loading branch information
AlanSimmons committed Nov 4, 2024
1 parent 08a6411 commit accf435
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 65 deletions.
38 changes: 16 additions & 22 deletions hs-ontology-api-spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ paths:
schema:
type: string
example: AF
- name: provide-hierarchy-info
in: query
required: false
description: optional response filter - if true, then the response will contain hierarchy information for the dataset type. The default is blank, or false.
schema:
type: string
responses:
'200':
description: A JSON array of assay classification objects
Expand Down Expand Up @@ -97,6 +103,12 @@ paths:
- primary
- derived
- epic
- name: provide-hierarchy-info
in: query
required: false
description: optional response filter - if true, then the response will contain hierarchy information for the dataset type. The default is blank, or false.
schema:
type: string
responses:
'200':
description: An assay classification objects
Expand Down Expand Up @@ -1781,28 +1793,10 @@ components:
type: string
description: identifier for the table schema for files in datasets generated by the processing workflow associated with the assay classification
example: imc-v
measurement_assay:
type: object
description: corresponds to the generic experiment or assay modality used to generate a "raw" dataset from a tissue sample.
properties:
codes:
type: array
description: set of codes from biomedical vocabularies for the measurement assay
items:
type: object
properties:
code:
type: string
description: code from a biomedical vocabulary or ontology
example: OBI:0003096
term:
type: string
description: preferred term for the code in the biomedical vocabulary or ontology
example: imaging mass cytometry assay
contains_full_genetic_sequences:
type: boolean
description: whether the measurement assay generates full genetic sequences from the sample. Full genetic sequencing information from human sources is considered Patient Identifying Information (PII), which is protected.
example: false
contains_full_genetic_sequences:
type: boolean
description: whether the measurement assay generates full genetic sequences from the sample. Full genetic sequencing information from human sources is considered Patient Identifying Information (PII), which is protected.
example: false
is_multiassay:
type: boolean
description: indicates whether the assay class is for a "multi-assay". Multi-assays use more than one experimental modality on samples to generate datasets--e.g., both RNAseq and ATACseq.
Expand Down
76 changes: 40 additions & 36 deletions src/hs_ontology_api/cypher/assayclass.cypher
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
// Obtain identifiers for rule-based datasets (assay classes) for the application context.
// The assayclass_filter allows filtering by either the UBKG code or term (rule_description)
// for the assay class.
WITH '$context' AS context
// OCT 2024 - filter response content based on
// 1. whether to provide dataset type hierarchical information
// 2. whether to provide measurement assay codes

WITH '$context' AS context, '$provide_hierarchy_info' AS provide_hierarchy_info
CALL
{
WITH context
Expand Down Expand Up @@ -65,6 +69,7 @@ CALL
RETURN tdsProcess.name as process_state
}
// dataset_type

// The dataset_type concepts in HUBMAP are cross-referenced to HRAVS concepts; however, the terms for the HRAVS concepts
// are enclosed in a list, so use the HUBMAP terms.
CALL
Expand Down Expand Up @@ -106,6 +111,29 @@ CALL
WHERE pDatasetType.CUI=CUIDatasetType AND r.CUI=pFig2category.CUI AND cFig2category.SAB=context
RETURN DISTINCT tFig2category.name AS fig2_category
}
// dataset_type summary
// Oct 2024 - content driven by provide_hierarchy_info parameter.
CALL
{
WITH dataset_type, pdr_category, fig2_aggregated_assaytype, fig2_modality, fig2_category, provide_hierarchy_info
RETURN
CASE
WHEN provide_hierarchy_info='True'
THEN
{
dataset_type:dataset_type,
PDR_category:pdr_category,
fig2:
{
aggregated_assaytype:fig2_aggregated_assaytype,
modality:fig2_modality,
category:fig2_category
}
}
ELSE
dataset_type
END AS dataset_type_summary
}
// description
CALL
{
Expand Down Expand Up @@ -139,28 +167,13 @@ CALL
WHERE pRBD.CUI=CUIRBD AND r.CUI=pDT.CUI AND cDT.SAB=context
RETURN COLLECT(DISTINCT tDT.name) AS must_contain
}
// measurement assay CUI
CALL
{
WITH CUIRBD
OPTIONAL MATCH (pRBD:Concept)-[:has_measurement_assay]->(pMeas:Concept)
WHERE pRBD.CUI=CUIRBD
RETURN DISTINCT pMeas.CUI as CUIMeas
}
// Optional measurement codes
CALL
{
WITH CUIMeas
MATCH (pMeas:Concept)-[:CODE]->(cMeas:Code)-[:PT]->(tMeas:Term)
WHERE pMeas.CUI = CUIMeas
RETURN COLLECT(DISTINCT {code:cMeas.CodeID,term:tMeas.name}) AS MeasCodes
}
// whether the measurement assay contains full_genetic_sequencesi

// whether the assay classification contains full_genetic_sequences
CALL
{
WITH CUIMeas,context
WITH CUIRBD,context
OPTIONAL MATCH (pRBD:Concept)-[:contains]->(ppii:Concept)
WHERE pRBD.CUI=CUIMeas
WHERE pRBD.CUI=CUIRBD
AND ppii.CUI = context+':C004009 CUI'
RETURN DISTINCT CASE WHEN NOT ppii.CUI IS null THEN true ELSE false END AS contains_full_genetic_sequences
}
Expand All @@ -173,14 +186,14 @@ CALL
WHERE pRBD.CUI=CUIRBD AND r.CUI=pStatus.CUI and cStatus.SAB=context
RETURN DISTINCT tStatus.name AS active_status
}
// Response
CALL
{
WITH
context, CodeRBD, NameRBD, assaytype, dir_schema, tbl_schema,
vitessce_hints,process_state,pipeline_shorthand,
description,dataset_type,pdr_category,
fig2_aggregated_assaytype,fig2_modality,fig2_category,
is_multiassay,must_contain,MeasCodes,contains_full_genetic_sequences,active_status
description,dataset_type_summary,
is_multiassay,must_contain,active_status, contains_full_genetic_sequences
RETURN
{
rule_description:
Expand All @@ -193,20 +206,11 @@ RETURN
pipeline_shorthand:pipeline_shorthand, description:description,
is_multiassay:is_multiassay, must_contain:must_contain,
active_status:active_status,
dataset_type:
{
dataset_type:dataset_type, PDR_category:pdr_category,
fig2:
{
aggregated_assaytype:fig2_aggregated_assaytype, modality:fig2_modality, category:fig2_category
}
},
measurement_assay:{
codes:MeasCodes,
contains_full_genetic_sequences:contains_full_genetic_sequences
}
dataset_type:dataset_type_summary,
contains_full_genetic_sequences:contains_full_genetic_sequences
}
} AS rule_based_dataset
}
AS rule_based_dataset
}
WITH rule_based_dataset
RETURN rule_based_dataset AS rule_based_datasets
57 changes: 52 additions & 5 deletions src/hs_ontology_api/routes/assayclasses/assayclasses_controller.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,43 @@
from flask import Blueprint, jsonify, current_app, make_response,request
from flask import Blueprint, jsonify, current_app, make_response,request, abort

from ubkg_api.utils.http_error_string import (get_404_error_string, validate_query_parameter_names,
validate_parameter_value_in_enum, validate_required_parameters)
from hs_ontology_api.utils.neo4j_logic import assayclasses_get_logic

from urllib.parse import urlencode

assayclasses_blueprint = Blueprint('assayclasses_hs', __name__, url_prefix='/assayclasses')

def validate_param_as_boolean(param_name: str) ->str:
"""
Evaluates a request parameter string as a boolean.
:param param_name: name of the request parameter
"""

val_enum = ['TRUE', 'FALSE']
param_value = request.args.get(param_name)
if param_value is not None:
param_value = param_value.upper()

return validate_parameter_value_in_enum(param_name=param_name, param_value=param_value, enum_list=val_enum)

def convert_param_to_boolean_string(param_name: str) -> str:
"""
Converts a parameter string to a "boolean string"--i.e., "True" or "False"
:param param_name: name of parameter
"""

param_value = request.args.get(param_name)
if param_value is None:
return 'False'

param_value = param_value.upper()
if param_value == 'TRUE':
return 'True'
else:
return 'False'

@assayclasses_blueprint.route('', methods=['GET'])
def assayclasses_expand_get():
return assayclasses_get()
Expand All @@ -19,11 +51,16 @@ def assayclasses_get(name=None):
in the testing rules json, with options to filter the list to those with specific property values.
Filters are additive (i.e., boolean AND)
October 2024 - Request parameters added for:
provide_hierarchy_info: whether to display hierarchical information on the dataset type
provide_measurement_assay_codes: whether to display the measurement assay codes
"""
# Validate parameters.

# Check for invalid parameter names.
err = validate_query_parameter_names(parameter_name_list=['application_context','process_state','assaytype'])
err = validate_query_parameter_names(parameter_name_list=['application_context', 'process_state', 'assaytype',
'provide-hierarchy-info'])
if err != 'ok':
return make_response(err, 400)

Expand All @@ -33,9 +70,11 @@ def assayclasses_get(name=None):
return make_response(err, 400)
application_context = request.args.get('application_context')

# Check for valid application context. The parameter is case-insensitive.
# Check for valid application context. The parameter is case-insensitive, but any error should return the
# value provided in the request.
val_enum = ['HUBMAP','SENNET']
err = validate_parameter_value_in_enum(param_name='application_context', param_value=application_context.upper(), enum_list=val_enum)
err = validate_parameter_value_in_enum(param_name='application_context', param_value=application_context.upper(),
enum_list=val_enum)
if err != 'ok':
return make_response(err, 400)
application_context = application_context.upper()
Expand All @@ -57,9 +96,17 @@ def assayclasses_get(name=None):
if assaytype is not None and name is not None:
assaytype = None

# Oct 2024 - filter response for dataset type hierarchy.
err = validate_param_as_boolean('provide-hierarchy-info')
if err != 'ok':
return make_response(err, 400)
provide_hierarchy_info = convert_param_to_boolean_string('provide-hierarchy-info')


neo4j_instance = current_app.neo4jConnectionHelper.instance()
result = assayclasses_get_logic(
neo4j_instance, assayclass=name, process_state=process_state, assaytype=assaytype, context=application_context)
neo4j_instance, assayclass=name, process_state=process_state, assaytype=assaytype,
context=application_context, provide_hierarchy_info=provide_hierarchy_info)

if result is None or result == []:
# Empty result
Expand Down
12 changes: 11 additions & 1 deletion src/hs_ontology_api/utils/neo4j_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1282,8 +1282,11 @@ def field_entities_get_logic(neo4j_instance, field_name=None, source=None, entit

return fieldentities

def assayclasses_get_logic(neo4j_instance,assayclass=None, assaytype=None, process_state=None, context=None) -> dict:
def assayclasses_get_logic(neo4j_instance,assayclass=None, assaytype=None, process_state=None,
context=None, provide_hierarchy_info=None) -> dict:
"""
October 2024 - filter response for hierarchical information.
July 2024
Obtains information on the assay classes (rule-based dataset "kinds") that are specified in
the testing rules json file.
Expand All @@ -1295,6 +1298,8 @@ def assayclasses_get_logic(neo4j_instance,assayclass=None, assaytype=None, proce
:param assaytype: the assaytype
:param context: application context--i.e., HUBMAP or SENNET
:param process_state: in the enum ['primary','derived','epic']
:param provide_hierarchy_info: "string boolean" (i.e. the word "True" or "False") indicating
whether to include dataset hierarchical information in response
example: if a assay class's rule has rule_description="non-DCWG primary AF" and rule code "HUBMAP:C200001", either
"non-DCWG primary AF" or "C200001" will result in selection of the assay class. The application context is used
Expand All @@ -1309,6 +1314,9 @@ def assayclasses_get_logic(neo4j_instance,assayclass=None, assaytype=None, proce
# Filter by application context.
querytxt = querytxt.replace('$context', context)

# Oct 2024 - Filter by dataset hierarchy.
querytxt = querytxt.replace('$provide_hierarchy_info', provide_hierarchy_info)

# Filter by assay class
if assayclass is not None:
querytxt = querytxt.replace('$assayclass_filter', f"AND (cRBD.CodeID = context+':{assayclass}' OR tRBD.name='{assayclass}')")
Expand All @@ -1328,7 +1336,9 @@ def assayclasses_get_logic(neo4j_instance,assayclass=None, assaytype=None, proce
elif assayclass is None:
querytxt = querytxt.replace('$assaytype_filter', f"AND REPLACE(tassaytype.name,'_assaytype','') = '{assaytype}'")
else:

querytxt = querytxt.replace('$assaytype_filter','')

# Set timeout for query based on value in app.cfg.
query = neo4j.Query(text=querytxt, timeout=neo4j_instance.timeout)

Expand Down
36 changes: 35 additions & 1 deletion test/test_api.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ echo | tee -a test.out


echo "TESTS FOR: assayclasses GET" | tee -a test.out
echo "SIGNATURE: /assayclasses?application_context=<context>&process_state=<process_state>" | tee -a test.out
echo "SIGNATURE: /assayclasses?application_context=<context>&process_state=<process_state>&provide_hiearchy_info=<true,false>&provide_measurement_assay_codes=<true,false>" | tee -a test.out
echo | tee -a test.out
echo | tee -a test.out
echo "1. /assayclasses?application_context=x => invalid application context; should return 400" | tee -a test.out
Expand Down Expand Up @@ -137,6 +137,40 @@ echo
echo | tee -a test.out
echo | tee -a test.out

echo "9. /assayclasses/C200001 => invalid provide-hierarchy-info; should return 400" | tee -a test.out
curl --request GET \
--url "${UBKG_URL}/assayclasses/C200001?application_context=HUBMAP&provide-hierarchy-info=x" \
--header "Accept: application/json" | cut -c1-60 | tee -a test.out
echo
echo | tee -a test.out
echo | tee -a test.out

echo "10. /assayclasses/C200001 => valid provide-hierarchy-info; should return 200" | tee -a test.out
curl --request GET \
--url "${UBKG_URL}/assayclasses/C200001?application_context=HUBMAP&provide-hierarchy-info=true" \
--header "Accept: application/json" | cut -c1-60 | tee -a test.out
echo
echo | tee -a test.out
echo | tee -a test.out

echo "11. /assayclasses/C200001 => invalid provide-measurement-assay-codes should return 400" | tee -a test.out
curl --request GET \
--url "${UBKG_URL}/assayclasses/C200001?application_context=HUBMAP&provide-measurement-assay-codes=x" \
--header "Accept: application/json" | cut -c1-60 | tee -a test.out
echo
echo | tee -a test.out
echo | tee -a test.out

echo "12. /assayclasses/C200001 => valid provide-measurement-assay-codes; should return 200" | tee -a test.out
curl --request GET \
--url "${UBKG_URL}/assayclasses/C200001?application_context=HUBMAP&provide-measurement-assay-codes=true" \
--header "Accept: application/json" | cut -c1-60 | tee -a test.out
echo
echo | tee -a test.out
echo | tee -a test.out

exit;

echo "TESTS FOR: dataset-types GET" | tee -a test.out
echo "SIGNATURE: /dataset-types?application_context" | tee -a test.out
echo | tee -a test.out
Expand Down

0 comments on commit accf435

Please sign in to comment.