def resolve_by_ui(args: dict, info: graphene.ResolveInfo, ui: str) -> ModelDescriptor: """Retrieves a `ModelDescriptor` object through its UI. Args: args (dict): The resolver arguments. info (graphene.ResolveInfo): The resolver info. ui (str): The UI of the `ModelDescriptor` to retrieve. Returns: DescriptorModel: The retrieved `ModelDescriptor` object or `None` if no match was not found. """ # Retrieve the query on `ModelDescriptor`. query = TypeDescriptor.get_query(info=info, ) # type: sqlalchemy.orm.query.Query # Filter to the `ModelDescriptor` record matching `ui`. query = query.filter(ModelDescriptor.ui == ui) # Limit query to fields requested in the GraphQL query adding # `load_only` and `joinedload` options as required. query = apply_requested_fields( info=info, query=query, orm_class=ModelDescriptor, ) obj = query.first() return obj
def resolve_by_group_name(args: dict, info: graphene.ResolveInfo, group_name: str) -> List[ModelHealthTopic]: """ Retrieves `ModelHealthTopic` record objects through their group name. Args: args (dict): The resolver arguments. info (graphene.ResolveInfo): The resolver info. group_name (str): The name of the health-topic group to retrieve health-topics for. Returns: List[ModelHealthTopic]: The retrieved `ModelHealthTopic` record objects or an empty list if no matches were found. """ # Retrieve the query on `ModelHealthTopic`. query = TypeHealthTopic.get_query( info=info) # type: sqlalchemy.orm.query.Query # Filter to the `ModelHealthTopic` records matching the given # group. query = query.join(ModelHealthTopic.health_topic_groups) query = query.filter(ModelHealthTopicGroup.name == group_name) # Limit query to fields requested in the GraphQL query adding # `load_only` and `joinedload` options as required. query = apply_requested_fields(info=info, query=query, orm_class=ModelHealthTopic) objs = query.all() return objs
def resolve_by_synonym( args: Dict, info: graphene.ResolveInfo, synonym: str, limit: Union[int, None] = None, ) -> List[ModelDescriptor]: """Retrieves a list of `ModelDescriptor` objects with a tree-number prefix-matching `tree_number_prefix`. Args: args (dict): The resolver arguments. info (graphene.ResolveInfo): The resolver info. synonym (str): The synonym query by which to perform the search. limit (int, optional): The number of closest-matching descriptors to return. Defaults to `None`. Returns: list[DescriptorModel]: The list of matched `ModelDescriptor` objects or an empty list if no match was found. """ # Retrieve the session out of the context as the `get_query` method # automatically selects the model. session = info.context.get("session") # type: sqlalchemy.orm.Session # Define a function to calculate the maximum similarity between a # descriptor's synonyms and the synonym query. func_similarity = sqlalchemy_func.max( sqlalchemy_func.similarity( ModelDescriptorSynonym.synonym, synonym, )).label("synonym_similarity") # Query out `ModelDescriptor`. query = session.query(ModelDescriptor) # type: sqlalchemy.orm.Query query = query.join(ModelDescriptor.synonyms) query = query.filter(ModelDescriptorSynonym.synonym.op("%%")(synonym)) query = query.order_by(func_similarity.desc()) query = query.group_by(ModelDescriptor.descriptor_id) if limit is not None: query = query.limit(limit=limit) # Limit query to fields requested in the GraphQL query adding # `load_only` and `joinedload` options as required. query = apply_requested_fields( info=info, query=query, orm_class=ModelDescriptor, ) objs = query.all() return objs
def resolve_by_search_uuid( args: dict, info: graphene.ResolveInfo, auth0_user_id: str, search_uuid: str, ) -> ModelSearch: """ Retrieves a `ModelUser` record object through its Auth0 IDs. Args: args (dict): The resolver arguments. info (graphene.ResolveInfo): The resolver info. auth0_user_id (str): The Auth0 ID of the user to which the search belongs. search_uuid (uuid.UUID): The search UUID for which the `ModelSearch` record object will be retrieved. Returns: ModelSearch: The retrieved `ModelSearch` record object or `None` if no matches were found. """ # Cleanup the Auth0 user ID. auth0_user_id = clean_auth0_user_id(auth0_user_id=str(auth0_user_id)) # Check that the requesting user is authorized to retrieve the user with # the given Auth0 ID. check_auth(info=info, auth0_user_id=auth0_user_id) # Retrieve the query on `ModelSearch`. query = TypeSearch.get_query( info=info, ) # type: sqlalchemy.orm.query.Query # Filter to the `ModelSearch` record matching the `search_uuid`. query = query.filter(ModelSearch.search_uuid == search_uuid) # Limit query to fields requested in the GraphQL query adding # `load_only` and `joinedload` options as required. query = apply_requested_fields( info=info, query=query, orm_class=ModelSearch, ) obj = query.one_or_none() # Raise an exception if the requested search could not be found. if not obj: msg = "Search with UUID '{}' could not be found." msg_fmt = msg.format(search_uuid) raise graphql.GraphQLError(message=msg_fmt) return obj
def resolve_by_tree_number_prefix( args: dict, info: graphene.ResolveInfo, tree_number_prefix: str) -> List[ModelDescriptor]: """Retrieves a list of `ModelDescriptor` objects with a tree-number prefix-matching `tree_number_prefix`. Args: args (dict): The resolver arguments. info (graphene.ResolveInfo): The resolver info. tree_number_prefix (str): The tree-number prefix to match against. Returns: list[DescriptorModel]: The list of matched `ModelDescriptor` objects or an empty list if no match was found. """ # Retrieve the session out of the context as the `get_query` method # automatically selects the model. session = info.context.get("session") # type: sqlalchemy.orm.Session # Query out `ModelDescriptor`. query = session.query(ModelDescriptor) # type: sqlalchemy.orm.Query query = query.join(ModelDescriptor.tree_numbers) # Filter down to `ModelDescriptor` objects with a tree-number # prefix-matching `tree_number_prefix`. query = query.filter( ModelTreeNumber.tree_number.like( "{}%".format(tree_number_prefix)), ) # Limit query to fields requested in the GraphQL query adding # `load_only` and `joinedload` options as required. query = apply_requested_fields( info=info, query=query, orm_class=ModelDescriptor, ) objs = query.all() return objs
def resolve_by_id( args: dict, info: graphene.ResolveInfo, citation_ids: List[int], ) -> List[ModelCitation]: """Retrieves `ModelCitation` record objects through their IDs. Args: args (dict): The resolver arguments. info (graphene.ResolveInfo): The resolver info. citation_ids (List[str]): The IDs for which `ModelCitation` record objects will be retrieved. Returns: List[ModelCitation]: The retrieved `ModelCitation` record objects or an empty list if no matches were found. """ # Retrieve the query on `ModelCitation`. query = TypeCitation.get_query( info=info, ) # type: sqlalchemy.orm.query.Query # Filter to the `ModelCitation` records matching any of the # `citation_ids`. query = query.filter(ModelCitation.citation_id.in_(citation_ids)) # Limit query to fields requested in the GraphQL query adding # `load_only` and `joinedload` options as required. query = apply_requested_fields( info=info, query=query, orm_class=ModelCitation, ) objs = query.all() return objs
def resolve_filter( args: dict, info: graphene.ResolveInfo, study_ids: List[int], overall_statuses: Optional[List[EnumOverallStatus]] = None, cities: Optional[List[str]] = None, states: Optional[List[str]] = None, countries: Optional[List[str]] = None, facility_canonical_ids: Optional[List[int]] = None, current_location_longitude: Optional[float] = None, current_location_latitude: Optional[float] = None, distance_max_km: Optional[int] = None, intervention_types: Optional[List[EnumIntervention]] = None, phases: Optional[List[EnumPhase]] = None, study_types: Optional[List[EnumStudy]] = None, gender: Optional[EnumGender] = None, year_beg: Optional[int] = None, year_end: Optional[int] = None, age_beg: Optional[int] = None, age_end: Optional[int] = None, order_by: Optional[str] = None, order: Optional[TypeEnumOrder] = None, offset: Optional[int] = None, limit: Optional[int] = None, ) -> List[ModelStudy]: # Retrieve the session out of the context as the `get_query` method # automatically selects the model. session = info.context.get("session") # type: sqlalchemy.orm.Session query = session.query(ModelStudy) # type: sqlalchemy.orm.query.Query # Limit query to fields requested in the GraphQL query adding # `load_only` and `joinedload` options as required. query = apply_requested_fields( info=info, query=query, orm_class=ModelStudy, ) # Apply the different optional filters to the query. query = TypeStudies._apply_query_filters( query=query, study_ids=study_ids, overall_statuses=overall_statuses, cities=cities, states=states, countries=countries, facility_canonical_ids=facility_canonical_ids, current_location_longitude=current_location_longitude, current_location_latitude=current_location_latitude, distance_max_km=distance_max_km, intervention_types=intervention_types, phases=phases, study_types=study_types, gender=gender, year_beg=year_beg, year_end=year_end, age_beg=age_beg, age_end=age_end, ) # Apply order (if defined). if order_by: # Convert the order-by field to snake-case. This allows for fields # to be defined in camel-case but won't error-out if the fields are # already in snake-case. order_by = to_snake_case(order_by) if order and order == TypeEnumOrder.DESC.value: query = query.order_by(getattr(ModelStudy, order_by).desc()) else: query = query.order_by(getattr(ModelStudy, order_by).asc()) query = query.group_by(ModelStudy.study_id) # Apply offset (if defined). if offset: query = query.offset(offset=offset) # Apply limit (if defined). if limit: query = query.limit(limit=limit) objs = query.all() return objs
def resolve_search( args: dict, info: graphene.ResolveInfo, mesh_descriptor_ids: List[int], gender: Optional[EnumGender] = None, year_beg: Optional[int] = None, year_end: Optional[int] = None, age_beg: Optional[int] = None, age_end: Optional[int] = None, do_include_children: Optional[bool] = True, ) -> List[ModelStudy]: """Retrieves a list of `ModelStudy` objects matching several optional filters. Args: args (dict): The resolver arguments. info (graphene.ResolveInfo): The resolver info. mesh_descriptor_ids (List[int]): A list of MeSH descriptor IDs of the descriptors tagged against the study. gender (Optional[EnumGender] = None): The patient gender to search by. year_beg (Optional[int] = None): The minimum year the start date a matched `ModelStudy` may have. year_end (Optional[int] = None): The maximum year the start date a matched `ModelStudy` may have. age_beg (Optional[int] = None): The minimum eligibility age date a matched `ModelStudy` may have. age_end (Optional[int] = None): The maximum eligibility age date a matched `ModelStudy` may have. do_include_children (Optional[bool] = True): Whether to search for and include in the search the children MeSH descriptors of the provided descriptors. Returns: List[StudyModel]: The list of matched `ModelStudy` objects or an empty list if no match was found. """ # Retrieve the session out of the context as the `get_query` method # automatically selects the model. session = info.context.get("session") # type: sqlalchemy.orm.Session # If the search is to account for the provided descriptors and their # children then find all the children descriptor IDs. Otherwise only # use the provided ones. if do_include_children: # Retrieve all tree-numbers for the specified MeSH descriptors. query_tns = session.query( ModelTreeNumber.tree_number, ModelDescriptorTreeNumber.descriptor_id, ) query_tns = query_tns.join(ModelTreeNumber.descriptor_tree_numbers) query_tns = query_tns.filter( ModelDescriptorTreeNumber.descriptor_id.in_( mesh_descriptor_ids, ), ) # Retrieve the tree-numbers and associate them with each provided # descriptor ID in `map_descriptor_tns`. map_descriptor_tns = {} tree_numbers_all = [] for tree_number, descriptor_id in query_tns.all(): map_descriptor_tns.setdefault( descriptor_id, [], ).append(tree_number) tree_numbers_all.append(tree_number) # Deduplicate the tree-numbers. tree_numbers_all = list(set(tree_numbers_all)) # If no tree-numbers have been found return an empty list. if not tree_numbers_all: return [] # Query out the IDs of all children descriptors of all provided # descriptors based on the retrieved tree-numbers. query_descs = session.query( ModelDescriptor.descriptor_id, ModelTreeNumber.tree_number, ) query_descs = query_descs.join(ModelDescriptor.tree_numbers) # Children descriptors are found by retrieving all descriptors # with any tree number prefixed by one of the previously found # tree-numbers. query_descs = query_descs.filter( ModelTreeNumber.tree_number.like( sqlalchemy.any_( postgresql.array( tuple([ "{}%".format(tn) for tn in tree_numbers_all ]))))) # Retrieve the children descriptor IDs and associate them with each # provided descriptor ID in `map_descriptor_children`. map_descriptor_children = {} for child_descriptor_id, tree_number in query_descs.all(): for descriptor_id, tree_numbers in map_descriptor_tns.items(): for tree_number_prefix in tree_numbers: if tree_number.startswith(tree_number_prefix): map_descriptor_children.setdefault( descriptor_id, [descriptor_id], ).append(child_descriptor_id) # Unique the descriptor IDs under each provided descriptor ID. for descriptor_id, tree_numbers in map_descriptor_children.items(): map_descriptor_children[descriptor_id] = list( set(tree_numbers)) else: map_descriptor_children = { descriptor_id: [descriptor_id] for descriptor_id in mesh_descriptor_ids } # If no descriptor IDs have been found return an empty list. if not map_descriptor_children: return [] # Create a function to aggregate the MeSH descriptor IDs of queried # studies into an array. array_descriptors = sqlalchemy_func.array_agg( ModelDescriptor.descriptor_id, ) # Find all clinical-trial studies associated with the MeSH descriptors # found prior. query = session.query(ModelStudy) # Filter studies by associated mesh-descriptors. query = query.join(ModelStudy.descriptors) # Filter studies the year of their start-date. if year_beg: query = query.filter( ModelStudy.start_date >= datetime.date(year_beg, 1, 1)) if year_end: query = query.filter( ModelStudy.start_date <= datetime.date(year_end, 12, 31)) # Join on the `eligibility` relationship if any of the fiters that # require it are defined. if gender or age_beg or age_end: query = query.join(ModelStudy.eligibility) # Apply a gender filter if defined. if gender: if gender == EnumGender.ALL: query = query.filter( ModelEligibility.gender == EnumGender.ALL.value, ) elif gender in [EnumGender.FEMALE, EnumGender.MALE]: _value = EnumGender.get_member(value=str(gender)) query = query.filter( sqlalchemy.or_( ModelEligibility.gender == EnumGender.ALL.value, ModelEligibility.gender == _value, )) # Filter studies by eligibility age. if age_beg or age_end: query = TypeStudies._apply_age_filter(query=query, age_beg=age_beg, age_end=age_end) # Group by study ID assembling the MeSH descriptor IDs of each study # into arrays. Each study will pass the filters if it has at least one # of the descriptors under the provided descriptors (which will be # multiple if children descriptors are used). This is achieved through # the overlap `&&` operator which returns `true` if one array shares at # least one element with the other. query = query.group_by(ModelStudy.study_id) query = query.having( sqlalchemy.and_(*[ array_descriptors.op("&&")(sqlalchemy_func.cast( descriptor_ids, postgresql.ARRAY(postgresql.BIGINT), )) for descriptor_ids in map_descriptor_children.values() ])) # Limit query to fields requested in the GraphQL query. query = apply_requested_fields( info=info, query=query, orm_class=ModelStudy, ) objs = query.all() return objs
def resolve_search( args: dict, info: graphene.ResolveInfo, mesh_descriptor_ids: List[int], year_beg: Optional[int] = None, year_end: Optional[int] = None, do_include_children: Optional[bool] = True, ) -> List[ModelCitation]: """Retrieves a list of `ModelCitation` objects matching several optional filters. Args: args (dict): The resolver arguments. info (graphene.ResolveInfo): The resolver info. mesh_descriptor_ids (List[int]): A list of MeSH descriptor IDs of the descriptors tagged against the study. year_beg (Optional[int]): The minimum year the publication date the article of a matched `ModelCitation` may have. year_end (Optional[int]): The maximum year the publication date the article of a matched `ModelCitation` may have. do_include_children (Optional[bool]): Whether to search for and include in the search the children MeSH descriptors of the provided descriptors. Returns: List[ModelCitation]: The list of matched `ModelCitation` objects or an empty list if no match was found. """ # Retrieve the session out of the context as the `get_query` method # automatically selects the model. session = info.context.get("session") # type: sqlalchemy.orm.Session # If the search is to account for the provided descriptors and their # children then find all the children descriptor IDs. Otherwise only # use the provided ones. if do_include_children: # Retrieve all tree-numbers for the specified MeSH descriptors. query_tns = session.query( ModelTreeNumber.tree_number, ModelDescriptorTreeNumber.descriptor_id, ) query_tns = query_tns.join(ModelTreeNumber.descriptor_tree_numbers) query_tns = query_tns.filter( ModelDescriptorTreeNumber.descriptor_id.in_( mesh_descriptor_ids, ), ) # Retrieve the tree-numbers and associate them with each provided # descriptor ID in `map_descriptor_tns`. map_descriptor_tns = {} tree_numbers_all = [] for tree_number, descriptor_id in query_tns.all(): map_descriptor_tns.setdefault( descriptor_id, [], ).append(tree_number) tree_numbers_all.append(tree_number) # Deduplicate the tree-numbers. tree_numbers_all = list(set(tree_numbers_all)) # If no tree-numbers have been found return an empty list. if not tree_numbers_all: return [] # Query out the IDs of all children descriptors of all provided # descriptors based on the retrieved tree-numbers. query_descs = session.query( ModelDescriptor.descriptor_id, ModelTreeNumber.tree_number, ) query_descs = query_descs.join(ModelDescriptor.tree_numbers) # Children descriptors are found by retrieving all descriptors # with any tree number prefixed by one of the previously found # tree-numbers. query_descs = query_descs.filter( ModelTreeNumber.tree_number.like( sqlalchemy.any_(postgresql.array( tuple([ "{}%".format(tn) for tn in tree_numbers_all ]) )) ) ) # Retrieve the children descriptor IDs and associate them with each # provided descriptor ID in `map_descriptor_children`. map_descriptor_children = {} for child_descriptor_id, tree_number in query_descs.all(): for descriptor_id, tree_numbers in map_descriptor_tns.items(): for tree_number_prefix in tree_numbers: if tree_number.startswith(tree_number_prefix): map_descriptor_children.setdefault( descriptor_id, [descriptor_id], ).append(child_descriptor_id) # Unique the descriptor IDs under each provided descriptor ID. for descriptor_id, tree_numbers in map_descriptor_children.items(): map_descriptor_children[descriptor_id] = list(set(tree_numbers)) else: map_descriptor_children = { descriptor_id: [descriptor_id] for descriptor_id in mesh_descriptor_ids } # If no descriptor IDs have been found return an empty list. if not map_descriptor_children: return [] # Create a function to aggregate the MeSH descriptor IDs of queried # citations into an array. array_descriptors = sqlalchemy_func.array_agg( ModelDescriptor.descriptor_id, ) # Find all PubMed citations associated with the MeSH descriptors found # prior. query = session.query(ModelCitation) # Filter citations by associated mesh-descriptors. query = query.join(ModelCitation.descriptors) if year_beg or year_end: query = query.join(ModelCitation.article) # Filter studies the year of their start-date. if year_beg: query = query.filter( ModelArticle.publication_year >= year_beg ) if year_end: query = query.filter( ModelArticle.publication_year <= year_end ) # Group by citation ID assembling the MeSH descriptor IDs of each study # into arrays. Each citation will pass the filters if it has at least # one of the descriptors under the provided descriptors (which will be # multiple if children descriptors are used). This is achieved through # the overlap `&&` operator which returns `true` if one array shares at # least one element with the other. query = query.group_by(ModelCitation.citation_id) query = query.having( sqlalchemy.and_( *[ array_descriptors.op("&&")( sqlalchemy_func.cast( descriptor_ids, postgresql.ARRAY(postgresql.BIGINT), ) ) for descriptor_ids in map_descriptor_children.values() ] ) ) # Limit query to fields requested in the GraphQL query. query = apply_requested_fields( info=info, query=query, orm_class=ModelCitation, ) objs = query.all() return objs
def resolve_count_citations_by_qualifier( args: dict, info: graphene.ResolveInfo, citation_ids: List[int], limit: Optional[int] = None, ) -> List[TypeCountCitationsQualifier]: """Creates a list of `TypeCountCitationsQualifier` objects with the number of citations per qualifier. Args: args (dict): The resolver arguments. info (graphene.ResolveInfo): The resolver info. citation_ids (List[int]): A list of citation IDs. limit (Optional[int]): The number of results to return. Defaults to `None` in which case all results are returned. Returns: list[TypeCountCitationsQualifier]: The list of `TypeCountCitationsQualifier` objects with the results of the aggregation. """ # Retrieve the session out of the context as the `get_query` method # automatically selects the model. session = info.context.get("session") # type: sqlalchemy.orm.Session # Define the `COUNT(citations.citation_id)` function. func_count_citations = sqlalchemy_func.count( sqlalchemy_func.distinct( ModelCitationDescriptorQualifier.citation_id), ) # Query out the count of citations by country. query = session.query( ModelQualifier, func_count_citations, ) # type: sqlalchemy.orm.Query query = query.join( ModelCitationDescriptorQualifier, ModelCitationDescriptorQualifier.qualifier_id == ModelQualifier.qualifier_id, ) query = query.filter( ModelCitationDescriptorQualifier.citation_id.in_(citation_ids), ) # Group by qualifier. query = query.group_by(ModelQualifier.qualifier_id, ) # Order by the number of studies. query = query.order_by(func_count_citations.desc()) # Extract the fields requested in the GraphQL query. fields = extract_requested_fields( info=info, fields=info.field_asts, do_convert_to_snake_case=True, )["count_citations_by_qualifier"]["qualifier"] # Limit query to `ModelQualifier` fields requested in the GraphQL # query. query = apply_requested_fields( info=info, query=query, orm_class=ModelQualifier, fields={"qualifier": fields}, ) # Apply limit (if defined). if limit: query = query.limit(limit=limit) results = query.all() # Wrap the results of the aggregation in `TypeCountCitationsQualifier` # objects. objs = [ TypeCountCitationsQualifier(qualifier=result[0], count_citations=result[1]) for result in results ] return objs