result']['translationstack'][a]) == dict:
                                                res_int += [int(res.json()['esearchresult']['translationstack'][a]['count'])]
                                            elif res.json()['esearchresult']['translationstack'][a] == 'OR':
                                                res_int = [res_int[0]]
                                                res_int += ['null_flag']
                                                return res_int
                            else:
                                for a in range(len(res.json()['esearchresult']['translationstack'])):
                                    if type(res.json()['esearchresult']['translationstack'][a]) == dict:
                                        res_int += [int(res.json()['esearchresult']['translationstack'][a]['count'])]
                                    elif res.json()['esearchresult']['translationstack'][a] == 'OR':
                                        res_int = [res_int[0]]
                                        res_int += ['null_flag']
                                        return res_int
                    else:
                        return [0]*n_terms
            else:
                print('HTTP response status code: ' + str(status_code) + ' for query term string {term}'.format(term=term_str))
        if res_int is None:
            res_int = [0]*n_terms
        return res_int

    @staticmethod
    def multi_normalized_google_distance(name_list, mesh_flags = None):
        """
        returns the normalized Google distance for a list of n MeSH Terms
        :param name_list: a list of strings containing search terms for each node
        :param mesh_flags: a list of boolean values indicating which terms need [MeSH Terms] appended to it.
        :returns: NGD, as a float (or math.nan if any counts are zero, or None if HTTP error)
        """

        if mesh_flags is None:
            mesh_flags = [True]*len(name_list)
        elif len(name_list) != len(mesh_flags):
            print('Warning: mismatching lengths for input lists of names and flags returning None...')
            return None

        search_string='('

        if sum(mesh_flags) == len(mesh_flags):
            search_string += '[MeSH Terms]) AND ('.join(name_list) + '[MeSH Terms])'
            counts = QueryNCBIeUtils.multi_pubmed_hits_count(search_string, n_terms=len(name_list))
        else:
            for a in range(len(name_list)):
                search_string += name_list[a]
                if mesh_flags[a]:
                    search_string += "[MeSH Terms]"
                if a < len(name_list)-1:
                    search_string += ') AND ('
            search_string += ')'
            counts = QueryNCBIeUtils.multi_pubmed_hits_count(search_string, n_terms =1)
            for a in range(len(name_list)):
                name = name_list[a]
                if mesh_flags[a]:
                    name += "[MeSH Terms]"
                counts += QueryNCBIeUtils.multi_pubmed_hits_count(name, n_terms = 1)

        if type(counts[1]) == str:
            if counts[1] == 'null_flag':
                missed_names = [name + '[MeSH Terms]' for name in name_list]
            else:
                missed_names = counts[1:]
            counts = [counts[0]]
            for name in name_list:
                name_decorated = name + '[MeSH Terms]'
                if name_decorated in missed_names:
                    counts += QueryNCBIeUtils.multi_pubmed_hits_count(name, n_terms=1)
                else:
                    counts += QueryNCBIeUtils.multi_pubmed_hits_count(name_decorated, n_terms=1)

        N = 2.7e+7 * 20  # from PubMed home page there are 27 million articles; avg 20 MeSH terms per article
        if None in counts:
            return math.nan
        if 0 in counts:
            return math.nan
        numerator = max([math.log(x) for x in counts[1:]]) - math.log(counts[0])
        denominator = math.log(N) - min([ math.log(x) for x in counts[1:]])
        ngd = numerator/denominator
        return ngd

    @staticmethod
    @CachedMethods.register
    def get_pubmed_from_ncbi_gene(gene_id):
        '''
        Returns a list of pubmed ids associated with a given ncbi gene id
        :param gene_id: A string containing the ncbi gene id
        '''
        res = QueryNCBIeUtils.send_query_get('elink.fcgi',
                                             'db=pubmed&dbfrom=gene&id=' + str(gene_id))
        ret_pubmed_ids = set()
        pubmed_list = None

        if res is not None:
            res_json = res.json()
            res_linksets = res_json.get('linksets', None)
            if res_linksets is not None:
                for res_linkset in res_linksets:
                    res_linksetdbs = res_linkset.get('linksetdbs', None)
                    if res_linksetdbs is not None:
                        for res_linksetdb in res_linksetdbs:
                            res_pubmed_ids = res_linksetdb.get('links', None)
                            if res_pubmed_ids is not None:
                                ret_pubmed_ids |= set(res_pubmed_ids)
        if len(ret_pubmed_ids) > 0:
            pubmed_list = [ str(x) + '[uid]' for x in ret_pubmed_ids]
        return pubmed_list


    @staticmethod
    @CachedMethods.register
    def is_mesh_term(mesh_term):
        ret_list = QueryNCBIeUtils.get_mesh_uids_for_mesh_term(mesh_term)
        return ret_list is not None and len(ret_list) > 0

    @staticmethod
    #@CachedMethods.register
    def get_mesh_terms_for_hp_id(hp_id):
        '''
        This takes a hp id and converts it into a list of mesh term strings with [MeSH Terms] appened to the end
        :param hp_id: a string containing the hp id formatted as follows: "HP:000000"
        '''
        if(hp_id[0])!='"':
            hp_id = '"' + hp_id + '"'
        hp_id+= "[Source ID]"
        res = QueryNCBIeUtils.send_query_get('esearch.fcgi',
                                             'db=medgen&term=' + str(hp_id))
        ret_medgen_ids = set()

        if res is not None:
            res_json = res.json()
            res_result = res_json.get('esearchresult', None)
            if res_result is not None:
                res_idlist = res_result.get('idlist', None)
                if res_idlist is not None:
                    ret_medgen_ids |= set(res_idlist)
        mesh_ids = set()
        for medgen_id in ret_medgen_ids:
            res = QueryNCBIeUtils.send_query_get('elink.fcgi',
                                             'dbfrom=medgen&db=mesh&id=' + str(medgen_id))
            if res is not None:
                res_json = res.json()
                res_linksets = res_json.get('linksets', None)
                if res_linksets is not None:
                    for res_linkset in res_linksets:
                        res_linksetdbs = res_linkset.get('linksetdbs', None)
                        if res_linksetdbs is not None:
                            for res_linksetdb in res_linksetdbs:
                                res_mesh_ids = res_linksetdb.get('links', None)
                                if res_mesh_ids is not None:
                                    mesh_ids |= set(res_mesh_ids)
        mesh_terms = set()
        if len(mesh_ids) > 0:
            for mesh_id in mesh_ids:
                mesh_terms|= set(QueryNCBIeUtils.get_mesh_terms_for_mesh_uid(int(mesh_id)))
        if len(mesh_terms) > 0:
            mesh_terms = [mesh_term + '[MeSH Terms]' for mesh_term in mesh_terms]
            return mesh_terms
        else:
            return None
    
    @staticmethod
    def test_ngd():
        #mesh1_str = 'Anemia, Sickle Cell'
        #mesh2_str = 'Malaria'
        omim1_str = '219700'
        omim2_str = '219550'
        print(QueryNCBIeUtils.normalized_google_distance(mesh1_str, mesh2_str))

    @staticmethod
    @CachedMethods.register
    def get_uniprot_names(id):
        """
        Takes a uniprot id then return a string containing all synonyms listed on uniprot seperated by the deliminator |
        :param id: a string containing the uniprot id
        :returns: a string containing all synonyms uniprot lists for
        """
        # We want the actual uniprot name P176..., not the curie UniProtKB:P176...
        if "UniProtKB:" in id:
            id = ":".join(id.split(":")[1:])
        url = 'https://www.uniprot.org/uniprot/?query=id:' + id + '&sort=score&columns=entry name,protein names,genes&format=tab' # hardcoded url for uniprot data
        requests = CacheControlHelper()
        r = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})  # send get request
        if r.status_code != 200:  # checks for error
            print('HTTP response status code: ' + str(r.status_code) + ' for URL:\n' + url, file=sys.stderr)
            return None
        if r.content.decode('utf-8') == '':
            return None
        df = pandas.read_csv(StringIO(r.content.decode('utf-8')), sep='\t')
        search = df.loc[0, 'Entry name']  # initializes search term variable
        if type(df.loc[0, 'Protein names']) == str:
            for name in re.compile("[()\[\]]").split(df.loc[0, 'Protein names']):  # checks for protein section
                if len(name) > 1:
                    if QueryNCBIeUtils.is_mesh_term(name):
                        search += '|' + name + '[MeSH Terms]'
                    else:
                        search += '|' + name
        if type(df.loc[0, 'Gene names']) == str:
            for name in df.loc[0, 'Gene names'].split(' '):
                if len(name) > 1:
                    if QueryNCBIeUtils.is_mesh_term(name):
                        search += '|' + name + '[MeSH Terms]'
                    else:
                        search += '|' + name
        return search

    @staticmethod
    @CachedMethods.register
    def get_reactome_names(id):
        '''
        Takes a reactome id then return a string containing all synonyms listed on reactome seperated by the deliminator |
        However, If it finds a MeSH terms in the list it will return the search term as a mesh term serach
        e.g. it will return something like '(IGF1R)[MeSH Terms]' 

        This can be inputed into the google function as a non mesh term and will search as a mesh term. 
        This is so that we do not need to handle the output of this function any differently it can all be input as non mesh terms

        Parameters:
            id - a string containing the reactome id

        Output:
            search - a string containing all synonyms of the reactome id or a mesh term formatted for the google distance function
        '''
        # We want the actual reactome name R-HSA..., not the curie REACT:R-HSA...
        if "REACT:" in id:
            id = ":".join(id.split(":")[1:])
        url = 'https://reactome.org/ContentService/data/query/'+id+'/name'  # hardcoded url for reactiome names
        requests = CacheControlHelper()
        r = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})  # sends get request that returns a string
        if r.status_code != 200:
            print('HTTP response status code: ' + str(r.status_code) + ' for URL:\n' + url, file=sys.stderr)
            return None
        nameList = r.text.split('\n')  # splits returned string by line
        search = ''  # initializes search term variable
        for name in nameList:
            if len(name) > 0:  # removes blank lines at beginning and end of response
                if len(re.compile("[()]").split(name)) > 1:  # check for parenthesis
                    for n in re.compile("[()]").split(name):  # splits on either "(" or ")"
                        if len(n) > 0:  # removes banks generated by split
                            if QueryNCBIeUtils.is_mesh_term(n):  # check for mesh term
                                search += '|' + n + '[MeSH Terms]'
                            else:
                                search += '|' + n
                elif len(name.split('ecNumber')) > 1:  # checks for ec number
                    if QueryNCBIeUtils.is_mesh_term(name.split('ecNumber')[0]):
                        search += '|' + name.split('ecNumber')[0] + '[MeSH Terms]'
                    else:
                        search += '|' + name.split('ecNumber')[0]
                    search += '|' + name.split('ecNumber')[1][:-1] + '[EC/RN Number]'  # removes trailing "/" and formats as ec search term
                else:
                    if QueryNCBIeUtils.is_mesh_term(name):
                        search += '|' + name + '[MeSH Terms]'
                    else:
                        search += '|' + name
        search = search[1:]  # removes leading |
        return search

    
    '''Returns a set of mesh ids for a given clinvar id

    '''
    @staticmethod
    @CachedMethods.register
    def get_mesh_id_for_clinvar_uid(clinvar_id):
        # This checks for a straight clinvar id -> mesh id conversion:
        res = QueryNCBIeUtils.send_query_get('elink.fcgi',
                                             'db=mesh&dbfrom=clinvar&id=' + str(clinvar_id))
        res_set = set()
        if res is not None:
            res_json = res.json()
            linksets = res_json.get('linksets', None)
            if linksets is not None:
                link = linksets[0]
                if link is not None:
                    dbs = link.get('linksetdbs', None)
                    if dbs is not None:
                        mesh_db = dbs[0]
                        if mesh_db is not None:
                            ids = mesh_db.get('links', None)
                            res_set |= set([int(uid_str) for uid_str in ids])

        # if there are no mesh ids returned above then this finds clinvar -> medgen -> mesh canversions:
        if len(res_set) == 0:
            res = QueryNCBIeUtils.send_query_get('elink.fcgi',
                                             'db=medgen&dbfrom=clinvar&id=' + str(clinvar_id))
            if res is not None:
                res_json = res.json()
                linksets = res_json.get('linksets', None)
                if linksets is not None:
                    link = linksets[0]
                    if link is not None:
                        dbs = link.get('linksetdbs', None)
                        if dbs is not None:
                            medgen = dbs[0]
                            if medgen is not None:
                                ids = medgen.get('links', None)
                                if ids is not None:
                                    for medgen_id in ids:
                                        res2 = QueryNCBIeUtils.send_query_get('elink.fcgi',
                                            'db=mesh&dbfrom=medgen&id=' + str(medgen_id))
                                        res2_json = res2.json()
                                        linksets2 = res2_json.get('linksets', None)
                                        if linksets2 is not None:
                                            link2 = linksets2[0]
                                            if link2 is not None:
                                                dbs2 = link2.get('linksetdbs', None)
                                                if dbs2 is not None:
                                                    mesh_data = dbs2[0]
                                                    if mesh_data is not None:
                                                        mesh_ids = mesh_data.get('links', None)
                                                        res_set |= set([int(uid_str) for uid_str in mesh_ids])
        return res_set

    @staticmethod
    def multi_pubmed_pmids(term_str, n_terms=1):
        '''
        This is almost the same as the above get_pubmed_hit_counts but is made to work with multi_normalized_google_distance
        '''
        term_str_encoded = urllib.parse.quote(term_str, safe='')
        res = QueryNCBIeUtils.send_query_get('esearch.fcgi',
                                             'db=pubmed&term=' + term_str_encoded)
        if res is None:
            params = {
                'db': 'pubmed',
                'term': term_str
            }
            res = QueryNCBIeUtils.send_query_post('esearch.fcgi',
                                                  params)
        res_pmids = []
        if res is not None:
            status_code = res.status_code
            if status_code == 200:
                res_pmids = [res.json()['esearchresult']['idlist']]
                if n_terms >= 2:
                    if 'errorlist' in res.json()['esearchresult'].keys():
                        if 'phrasesnotfound' in res.json()['esearchresult']['errorlist'].keys():
                            #res_int += res.json()['esearchresult']['errorlist']['phrasesnotfound']
                            pass
                    else:
                        for a in range(len(res.json()['esearchresult']['translationstack'])):
                            if type(res.json()['esearchresult']['translationstack'][a]) == dict:
                                #print(res.json()['esearchresult']['translationstack'][a])  # FIXME this returns a dict that looks like
                                #{'term': '"hypertension"[MeSH Terms]', 'field': 'MeSH Terms', 'count': '250821','explode': 'Y'}
                                #{'term': '"neutropenia"[MeSH Terms]', 'field': 'MeSH Terms', 'count': '18429','explode': 'Y'}
                                #{'term': '"leukopenia"[MeSH Terms]', 'field': 'MeSH Terms', 'count': '36815','explode': 'Y'}
                                #res_pmids += [res.json()['esearchresult']['translationstack'][a]['idlist']]  # FIXME: so can't get an idlist
                                pass
                            elif res.json()['esearchresult']['translationstack'][a] == 'OR':
                                res_pmids = [res_pmids[0]]
                                res_pmids += ['null_flag']
                                return res_pmids

            else:
                print('HTTP response status code: ' + str(status_code) + ' for query term string {term}'.format(
                    term=term_str))
        return res_pmids

    @staticmethod
    def multi_normalized_pmids(name_list, mesh_flags=None):
        """
        returns the normalized Google distance for a list of n MeSH Terms
        :param name_list: a list of strings containing search terms for each node
        :param mesh_flags: a list of boolean values indicating which terms need [MeSH Terms] appended to it.
        :returns: list of pmids
        """

        if mesh_flags is None:
            mesh_flags = [True] * len(name_list)
        elif len(name_list) != len(mesh_flags):
            print('Warning: mismatching lengths for input lists of names and flags returning None...')
            return None

        search_string = '('

        if sum(mesh_flags) == len(mesh_flags):
            search_string += '[MeSH Terms]) AND ('.join(name_list) + '[MeSH Terms])'
            pmids = QueryNCBIeUtils.multi_pubmed_pmids(search_string, n_terms=len(name_list))
        else:
            for a in range(len(name_list)):
                search_string += name_list[a]
                if mesh_flags[a]:
                    search_string += "[MeSH Terms]"
                if a < len(name_list) - 1:
                    search_string += ') AND ('
            search_string += ')'
            pmids = QueryNCBIeUtils.multi_pubmed_pmids(search_string, n_terms=1)
            for a in range(len(name_list)):
                name = name_list[a]
                if mesh_flags[a]:
                    name += "[MeSH Terms]"
                pmids += QueryNCBIeUtils.multi_pubmed_pmids(name, n_terms=1)

        if len(pmids) > 1 and type(pmids[1]) == str:
            if pmids[1] == 'null_flag':
                missed_names = [name + '[MeSH Terms]' for name in name_list]
            else:
                missed_names = pmids[1:]
            pmids = [pmids[0]]
            for name in name_list:
                name_decorated = name + '[MeSH Terms]'
                if name_decorated in missed_names:
                    pmids += QueryNCBIeUtils.multi_pubmed_pmids(name, n_terms=1)
                else:
                    pmids += QueryNCBIeUtils.multi_pubmed_pmids(name_decorated, n_terms=1)

        return pmids


def test_phrase_not_found():
        print('----------')
        print('Result and time for 1st error (joint search):')
        print('----------')
        t0 = time.time()
        print(QueryNCBIeUtils.normalized_google_distance('lymph nodes','IL6'))
        print(time.time() - t0)
        print('----------')
        print('Result and time for 2nd error (individual search):')
        print('----------')
        t0 = time.time()
        print(QueryNCBIeUtils.normalized_google_distance('IL6','lymph nodes[MeSH Terms]',mesh2=False))
        print(time.time() - t0)
        print('Result and time for potential curve ball:')
        print('----------')
        t0 = time.time()
        print(QueryNCBIeUtils.normalized_google_distance('IL6','lymph node[MeSH Terms]|Naprosyn[MeSH Terms]|asdasdjkahfjkaf|flu|cold',mesh2=False))
        print(time.time() - t0)
        print('----------')
        print('Time with no error:')
        print('----------')
        t0 = time.time()
        QueryNCBIeUtils.normalized_google_distance('Naprosyn','lymph nodes')
        print(time.time() - t0)
              
              
if __name__ == '__main__':
    pass
    #print(QueryNCBIeUtils.get_clinvar_uids_for_disease_or_phenotype_string('hypercholesterolemia'))
    #print(QueryNCBIeUtils.get_mesh_uids_for_mesh_term('Anorexia Nervosa'))
    #print(QueryNCBIeUtils.get_mesh_uids_for_mesh_term('Leukemia, Promyelocytic, Acute'))
    #print(QueryNCBIeUtils.get_mesh_uids_for_mesh_term('Leukemia, Myeloid, Acute'))
    
    # for mesh_term in ['Osteoporosis',
    #                   'HIV Infections',
    #                   'Cholera',
    #                   'Ebola Infection',
    #                   'Malaria',
    #                   'Osteomalacia',
    #                   'Hypercholesterolemia',
    #                   'Diabetes Mellitus, Type 2',
    #                   'Asthma',
    #                   'Pancreatitis, Chronic',
    #                   'Alzheimer Disease',
    #                   'Myocardial Infarction',
    #                   'Muscular Dystrophy, Duchenne',
    #                   'NGLY1 protein, human',
    #                   'Alcoholism',
    #                   'Depressive Disorder, Major',
    #                   'Niemann-Pick Disease, Type C',
    #                   'Huntington Disease',
    #                   'Alkaptonuria',
    #                   'Anemia, Sickle Cell',
    #                   'Stress Disorders, Post-Traumatic']:
    #     print(QueryNCBIeUtils.normalized_google_distance(mesh_term, QueryNCBIeUtils.get_mesh_terms_for_omim_id(219700)[0]))
              
    #print(QueryNCBIeUtils.normalized_google_distance(
    #    QueryNCBIeUtils.get_mesh_terms_for_omim_id(219700)[0],
    #    "Cholera"))
    




    #reactome_list = [
    #"R-HSA-5626467",
    #"R-HSA-5627083",
    #"R-HSA-447115",
    #"R-HSA-5579012",
    #"R-HSA-199992",
    #"R-HSA-3000170",
    #"R-HSA-5683371",
    #"R-HSA-5619058",
    #"R-HSA-5579006",
    #"R-HSA-2404192",
    #]
    #
    #for ids in reactome_list:
    #    t0 = time.time()
    #    searchTerm = QueryNCBIeUtils.get_reactome_names(ids)
    #    print(searchTerm)
    #    print(QueryNCBIeUtils.normalized_google_distance(
    #        searchTerm,
    #        'Human',
    #        mesh1 = False
    #        ))
    #    t1 = time.time()
    #    print(t1-t0)
    #
    #uniprot_list = [
    #"Q15699",
    #"A0A0G2JJD3",
    #"Q9NR22",
    #"Q92949",
    #"Q12996",
    #"Q92544",
    #"Q14789",
    #"Q9NRN5",
    #"Q9BXW9",
    #"P56556",
    #"P23219"
    #]
    #
    #for ids in uniprot_list:
    #    t0 = time.time()
    #    searchTerm = QueryNCBIeUtils.get_uniprot_names(ids)
    #    print(searchTerm)
    #    print(QueryNCBIeUtils.normalized_google_distance(
    #        searchTerm,
    #        'Human',
    #        mesh1 = False
    #        ))
    #    t1 = time.time()
    #    print(t1-t0)

    #print(QueryNCBIeUtils.normalized_google_distance("acetaminophen","liver"))
    #print(QueryNCBIeUtils.normalized_google_distance(QueryNCBIeUtils.get_uniprot_names('P23219'), 'Naprosyn', mesh1=False))
    #print(QueryNCBIeUtils.get_mesh_terms_for_mesh_uid(68014059))
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(219700)) # OMIM preferred name: "CYSTIC FIBROSIS"
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(125050)) # OMIM preferred name: "DEAFNESS WITH ANHIDROTIC ECTODERMAL DYSPLASIA"
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(310350)) # OMIM preferred name: "MYELOLYMPHATIC INSUFFICIENCY"
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(603903)) # OMIM preferred name: "SICKLE CELL ANEMIA"
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(612067)) # OMIM preferred name: "DYSTONIA 16; DYT16"
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(615113)) # OMIM preferred name: "MICROPHTHALMIA, ISOLATED 8; MCOP8"
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(615860)) # OMIM preferred name: "CONE-ROD DYSTROPHY 19; CORD19"
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(180200)) # OMIM preferred name: "RETINOBLASTOMA; RB1"
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(617062)) # OMIM preferred name: "OKUR-CHUNG NEURODEVELOPMENTAL SYNDROME; OCNDS"
    # print(QueryNCBIeUtils.get_mesh_terms_for_omim_id(617698)) # OMIM preferred name: "3-METHYLGLUTACONIC ACIDURIA, TYPE IX; MGCA9"
    # print(QueryNCBIeUtils.get_mesh_terms_for_mesh_uid(68003550))
    # print(QueryNCBIeUtils.get_medgen_uid_for_omim_id(219550))
    # print(QueryNCBIeUtils.get_mesh_uid_for_medgen_uid(41393))



# code/reasoningtool/kg-construction/QueryOMIM.py

""" This module defines the class QueryOMIM.
It is written to connect to http://api.omim.org/api, which converts omim id to
gene symbol and uniprot id.
"""

__author__ = ""
__copyright__ = ""
__credits__ = []
__license__ = ""
__version__ = ""
__maintainer__ = ""
__email__ = ""
__status__ = "Prototype"

import sys
# import requests
# import CachedMethods
# import requests_cache
from cache_control_helper import CacheControlHelper
import simplecrypt


class QueryOMIM:
    ENCRYPTED_BYTES = b'sc\x00\x02\xcb@\xces\xda\xd6\xe4\xb4\xba\xd7&=qF\xfe(TB\xb8ax\xdd\xf3\xe4I\xc1\x94ivg\xdbY\x86\xbc\xba\x08\xd6\xee\xc9U\xe1\xf4X*\x8c\x86B.7\xa0qN\xd3\x1c\xe9\xd1\x98\xe6 \xd0\x9f\xb6t\xf9E\n\xaf\xac\x00\xad\xa3\xd5;\xcc]`}\xc6-\xab\x00\x11\xce\x12x\xfc'
    API_BASE_URL = 'https://api.omim.org/api'
    TIMEOUT_SEC = 120

    def __init__(self, encryption_key):
        requests = CacheControlHelper()
        api_key = simplecrypt.decrypt(encryption_key, QueryOMIM.ENCRYPTED_BYTES)
        url = QueryOMIM.API_BASE_URL + "/apiKey"
        session_data = {'apiKey': api_key,
                        'format': 'json'}
        r = requests.post(url, data=session_data)
        assert 200 == r.status_code
        self.cookie = r.cookies

    def send_query_get(self, omim_handler, url_suffix):
        requests = CacheControlHelper()
        url = "{api_base_url}/{omim_handler}?{url_suffix}&format=json".format(api_base_url=QueryOMIM.API_BASE_URL,
                                                                              omim_handler=omim_handler,
                                                                              url_suffix=url_suffix)
#        print(url)
        try:
            res = requests.get(url, cookies=self.cookie)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print("Timeout in QueryOMIM for URL: " + url, file=sys.stderr)
            return None
        except KeyboardInterrupt:
            sys.exit(0)
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryOMIM for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print("Status code " + str(status_code) + " for URL: " + url, file=sys.stderr)
            return None
        return res

    # @CachedMethods.register
    def disease_mim_to_gene_symbols_and_uniprot_ids(self, mim_id):
        """for a given MIM ID for a genetic disease (as input), returns a dict of of gene symbols and UniProt IDs
        {gene_symbols: [gene_symbol_list], uniprot_ids: [uniprot_ids_list]}

        :param mim_id: a string OMIMD ID (of the form 'OMIM:605543')
        :returns: a ``dict`` with two keys; ``gene_symbols`` and ``uniprot_ids``; the entry for each of the
        keys is a ``set`` containing the indicated identifiers (or an empty ``set`` if no such identifiers are available)
        """
        # assert type(mim_id) == str
        if not isinstance(mim_id, str):
            return {'gene_symbols': set(), 'uniprot_ids': set()}
        mim_num_str = mim_id.replace('OMIM:','')
        omim_handler = "entry"
        url_suffix = "mimNumber=" + mim_num_str + "&include=geneMap,externalLinks&exclude=text"
        r = self.send_query_get(omim_handler, url_suffix)
        if r is None:
            return {'gene_symbols': set(),
                    'uniprot_ids': set()}
        result_dict = r.json()
#        print(result_dict)
        result_entry = result_dict["omim"]["entryList"][0]["entry"]
        external_links = result_entry.get('externalLinks', None)
        uniprot_ids = []
        gene_symbols = []
        if external_links is not None:
            uniprot_ids_str = external_links.get("swissProtIDs", None)
            if uniprot_ids_str is not None:
                uniprot_ids = uniprot_ids_str.split(",")
            else:
                phenotype_map_list = result_entry.get("phenotypeMapList", None)
                if phenotype_map_list is not None:
                    gene_symbols = [phenotype_map_list[i]["phenotypeMap"]["geneSymbols"].split(", ")[0] for i in
                                    range(0, len(phenotype_map_list))]
        return {'gene_symbols': set(gene_symbols),
                'uniprot_ids': set(uniprot_ids)}
    
    def disease_mim_to_description(self, mim_id):
        # assert type(mim_id) == str
        if not isinstance(mim_id, str):
            return "None"
        mim_num_str = mim_id.replace('OMIM:', '')
        omim_handler = "entry"
        url_suffix = "mimNumber=" + mim_num_str + "&include=text:description"
        r = self.send_query_get(omim_handler, url_suffix)
        if r is None:
            return "None"
        result_dict = r.json()
        # print(result_dict)
        result_entry = result_dict["omim"]["entryList"][0]["entry"]
        res_description = "None"
        text_section_list = result_entry.get('textSectionList', None)
        if text_section_list is not None and len(text_section_list) > 0:
            res_description_dict = text_section_list[0].get("textSection", None)
            if res_description_dict is not None:
                text_section_content = res_description_dict.get("textSectionContent", None)
                if text_section_content is not None:
                    res_description = text_section_content
        return res_description

if __name__ == '__main__':

    qo = QueryOMIM('1337')
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:145270'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:601351'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:248260'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:608670'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:184400'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:100200'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:111360'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:250300'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:142700'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:312500'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:166710'))
    print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:129905'))
    print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:603903'))
    print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:613074'))
    print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:603918'))  # test issue 1
    print(qo.disease_mim_to_description('OMIM:100100'))
    print(qo.disease_mim_to_description('OMIM:614747'))  # no textSectionContent field
    print(qo.disease_mim_to_description('OMIM:61447'))  # wrong ID
    print(qo.disease_mim_to_description(614747))


# code/reasoningtool/kg-construction/QueryOMIMExtended.py

""" This module defines the class QueryOMIM.
It is written to connect to http://api.omim.org/api, which converts omim id to
gene symbol and uniprot id.
"""

__author__ = ""
__copyright__ = ""
__credits__ = []
__license__ = ""
__version__ = ""
__maintainer__ = ""
__email__ = ""
__status__ = "Prototype"

import sys
# import requests
# import CachedMethods
# import requests_cache
import json
from cache_control_helper import CacheControlHelper


class QueryOMIMExtended:
    API_KEY = ''
    API_BASE_URL = 'https://api.omim.org/api'
    TIMEOUT_SEC = 120
 
    def __init__(self):
        requests = CacheControlHelper()
        url = QueryOMIMExtended.API_BASE_URL + "/apiKey"
        session_data = {'apiKey': QueryOMIMExtended.API_KEY,
                        'format': 'json'}
        r = requests.post(url, data=session_data)
        assert 200 == r.status_code
        self.cookie = r.cookies

    def send_query_get(self, omim_handler, url_suffix):
        requests = CacheControlHelper()
        url = "{api_base_url}/{omim_handler}?{url_suffix}&format=json".format(api_base_url=QueryOMIMExtended.API_BASE_URL,
                                                                              omim_handler=omim_handler,
                                                                              url_suffix=url_suffix)
        #print(url)
        try:
            res = requests.get(url, cookies=self.cookie)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print("Timeout in QueryOMIM for URL: " + url, file=sys.stderr)
            return None
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryOMIM for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print("Status code " + str(status_code) + " for URL: " + url, file=sys.stderr)
            return None
        return res

    # @CachedMethods.register
    def disease_mim_to_gene_symbols_and_uniprot_ids(self, mim_id):
        """for a given MIM ID for a genetic disease (as input), returns a dict of of gene symbols and UniProt IDs
        {gene_symbols: [gene_symbol_list], uniprot_ids: [uniprot_ids_list]}

        :param mim_id: a string OMIMD ID (of the form 'OMIM:605543')
        :returns: a ``dict`` with two keys; ``gene_symbols`` and ``uniprot_ids``; the entry for each of the
        keys is a ``set`` containing the indicated identifiers (or an empty ``set`` if no such identifiers are available)
        """
        assert type(mim_id) == str
        mim_num_str = mim_id.replace('OMIM:','')
        omim_handler = "entry"
        url_suffix = "mimNumber=" + mim_num_str + "&include=geneMap,externalLinks&exclude=text"
        r = self.send_query_get(omim_handler, url_suffix)
        if r is None:
            return {'gene_symbols': set(), 'uniprot_ids': set()}
        result_dict = r.json()
#        print(result_dict)
        result_entry = result_dict["omim"]["entryList"][0]["entry"]
        external_links = result_entry.get('externalLinks', None)
        uniprot_ids = []
        gene_symbols = []
        if external_links is not None:
            uniprot_ids_str = external_links.get("swissProtIDs", None)
            if uniprot_ids_str is not None:
                uniprot_ids = uniprot_ids_str.split(",")
            else:
                phenotype_map_list = result_entry.get("phenotypeMapList", None)
                if phenotype_map_list is not None:
                    gene_symbols = [phenotype_map_list[i]["phenotypeMap"]["geneSymbols"].split(", ")[0] for i in
                                    range(0, len(phenotype_map_list))]
        return {'gene_symbols': set(gene_symbols),
                'uniprot_ids': set(uniprot_ids)}

    def disease_mim_to_description(self, mim_id):
        assert type(mim_id) == str
        mim_num_str = mim_id.replace('OMIM:', '')
        omim_handler = "entry"
        url_suffix = "mimNumber=" + mim_num_str + "&include=text:description"
        r = self.send_query_get(omim_handler, url_suffix)
        if r is None:
            return "None"
        result_dict = r.json()
        # print(result_dict)
        result_entry = result_dict["omim"]["entryList"][0]["entry"]
        res_description = "None"
        text_section_list = result_entry.get('textSectionList', None)
        if text_section_list is not None and len(text_section_list) > 0:
            res_description_dict = text_section_list[0].get("textSection", None)
            if res_description_dict is not None:
                text_section_content = res_description_dict.get("textSectionContent", None)
                if text_section_content is not None:
                    res_description = text_section_content
        return res_description


if __name__ == '__main__':

    def save_to_test_file(filename, key, value):
        f = open(filename, 'r+')
        try:
            json_data = json.load(f)
        except ValueError:
            json_data = {}
        f.seek(0)
        f.truncate()
        json_data[key] = value
        json.dump(json_data, f)
        f.close()

    qo = QueryOMIMExtended()
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:145270'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:601351'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:248260'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:608670'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:184400'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:100200'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:111360'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:250300'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:142700'))
    # print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:312500'))
    print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:166710'))
    print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:129905'))
    print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:603903'))
    print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:613074'))
    print(qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:603918'))  # test issue 1
    print(qo.disease_mim_to_description('OMIM:100100'))
    print(qo.disease_mim_to_description('OMIM:614747'))  # no textSectionContent field
    print(qo.disease_mim_to_description('OMIM:61447'))  # wrong ID
    save_to_test_file('tests/query_desc_test_data.json', 'OMIM:100100', qo.disease_mim_to_description('OMIM:100100'))
    save_to_test_file('tests/query_desc_test_data.json', 'OMIM:614747', qo.disease_mim_to_description('OMIM:614747'))
    save_to_test_file('tests/query_desc_test_data.json', 'OMIM:61447', qo.disease_mim_to_description('OMIM:61447'))



# code/reasoningtool/kg-construction/QueryPC2.py

""" This module defines the class QueryPC2. It is designed to connect to
http://www.pathwaycommons.org/pc2, querying uniprot id and pathway id mutually.
"""

__author__ = 'Stephen Ramsey'
__copyright__ = 'Oregon State University'
__credits__ = ['Stephen Ramsey', 'Zheng Liu', 'Yao Yao']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'

# import requests
import sys
from cache_control_helper import CacheControlHelper


class QueryPC2:
    TIMEOUT_SEC = 120
    API_BASE_URL = 'http://www.pathwaycommons.org/pc2'

    @staticmethod
    def send_query_get(handler, url_suffix):
        requests = CacheControlHelper()
        url_str = QueryPC2.API_BASE_URL + "/" + handler + "?" + url_suffix
#        print(url_str)
        try:
            res = requests.get(url_str, timeout=QueryPC2.TIMEOUT_SEC)
        except requests.exceptions.Timeout:
            # print(url, file=sys.stderr)
            # print('Timeout in QueryPC2 for URL: ' + url, file=sys.stderr)
            return None
        except BaseException as e:
            print(url_str, file=sys.stderr)
            print('%s received in QueryPC2 for URL: %s' % (e, url_str), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            # print(url, file=sys.stderr)
            # print('Status code ' + str(status_code) + ' for url: ' + url, file=sys.stderr)
            return None
        return res

    @staticmethod
    def pathway_id_to_uniprot_ids(pathway_reactome_id):
        query_str = "uri=http://identifiers.org/reactome/" + pathway_reactome_id + "&format=TXT"
        res = QueryPC2.send_query_get("get", query_str)
        res_set = set()
        if res is not None:
            start_capturing = False
            res_text = res.text
            for line_str in res_text.splitlines():
                if start_capturing:
                    unification_xref_str = line_str.split("\t")[3]
                    unification_xref_namevals = unification_xref_str.split(";")
                    for unification_xref_nameval in unification_xref_namevals:
                        unification_xref_nameval_fields = unification_xref_nameval.split(":")
                        if unification_xref_nameval_fields[0] == "uniprot knowledgebase":
                            res_set.add(unification_xref_nameval_fields[1])
                            if line_str.split("\t")[0] == "PARTICIPANT":
                                start_capturing = True
        return res_set

    @staticmethod
    def uniprot_id_to_reactome_pathways(uniprot_id):
        res = QueryPC2.send_query_get("search.json", "q=" + uniprot_id + "&type=pathway")
        res_dict = res.json()
        if res is not None:
            search_hits = res_dict["searchHit"]
            pathway_list = [item.split("http://identifiers.org/reactome/")[1] for i in range(0, len(search_hits)) for item
                            in search_hits[i]["pathway"]]
        return set(pathway_list)


if __name__ == '__main__':
    print(QueryPC2.uniprot_id_to_reactome_pathways("P68871"))
    print(QueryPC2.pathway_id_to_uniprot_ids("R-HSA-2168880"))


# code/reasoningtool/kg-construction/QueryPharos.py

""" This module defines the class QueryPharos which connects to APIs at
https://pharos.ncats.io/idg/api/v1, querying information correspondances among
drugs, dieases, targets.
"""

__author__ = ""
__copyright__ = ""
__credits__ = []
__license__ = ""
__version__ = ""
__maintainer__ = ""
__email__ = ""
__status__ = "Prototype"

# import requests
from cache_control_helper import CacheControlHelper
import CachedMethods
import sys


class QueryPharos:

    API_BASE_URL = 'https://pharos.ncats.io/idg/api/v1'

    @staticmethod
    def send_query_get(entity, url_suffix):
        requests = CacheControlHelper()
        url_str = QueryPharos.API_BASE_URL + "/" + entity + url_suffix
        #print(url_str)

        try:
            res = requests.get(url_str)
        except requests.exceptions.Timeout:
            print(url_str, file=sys.stderr)
            print('Timeout in QueryMiRBase for URL: ' + url_str, file=sys.stderr)
            return None
        except KeyboardInterrupt:
            sys.exit(0)
        except BaseException as e:
            print(url_str, file=sys.stderr)
            print('%s received in QueryMiRBase for URL: %s' % (e, url), file=sys.stderr)
            return None

        status_code = res.status_code
        #print("Status code="+str(status_code))
        assert status_code in [200, 404]
        if status_code == 404:
            res = None
        return res


    @staticmethod
    @CachedMethods.register
    def query_drug_name_to_targets(drug_name):
        ret_ids = []
        res = QueryPharos.send_query_get("targets","/search?q="+drug_name+"&facet=IDG%20Development%20Level%2FTclin")
        if res is not None:
            res_json = res.json()
            #print(res_json)
            if type(res_json)==dict:
                res_content = res_json['content']
                #print(res_content)
                for content_entry in res_content:
                    ret_ids.append( { 'id': content_entry['id'], 'name': content_entry['name'] } )
        return ret_ids


    @staticmethod
    @CachedMethods.register
    def query_target_to_diseases(target_id):
        ret_ids = []
        res = QueryPharos.send_query_get("targets","("+target_id+")/links(kind=ix.idg.models.Disease)")
        if res is not None:
            res_json = res.json()
            #print(res_json)
            if type(res_json) == list:
                for res_entry in res_json:
                    id = res_entry['refid']
                    name = None
                    if res_entry['properties'] is not None:
                        #print(res_entry['properties'])
                        for res_property in res_entry['properties']:
                            if res_property['label'] == 'IDG Disease':
                                name = res_property['term']
                    ret_ids.append( { 'id': id, 'name': name } )
        return ret_ids


    @staticmethod
    @CachedMethods.register
    def query_target_to_drugs(target_id):
        ret_ids = []
        res = QueryPharos.send_query_get("targets","("+target_id+")/links(kind=ix.idg.models.Ligand)")
        if res is not None:
            res_json = res.json()
            #print(res_json)
            if type(res_json)==list:
                for res_entry in res_json:
                    if res_entry['properties'] is not None:
                        #print(res_entry['properties'])
                        name = None
                        action = None
                        id = None
                        for res_property in res_entry['properties']:
                            if res_property['label'] == 'IDG Ligand':
                                name = res_property['term']
                                id = res_property['id']
                            if res_property['label'] == 'Pharmalogical Action':
                                action = res_property['term']
                        if name is not None and action is not None:
                            ret_ids.append( { 'id': id, 'name': name, 'action': action } )
        return ret_ids


    @staticmethod
    @CachedMethods.register
    def query_drug_to_targets(drug_id):
        ret_ids = []
        res = QueryPharos.send_query_get("ligands","("+drug_id+")/links(kind=ix.idg.models.Target)")
        if res is not None:
            res_json = res.json()
            #print(res_json)
            if type(res_json)==list:
                for res_entry in res_json:
                    if res_entry['properties'] is not None:
                        #print(res_entry['properties'])
                        name = None
                        id = None
                        for res_property in res_entry['properties']:
                            if res_property['label'] == 'IDG Target':
                                name = res_property['term']
                                id = res_property['id']
                        if name is not None and id is not None:
                            ret_ids.append( { 'id': id, 'name': name } )
            if type(res_json)==dict:
                if res_json['properties'] is not None:
                    #print(res_json['properties'])
                    name = None
                    id = None
                    for res_property in res_json['properties']:
                        if res_property['label'] == 'IDG Target':
                            name = res_property['term']
                            id = res_property['id']
                    if name is not None and id is not None:
                        ret_ids.append( { 'id': id, 'name': name } )
        return ret_ids


    @staticmethod
    @CachedMethods.register
    def query_target_name(target_id):
        res = QueryPharos.send_query_get("targets","("+target_id+")")
        if res is not None:
            res_json = res.json()
            #print(res_json)
            if type(res_json)==dict:
                ret_value = res_json['name']
            else:
                ret_value = None
        else:
            ret_value = None
        return ret_value


    @staticmethod
    @CachedMethods.register
    def query_target_uniprot_accession(target_id):
        res = QueryPharos.send_query_get("targets","("+target_id+")/synonyms")
        if res is not None:
            res_json = res.json()
            #print(res_json)
            if type(res_json)==list:
                for res_entry in res_json:
                    if res_entry['label'] == 'UniProt Accession':
                        return res_entry['term']   # return the first one of many
        return None


    @staticmethod
    @CachedMethods.register
    def query_disease_name(disease_id):
        res = QueryPharos.send_query_get("diseases","("+disease_id+")")
        if res is not None:
            res_json = res.json()
            #print(res_json)
            if type(res_json)==dict:
                ret_value = res_json['name']
            else:
                ret_value = None
        else:
            ret_value = None
        return ret_value


    @staticmethod
    @CachedMethods.register
    def query_drug_name(ligand_id):
        res = QueryPharos.send_query_get("ligands","("+ligand_id+")")
        if res is not None:
            res_json = res.json()
            #print(res_json)
            if type(res_json)==dict:
                ret_value = res_json['name']
            else:
                ret_value = None
        else:
            ret_value = None
        return ret_value


    @staticmethod
    @CachedMethods.register
    def query_drug_id_by_name(drug_name):
        res = QueryPharos.send_query_get("ligands","/search?q="+drug_name)
        if res is not None:
            res_json = res.json()
            #print(res_json)
            ret_value = None
            if type(res_json)==dict:
                res_content = res_json['content']
                for content_entry in res_content:
                    #print(content_entry)
                    if content_entry['name'] == drug_name:
                        ret_value = content_entry['id']
            else:
                ret_value = None
        else:
            ret_value = None
        return ret_value


    @staticmethod
    @CachedMethods.register
    def query_disease_id_by_name(disease_name):
        res = QueryPharos.send_query_get("diseases","/search?q="+disease_name)
        if res is not None:
            res_json = res.json()
            #print(res_json)
            ret_value = None
            if type(res_json)==dict:
                res_content = res_json['content']
                for content_entry in res_content:
                    #print(content_entry)
                    if content_entry['name'] == disease_name:
                        ret_value = content_entry['id']
        return ret_value

if __name__ == '__main__':
    print(QueryPharos.query_drug_id_by_name('paclitaxel'))
    print(QueryPharos.query_drug_name_to_targets('paclitaxel'))
    print(QueryPharos.query_drug_to_targets("254599"))
    print(QueryPharos.query_drug_id_by_name('clothiapine'))
    print(QueryPharos.query_drug_id_by_name("lovastatin"))
    print(QueryPharos.query_drug_to_targets("254599"))
    print(QueryPharos.query_drug_name_to_targets("lovastatin"))


# code/reasoningtool/kg-construction/QueryPubChem.py

__author__ = 'Stephen Ramsey'
__copyright__ = 'Oregon State University'
__credits__ = ['Stephen Ramsey', 'Finn Womack']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'

import urllib
import pandas
# import requests
import sys
import time
import math
from io import StringIO
import re
import os
import CachedMethods
# import requests_cache
import json
from cache_control_helper import CacheControlHelper


class QueryPubChem:
    API_BASE_URL = 'https://pubchem.ncbi.nlm.nih.gov/rest/pug'
    TIMEOUT_SEC = 120
    HANDLER_MAP = {
        'get_pubchem_cid':      'substance/sid/{sid}/JSON',
        'get_description_url':  'compound/cid/{cid}/description/JSON'
    }

    @staticmethod
    def __access_api(handler):
        requests = CacheControlHelper()
        url = QueryPubChem.API_BASE_URL + '/' + handler
        # print(url)
        try:
            res = requests.get(url, timeout=QueryPubChem.TIMEOUT_SEC)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print('Timeout in QueryPubChem for URL: ' + url, file=sys.stderr)
            return None
        except KeyboardInterrupt:
            sys.exit(0)
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryPubChem for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print(url, file=sys.stderr)
            print('Status code ' + str(status_code) + ' for url: ' + url, file=sys.stderr)
            return None
        return res.json()

    @staticmethod
    def send_query_get(handler, url_suffix):
        requests = CacheControlHelper()
        url = QueryPubChem.API_BASE_URL + '/' + handler + '/' + url_suffix
        # print(url)
        try:
            res = requests.get(url, timeout=QueryPubChem.TIMEOUT_SEC)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print('Timeout in QueryPubChem for URL: ' + url, file=sys.stderr)
            return None
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryPubChem for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print(url, file=sys.stderr)
            print('Status code ' + str(status_code) + ' for url: ' + url, file=sys.stderr)
            return None
        return res.json()

    @staticmethod
    def get_chembl_ids_for_drug(drug_name):
        drug_name_safe = urllib.parse.quote(drug_name, safe='')
        res = QueryPubChem.send_query_get(handler='compound/name',
                                          url_suffix=drug_name_safe + '/synonyms/JSON')
        res_chembl_set = set()
        if res is not None:
            information_list_dict = res.get('InformationList', None)
            if information_list_dict is not None:
                information_list = information_list_dict.get('Information', None)
                if information_list is not None:
                    for information_dict in information_list:
                        synonyms = information_dict.get('Synonym', None)
                        if synonyms is not None:
                            for syn in synonyms:
                                if syn.startswith('CHEMBL'):
                                    res_chembl_set.add(syn)
                                    # res_chembl_set.add('ChEMBL:' + syn.replace('CHEMBL', ''))
        return res_chembl_set

    # @staticmethod
    # def test():
    #     print(QueryPubChem.get_chembl_ids_for_drug('gne-493'))
    #     print(QueryChEMBL.get_target_uniprot_ids_for_drug('clothiapine'))

    @staticmethod
    # @CachedMethods.register
    def get_pubchem_id_for_chembl_id(chembl_id):
        """This takes a chembl id and then looks up the corresponding pubchem id from a pre-generated .tsv

        NOTE: pubchem-chembl mappings .tsv generated using https://pubchem.ncbi.nlm.nih.gov/idexchange/idexchange.cgi
        it took ~3 or so seconds to map all ids in the KG (2226 ids) and not all ids were successful (missed 204 terms -> ~91% success rate)
        """
        dir_path = os.path.dirname(os.path.realpath(__file__))
        df = pandas.read_csv(dir_path + '/chemblMap.tsv', sep='\t', index_col=0, header=None)
        try:
            ans = df.loc[chembl_id].iloc[0]
        except KeyError:
            return None
        if math.isnan(ans):
            return None
        else:
            return str(int(ans))

    @staticmethod
    # @CachedMethods.register
    def get_pubmed_id_for_pubchem_id(pubchem_id):
        """
        This takes a PubChem id and then gets the PMIDs for articles on PubMed from PubChem which include this entity.
        """
        if not isinstance(pubchem_id, str):
            return None

        requests = CacheControlHelper()
        url = 'https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/' + str(pubchem_id) + '/xrefs/PubMedID/JSON'
        try:
            r = requests.get(url, timeout=10)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print('Timeout in QueryPubChem for URL: ' + url, file=sys.stderr)
            return None
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryPubChem for URL: %s' % (e, url), file=sys.stderr)
            return None
        if r is not None:
            if 'Fault' in r.json().keys():
                return None
            else:
                ans = [str(x) + '[uid]' for x in r.json()['InformationList']['Information'][0]['PubMedID']]
                return ans
        else:
            return None

    @staticmethod
    def get_pubchem_cid(pubchem_sid):
        pubchem_cid = None
        if not isinstance(pubchem_sid, str):
            return pubchem_cid
        handler = QueryPubChem.HANDLER_MAP['get_pubchem_cid'].format(sid=pubchem_sid)
        res = QueryPubChem.__access_api(handler)
        if res is not None:
            if 'PC_Substances' in res.keys():
                substance = res['PC_Substances'][0]
                if len(substance) > 0:
                    if 'compound' in substance.keys():
                        compounds = substance['compound']
                        if len(compounds) > 1:
                            compound = compounds[1]
                            if 'id' in compound.keys():
                                obj = compound['id']
                                if 'id' in obj.keys():
                                    id_obj = obj['id']
                                    if 'cid' in id_obj.keys():
                                        pubchem_cid = str(id_obj['cid'])
        return pubchem_cid


    @staticmethod
    def get_description_url_from_cid(pubchem_cid):
        """	query the description URL from HMDB
        Args:
            pubchem_cid (str): PubChem CID, e.g. 123689
        Returns:
            desc_url (str): the URL of HMDB website, which contains the description of the compound
        """
        res_url = None
        if not isinstance(pubchem_cid, str):
            return res_url
        handler = QueryPubChem.HANDLER_MAP['get_description_url'].format(cid=pubchem_cid)
        res = QueryPubChem.__access_api(handler)
        if res is not None:
            if 'InformationList' in res.keys():
                info_list = res['InformationList']
                if 'Information' in info_list.keys():
                    infos = info_list['Information']
                    for info in infos:
                        if 'DescriptionSourceName' in info.keys() and 'DescriptionURL' in info.keys():
                            if info['DescriptionSourceName'] == "Human Metabolome Database (HMDB)":
                                return info['DescriptionURL']
        return res_url


    @staticmethod
    def get_description_url(pubchem_sid):
        res_url = None
        if not isinstance(pubchem_sid, str):
            return res_url
        pubchem_cid = QueryPubChem.get_pubchem_cid(pubchem_sid)
        if pubchem_cid is not None:
            res_url = QueryPubChem.get_description_url_from_cid(pubchem_cid)
        return res_url

if __name__ == '__main__':
    print(QueryPubChem.get_chembl_ids_for_drug('gne-493'))
    print(QueryPubChem.get_pubchem_id_for_chembl_id('CHEMBL521'))
    print(QueryPubChem.get_pubchem_id_for_chembl_id('chembl521'))
    print(QueryPubChem.get_pubchem_id_for_chembl_id('3400'))
    print(QueryPubChem.get_pubmed_id_for_pubchem_id('3672'))
    print(QueryPubChem.get_pubmed_id_for_pubchem_id('3500'))
    print(QueryPubChem.get_pubmed_id_for_pubchem_id('3400'))
    print(QueryPubChem.get_description_url('6921'))
    print(QueryPubChem.get_description_url('3500'))
    print(QueryPubChem.get_description_url('3400'))
    print(QueryPubChem.get_description_url(3400))
    print(QueryPubChem.get_description_url('3324'))



# code/reasoningtool/kg-construction/QueryReactome.py

""" This module defines the class QueryReactome. QueryReactome is written to
connect with APIs at https://reactome.org/ContentService, querying the pathway
information.
"""

__author__ = 'Stephen Ramsey'
__copyright__ = 'Oregon State University'
__credits__ = ['Stephen Ramsey', 'Yao Yao', 'Zheng Liu']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'

# import requests
import sys
import re
# import requests_cache
import json
from cache_control_helper import CacheControlHelper


class QueryReactome:

    API_BASE_URL = 'https://reactome.org/ContentService'

    TIMEOUT_SEC = 120

    HANDLER_MAP = {
        'get_pathway': 'data/pathway/{id}/containedEvents',
        'get_pathway_desc': 'data/query/{id}'
    }

    SPECIES_MNEMONICS = ['BOVIN',
                         'ACAVI',
                         'VACCW',
                         'PLAVS',
                         'CHICK',
                         'ECOLI',
                         'HORSE',
                         'MAIZE',
                         'MOUSE',
                         'PEA',
                         'PIG',
                         'RABIT',
                         'RAT',
                         'SHEEP',
                         'SOYBN',
                         'TOBAC',
                         'WHEAT',
                         'YEAST',
                         'HV1N5',
                         'HV1H2',
                         'DANRE',
                         'XENLA',
                         'MYCTU',
                         'HHV8P',
                         'HTLV2',
                         'HHV1',
                         'HPV16',
                         '9HIV1',
                         'EBVB9',
                         'PROBE',
                         'HTL1C',
                         'I72A2',
                         'SV40',
                         'HV1B1',
                         'SCHPO',
                         'RUBV',
                         'MUS']
    
    @staticmethod
    def send_query_get(handler, url_suffix):
        requests = CacheControlHelper()
        url_str = QueryReactome.API_BASE_URL + '/' + handler + '/' + url_suffix
#        print(url_str)
        try:
            res = requests.get(url_str, timeout=QueryReactome.TIMEOUT_SEC)
        except KeyboardInterrupt:
            sys.exit(0)
        except BaseException as e:
            print('%s received in QueryReactome for URL: %s' % (e, url_str), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print('Status code ' + str(status_code) + ' for url: ' + url_str, file=sys.stderr)
            return None
        return res

    @staticmethod
    def __query_uniprot_to_reactome_entity_id(uniprot_id):
        res = QueryReactome.send_query_get('data/complexes/UniProt', uniprot_id)
        if res is not None:
            res_json = res.json()
            #        print(res_json)
            if type(res_json) == list:
                ret_ids = set([res_entry['stId'] for res_entry in res_json])
            else:
                ret_ids = set()
        else:
            ret_ids = set()
        return ret_ids

    @staticmethod
    def __query_uniprot_to_reactome_entity_id_desc(uniprot_id):
        res = QueryReactome.send_query_get('data/complexes/UniProt', uniprot_id)
        ret_ids = dict()
        if res is not None:
            res_json = res.json()
            if type(res_json) == list:
                for res_entry in res_json:
                    entity_id = res_entry['stId']
                    if 'R-HSA-' in entity_id:
                        ret_ids[entity_id] = res_entry['displayName']
                    else:
                        print('Non-human reactome entity ID: ' + entity_id + ' from Uniprot ID: ' + uniprot_id)
        return ret_ids

    @staticmethod
    def __query_reactome_entity_id_to_reactome_pathway_ids_desc(reactome_entity_id):
        res = QueryReactome.send_query_get('data/pathways/low/diagram/entity', reactome_entity_id + '/allForms?species=9606')
        reactome_ids_dict = dict()
        if res is not None:
            res_json = res.json()
            for res_entry in res_json:
                res_id = res_entry['stId']
                if 'R-HSA-' in res_id:
                    reactome_ids_dict[res_id] = res_entry['displayName']
                else:
                    print('Non-human Reactome pathway: ' + res_id + ' from reactome entity ID: ' + reactome_entity_id, file=sys.stderr)
        else:
            reactome_ids_dict = dict()
        return reactome_ids_dict

    ## called from BioNetExpander
    @staticmethod
    def query_uniprot_id_to_reactome_pathway_ids_desc(uniprot_id):
        reactome_entity_ids = QueryReactome.__query_uniprot_to_reactome_entity_id(uniprot_id)
        res_dict = dict()
        for reactome_entity_id in reactome_entity_ids:
            if 'R-HSA-' in reactome_entity_id:
                pathway_ids_dict = QueryReactome.__query_reactome_entity_id_to_reactome_pathway_ids_desc(reactome_entity_id)
                if len(pathway_ids_dict) > 0:
                    res_dict.update(pathway_ids_dict)
                else:
                    print('Non-human Reactome entity: ' + reactome_entity_id + ' from Uniprot ID: ' + uniprot_id, file=sys.stderr)
        return res_dict
    
    ## called from BioNetExpander
    @staticmethod
    def query_reactome_pathway_id_to_uniprot_ids_desc(reactome_pathway_id):
        res = QueryReactome.send_query_get('data/participants', reactome_pathway_id)
        ret_dict = dict()
        if res is not None:
            res_json = res.json()
            #        print(res_json)
            participant_ids_list = [refEntity['displayName'] for peDbEntry in res_json for refEntity in peDbEntry['refEntities']]
            for participant_id in participant_ids_list:
                if 'UniProt:' in participant_id:
                    uniprot_id = participant_id.split(' ')[0].split(':')[1]
                    if ' ' in participant_id:
                        prot_desc = participant_id.split(' ')[1]
                    else:
                        prot_desc = 'UNKNOWN'
                    if '-' in uniprot_id:
                        uniprot_id = uniprot_id.split('-')[0]
                    ret_dict[uniprot_id] = prot_desc
#            uniprot_ids_list = [[id.split(' ')[0].split(':')[1], id.split(' ')[1]] for id in participant_ids_list if 'UniProt:' in id]
        return ret_dict

    @staticmethod
    def is_valid_uniprot_accession(accession_str):
        return re.match("[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2}", accession_str) is not None

    # called from BioNetExpander
    @staticmethod
    def query_uniprot_id_to_interacting_uniprot_ids_desc(uniprot_id):
        res = QueryReactome.send_query_get('interactors/static/molecule', uniprot_id + '/details')
        res_uniprot_ids = dict()
        if res is not None:
            res_json = res.json()
#            print(res_json)
            if res_json is not None:
                res_entities = res_json.get('entities', None)
                if res_entities is not None:
                    for res_entity in res_entities:
                        res_entity_interactors = res_entity.get('interactors', None)
                        if res_entity_interactors is not None:
                            for res_entity_interactor in res_entity_interactors:
                                int_uniprot_id = res_entity_interactor.get('acc', None)
                                if int_uniprot_id is not None:
                                    if 'CHEBI:' not in int_uniprot_id:
                                        if '-' in int_uniprot_id:
                                            int_uniprot_id = int_uniprot_id.split('-')[0]
                                        int_alias = res_entity_interactor.get('alias', '')
                                        alt_species = None
                                        if ' ' in int_alias:
                                            int_alias_split = int_alias.split(' ')
                                            int_alias = int_alias_split[0]
                                            alt_species = int_alias_split[1]
                                        if alt_species is None or (alt_species not in QueryReactome.SPECIES_MNEMONICS and \
                                                                   not (alt_species[0] == '9')):
                                            if alt_species is not None:
                                                if 'DNA' in int_alias_split or \
                                                   'DNA-PROBE' in int_alias_split or \
                                                   'DSDNA' in int_alias_split or \
                                                   'GENE' in int_alias_split or \
                                                   'PROMOTE' in int_alias_split or \
                                                   'PROMOTER' in int_alias_split or \
                                                   any(['-SITE' in alias_element for alias_element in int_alias_split]) or \
                                                   any(['BIND' in alias_element for alias_element in int_alias_split]):                                               
                                                    target_gene_symbol = int_alias_split[0]
                                                    int_alias = 'BINDSGENE:' + int_alias_split[0]
                                                else:
                                                    print('For query protein ' + uniprot_id + ' and interactant protein ' + int_uniprot_id + ', check for potential other species name in Reactome output: ' + alt_species, file=sys.stderr)
                                                    int_alias = None
                                        if int_alias is not None and int_alias != "" and QueryReactome.is_valid_uniprot_accession(int_uniprot_id):
                                                res_uniprot_ids[int_uniprot_id] = int_alias
        return res_uniprot_ids

    @staticmethod
    def __access_api(handler):

        requests = CacheControlHelper()
        url = QueryReactome.API_BASE_URL + '/' + handler

        try:
            res = requests.get(url, timeout=QueryReactome.TIMEOUT_SEC)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print('Timeout in QueryReactome for URL: ' + url, file=sys.stderr)
            return None
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryReactome for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print(url, file=sys.stderr)
            print('Status code ' + str(status_code) + ' for url: ' + url, file=sys.stderr)
            return None

        return res.text

    @staticmethod
    def __get_entity(entity_type, entity_id):
        handler = QueryReactome.HANDLER_MAP[entity_type].format(id=entity_id)
        results = QueryReactome.__access_api(handler)
        result_str = 'None'
        if results is not None:
            #   remove all \n characters using json api and convert the string to one line
            json_dict = json.loads(results)
            result_str = json.dumps(json_dict)
        return result_str

    @staticmethod
    def __get_desc(entity_type, entity_id):
        handler = QueryReactome.HANDLER_MAP[entity_type].format(id=entity_id)
        results = QueryReactome.__access_api(handler)
        result_str = 'None'
        if results is not None:
            #   remove all \n characters using json api and convert the string to one line
            json_dict = json.loads(results)
            if 'summation' in json_dict.keys():
                summation = json_dict['summation']
                if len(summation) > 0:
                    if 'text' in summation[0].keys():
                        result_str = summation[0]['text']
        return result_str

    @staticmethod
    #   example of pathway_id: REACT:R-HSA-70326
    def get_pathway_entity(pathway_id):
        if not isinstance(pathway_id, str):
            return 'None'
        if pathway_id[:6] == "REACT:":
            pathway_id = pathway_id[6:]
        return QueryReactome.__get_entity("get_pathway", pathway_id)

    @staticmethod
    #   example of pathway_id: Reactome:R-HSA-70326
    def get_pathway_desc(pathway_id):
        if not isinstance(pathway_id, str):
            return 'None'
        if pathway_id[:6] == "REACT:":
            pathway_id = pathway_id[6:]
        return QueryReactome.__get_desc("get_pathway_desc", pathway_id)

    @staticmethod
    def test():
        print(QueryReactome.query_uniprot_id_to_interacting_uniprot_ids_desc("P62991"))
        print(QueryReactome.is_valid_uniprot_accession("Q16665"))
        print(QueryReactome.is_valid_uniprot_accession("EBI"))
        print(QueryReactome.query_uniprot_id_to_interacting_uniprot_ids_desc("Q16665"))
        print(QueryReactome.query_uniprot_id_to_interacting_uniprot_ids_desc('P04150'))
        print(QueryReactome.query_uniprot_id_to_interacting_uniprot_ids_desc('Q06609'))
        print(QueryReactome.query_uniprot_id_to_interacting_uniprot_ids_desc('Q13501'))
        print(QueryReactome.query_uniprot_id_to_interacting_uniprot_ids_desc('P68871'))
        print(QueryReactome.query_uniprot_id_to_interacting_uniprot_ids_desc('O75521-2'))
        print(QueryReactome.query_reactome_pathway_id_to_uniprot_ids_desc('R-HSA-5423646'))
        print(QueryReactome.query_uniprot_id_to_interacting_uniprot_ids_desc("P68871"))
        print(QueryReactome.__query_uniprot_to_reactome_entity_id('O75521-2'))
        print(QueryReactome.__query_reactome_entity_id_to_reactome_pathway_ids_desc('R-HSA-2230989'))
        print(QueryReactome.__query_uniprot_to_reactome_entity_id_desc('P68871'))


if __name__ == '__main__':
    QueryReactome.test()

    def save_to_test_file(filename, key, value):
        f = open(filename, 'r+')
        try:
            json_data = json.load(f)
        except ValueError:
            json_data = {}
        f.seek(0)
        f.truncate()
        json_data[key] = value
        json.dump(json_data, f)
        f.close()

    save_to_test_file('tests/query_test_data.json', 'REACT:R-HSA-70326', QueryReactome.get_pathway_entity('REACT:R-HSA-70326'))
    print(QueryReactome.get_pathway_desc('REACT:R-HSA-70326'))


# code/reasoningtool/kg-construction/QueryReactomeExtended.py

''' This module defines the class QueryReactomeExtended. QueryReactomeExtended class is designed
to communicate with Reactome APIs and their corresponding data sources. The available methods include:
    * query pathway entity by ID
'''


__author__ = 'Deqing Qu'
__copyright__ = 'Oregon State University'
__credits__ = ['Deqing Qu', 'Stephen Ramsey']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'

# import requests
# import requests_cache
import sys
import json
from cache_control_helper import CacheControlHelper


class QueryReactomeExtended:
    TIMEOUT_SEC = 120
    API_BASE_URL = 'https://reactome.org/ContentService'
    HANDLER_MAP = {
        'get_pathway': 'data/pathway/{id}/containedEvents',
        'get_pathway_desc': 'data/query/{id}'
    }

    @staticmethod
    def __access_api(handler):

        requests = CacheControlHelper()
        url = QueryReactomeExtended.API_BASE_URL + '/' + handler

        try:
            res = requests.get(url, timeout=QueryReactomeExtended.TIMEOUT_SEC)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print('Timeout in QueryReactome for URL: ' + url, file=sys.stderr)
            return None
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryReactome for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print(url, file=sys.stderr)
            print('Status code ' + str(status_code) + ' for url: ' + url, file=sys.stderr)
            return None

        return res.text

    @staticmethod
    def __get_entity(entity_type, entity_id):
        handler = QueryReactomeExtended.HANDLER_MAP[entity_type].format(id=entity_id)
        results = QueryReactomeExtended.__access_api(handler)
        result_str = 'None'
        if results is not None:
            #   remove all \n characters using json api and convert the string to one line
            json_dict = json.loads(results)
            result_str = json.dumps(json_dict)
        return result_str

    @staticmethod
    def __get_desc(entity_type, entity_id):
        handler = QueryReactomeExtended.HANDLER_MAP[entity_type].format(id=entity_id)
        results = QueryReactomeExtended.__access_api(handler)
        result_str = 'None'
        if results is not None:
            #   remove all \n characters using json api and convert the string to one line
            json_dict = json.loads(results)
            if 'summation' in json_dict.keys():
                summation = json_dict['summation']
                if len(summation) > 0:
                    if 'text' in summation[0].keys():
                        result_str = summation[0]['text']
        return result_str

    @staticmethod
    #   example of pathway_id: REACT:R-HSA-70326
    def get_pathway_entity(pathway_id):
        if pathway_id[:6] == "REACT:":
            pathway_id = pathway_id[6:]
        return QueryReactomeExtended.__get_entity("get_pathway", pathway_id)

    @staticmethod
    #   example of pathway_id: Reactome:R-HSA-70326
    def get_pathway_desc(pathway_id):
        if pathway_id[:6] == "REACT:":
            pathway_id = pathway_id[6:]
        return QueryReactomeExtended.__get_desc("get_pathway_desc", pathway_id)


if __name__ == '__main__':

    def save_to_test_file(filename, key, value):
        f = open(filename, 'r+')
        try:
            json_data = json.load(f)
        except ValueError:
            json_data = {}
        f.seek(0)
        f.truncate()
        json_data[key] = value
        json.dump(json_data, f)
        f.close()

    save_to_test_file('tests/query_test_data.json', 'REACT:R-HSA-70326', QueryReactomeExtended.get_pathway_entity('REACT:R-HSA-70326'))
    save_to_test_file('tests/query_desc_test_data.json', 'REACT:R-HSA-70326', QueryReactomeExtended.get_pathway_desc('REACT:R-HSA-70326'))
    save_to_test_file('tests/query_desc_test_data.json', 'REACT:R-HSA-703260', QueryReactomeExtended.get_pathway_desc('REACT:R-HSA-703260'))
    print(QueryReactomeExtended.get_pathway_desc('REACT:R-HSA-703260'))


# code/reasoningtool/kg-construction/QuerySciGraph.py

""" This module defines the class QuerySciGraph which connects to APIs at
https://scigraph-ontology.monarchinitiative.org/scigraph/graph/neighbors/,
querying "sub phenotypes" for phenotypes.
"""

__author__ = ""
__copyright__ = ""
__credits__ = []
__license__ = ""
__version__ = ""
__maintainer__ = ""
__email__ = ""
__status__ = "Prototype"

# import requests
import sys
from cache_control_helper import CacheControlHelper


class QuerySciGraph:
    TIMEOUT_SEC=120
    API_BASE_URL = {
        'node_properties': 'https://scigraph-ontology.monarchinitiative.org/scigraph/graph/{node_id}',
        'graph_neighbors': 'https://scigraph-ontology.monarchinitiative.org/scigraph/graph/neighbors/{node_id}',
        'cypher_query': 'https://scigraph-ontology.monarchinitiative.org/scigraph/cypher/execute.json'
    }

    @staticmethod
    def __access_api(url, params=None, headers=None):
#        print(url)
        requests = CacheControlHelper()
        try:
            res = requests.get(url, params=params, timeout=QuerySciGraph.TIMEOUT_SEC)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print('Timeout in QuerySciGraph for URL: ' + url, file=sys.stderr)
            return None
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QuerySciGraph for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print(url, file=sys.stderr)
            print('Status code ' + str(status_code) + ' for url: ' + res.url, file=sys.stderr)
            return None

        return res.json()

    @staticmethod
    def get_gene_ontology_curie_ids_for_uberon_curie_id(uberon_curie_id_str):
        results = QuerySciGraph.__access_api(QuerySciGraph.API_BASE_URL['graph_neighbors'].format(node_id=uberon_curie_id_str))
#        print(results)
        go_curie_id_str_dict = dict()
        if results is not None:
            res_edges = results.get("edges", None)
            if res_edges is not None:
                assert type(res_edges) == list
                for res_edge in res_edges:
                    object_curie_id = res_edge.get("obj", None)
                    if object_curie_id is not None:
                        if object_curie_id.startswith("GO:"):
                            edge_object_meta = res_edge.get("meta", None)
                            if edge_object_meta is not None:
                                assert type(edge_object_meta) == dict
                                edge_label = edge_object_meta.get("lbl", None)
                                if edge_label is not None:
                                    assert type(edge_label) == list
                                    go_dict = QuerySciGraph.query_get_ontology_node_category_and_term(object_curie_id)
                                    if len(go_dict) > 0:
                                        go_curie_id_str_dict.update({object_curie_id: {"predicate": edge_label[0],
                                                                                       "ontology": go_dict["category"],
                                                                                       "name": go_dict["name"]}})
        return go_curie_id_str_dict

    @staticmethod
    def get_gene_ontology_curie_ids_for_disease_curie_id(disease_curie_id_str):
        results = QuerySciGraph.__access_api(QuerySciGraph.API_BASE_URL['graph_neighbors'].format(node_id=disease_curie_id_str))
#        print(results)
        go_curie_id_str_dict = dict()
        mondo_curie_id_str = None
        is_mondo_bool = disease_curie_id_str.startswith("MONDO:")
        if results is not None:
            res_edges = results.get("edges", None)
            if res_edges is not None:
                assert type(res_edges) == list
                for res_edge in res_edges:
                    object_curie_id = res_edge.get("obj", None)
                    if object_curie_id is not None:
                        if object_curie_id.startswith("GO:"):
                            edge_object_meta = res_edge.get("meta", None)
                            if edge_object_meta is not None:
                                assert type(edge_object_meta) == dict
                                edge_label = edge_object_meta.get("lbl", None)
                                if edge_label is not None:
                                    assert type(edge_label) == list
                                    go_dict = QuerySciGraph.query_get_ontology_node_category_and_term(object_curie_id)
                                    if len(go_dict) > 0:
                                        go_curie_id_str_dict.update({object_curie_id: {"predicate": edge_label[0],
                                                                                       "ontology": go_dict["category"],
                                                                                       "name": go_dict["name"]}})
                        else:
                            if (not is_mondo_bool) and object_curie_id.startswith("MONDO:"):
                                edge_pred = res_edge.get("pred", None)
                                if edge_pred is not None and edge_pred == "equivalentClass":
                                    mondo_curie_id_str = object_curie_id
        if (not is_mondo_bool) and mondo_curie_id_str is not None:
            go_curie_id_str_dict.update(QuerySciGraph.get_gene_ontology_curie_ids_for_disease_curie_id(mondo_curie_id_str))
        return go_curie_id_str_dict

    
    '''returns the disease ontology IDs (DOID:NNNNN) for a given mesh ID in CURIE format, 'MESH:D012345'
    
    :param mesh_id: str containing the MeSH ID in CURIE format, i.e., MESH:D003550
    :return: set(str)
    '''
    @staticmethod
    def get_disont_ids_for_mesh_id(mesh_id):
        results = QuerySciGraph.__access_api(QuerySciGraph.API_BASE_URL['graph_neighbors'].format(node_id=mesh_id))
        disont_ids = set()
        if results is not None:
            res_nodes = results.get('nodes', None)
            if res_nodes is not None:
                # assert type(res_nodes) == listq
                for res_node in res_nodes:
                    id = res_node.get('id', None)
                    if id is not None:
                        if 'DOID:' in id:
                            disont_ids.add(id)
                        else:
                            meta = res_node.get('meta', None)
                            if meta is not None:
                                dbxrefs = meta.get('http://www.geneontology.org/formats/oboInOwl#hasDbXref', None)
                                if dbxrefs is not None:
                                    assert type(dbxrefs)==list
                                    for dbxref in dbxrefs:
                                        if 'DOID:' in dbxref:
                                            disont_ids.add(dbxref)
        else:
            cypher_query = 'MATCH (dis:disease) WHERE ANY(item IN dis.`http://www.geneontology.org/formats/oboInOwl#hasDbXref` WHERE item = \'' + mesh_id + '\') RETURN dis.`http://www.geneontology.org/formats/oboInOwl#hasDbXref` as xref'
            
            url = QuerySciGraph.API_BASE_URL['cypher_query']
            results = QuerySciGraph.__access_api(url, params={'cypherQuery': cypher_query, 'limit': '10'})
            if results is not None:
                if len(results) > 0:
                    results_xref = results[0].get('xref', None)
                    if results_xref is not None:
                        disont_ids = set([x for x in results_xref if 'DOID:' in x])
        return disont_ids

    @staticmethod
    def query_get_ontology_node_category_and_term(ontology_term_id_str):
        results = QuerySciGraph.__access_api(QuerySciGraph.API_BASE_URL["node_properties"].format(node_id=ontology_term_id_str))
        res_dict = dict()
#        print(results)
        if results is not None:
            results_nodes = results.get("nodes", None)
            if results_nodes is not None:
                assert type(results_nodes) == list
                for results_node in results_nodes:
                    assert type(results_node) == dict
                    results_node_meta = results_node.get("meta", None)
                    if results_node_meta is not None:
                        assert type(results_node_meta) == dict
                        results_category = results_node_meta.get("category", None)
                        if results_category is not None:
                            assert type(results_category) == list
                            category_str = results_category[0]
                            label = results_node.get("lbl", None)
                            if label is not None:
                                assert type(label) == str
                                res_dict.update({"name": label,
                                                 "category": category_str})
        return(res_dict)
                                                                
    @staticmethod
    def query_sub_ontology_terms_for_ontology_term(ontology_term_id):
        """
        Return a dict of `<id, label>`, where `id`s are all sub-phenotype of parameter `ontology_term_id`.

        E.g. input "HP:0000107" (Renal cyst),
        '>>> QuerySciGraph.query_sub_phenotypes_for_phenotype("HP:0000107")
        {'HP:0100877': 'Renal diverticulum', 'HP:0000108': 'Renal corticomedullary cysts',
         'HP:0000803': 'Renal cortical cysts', 'HP:0000003': 'Multicystic kidney dysplasia',
         'HP:0008659': 'Multiple small medullary renal cysts', 'HP:0005562': 'Multiple renal cysts',
         'HP:0000800': 'Cystic renal dysplasia', 'HP:0012581': 'Solitary renal cyst'}
        """

        curie_prefix = ontology_term_id.split(":")[0]
        
        params = {
            # "depth": 1,
            # "blankNodes": "false",

            # Suppose `ontology_term_id` is X
            # If direction is INCOMING, find all Y that (sub {id: Y})-[:subClassOf]->(obj {id: X})
            # If direction is OUTGOING, find all Y that (sub {id: X})-[:subClassOf]->(obj {id: Y})
            "direction": "INCOMING",  # "OUTGOING" / "BOTH"
            "relationshipType": "subClassOf"
        }

        # param_str = "&".join(["{}={}".format(key, value) for key, value in params.items()])
        url = QuerySciGraph.API_BASE_URL["graph_neighbors"].format(node_id=ontology_term_id)
        json = QuerySciGraph.__access_api(url, params=params)
        sub_nodes_with_labels = dict()
        if json is not None:
            sub_edges = json['edges']  # Get all INCOMING edges
            sub_nodes = set(map(lambda e: e["sub"], sub_edges))  # Get all neighboring nodes (duplicates may exist; so set is used here)
            sub_nodes = set(filter(lambda s: s.startswith(curie_prefix + ":"), sub_nodes))  # Keep human phenotypes only
            
            sub_nodes_with_labels = dict([(node["id"], node['lbl']) for node in json['nodes'] if node["id"] in sub_nodes])
            
            if len(sub_nodes_with_labels) >= 200:
                print("[Warning][SciGraph] Found {} sub phenotypes for {}".format(len(sub_nodes_with_labels), ontology_term_id), file=sys.stderr)

        return sub_nodes_with_labels


if __name__ == '__main__':
    print(QuerySciGraph.query_get_ontology_node_category_and_term("GO:0005777"))
    print(QuerySciGraph.get_gene_ontology_curie_ids_for_uberon_curie_id("UBERON:0000171"))
    print(QuerySciGraph.query_get_ontology_node_category_and_term("GO:XXXXXXX"))
    print(QuerySciGraph.get_gene_ontology_curie_ids_for_disease_curie_id("MONDO:0019053"))
    print(QuerySciGraph.get_gene_ontology_curie_ids_for_disease_curie_id("DOID:906"))
    print(QuerySciGraph.query_sub_ontology_terms_for_ontology_term("GO:0005777"))
    print(QuerySciGraph.get_disont_ids_for_mesh_id('MESH:D005199'))
    print(QuerySciGraph.get_disont_ids_for_mesh_id('MESH:D006937'))
    print(QuerySciGraph.get_disont_ids_for_mesh_id('MESH:D000856'))
    print(QuerySciGraph.query_sub_ontology_terms_for_ontology_term('HP:12072'))
    print(QuerySciGraph.query_sub_ontology_terms_for_ontology_term('GO:1904685'))
    print(QuerySciGraph.get_disont_ids_for_mesh_id('MESH:D015473'))
    print(QuerySciGraph.query_sub_ontology_terms_for_ontology_term("HP:0000107"))  # Renal cyst
    print(QuerySciGraph.get_disont_ids_for_mesh_id('MESH:D015470'))
    print(QuerySciGraph.query_sub_ontology_terms_for_ontology_term('GO:0062026'))



# code/reasoningtool/kg-construction/QueryUniprot.py

""" This module defines the class QueryUniprot which connects to APIs at
http://www.uniprot.org/uploadlists/, querying reactome pathways from uniprot id.

*   map_enzyme_commission_id_to_uniprot_ids(ec_id)

    Description:
        map enzyme commission id to UniProt ids

    Args:
        ec_id (str): enzyme commission id, e.g., "ec:1.4.1.17"

    Returns:
        ids (set): a set of the enzyme commission ids, or empty set if no UniProt id can be obtained or the response
                    status code is not 200.

"""

__author__ = ""
__copyright__ = ""
__credits__ = []
__license__ = ""
__version__ = ""
__maintainer__ = ""
__email__ = ""
__status__ = "Prototype"

# import requests
# import requests_cache
from cache_control_helper import CacheControlHelper
import CachedMethods
import sys
import urllib.parse
import xmltodict


class QueryUniprot:
    API_BASE_URL = "http://www.uniprot.org/uploadlists/"
    TIMEOUT_SEC = 120
    HANDLER_MAP = {
        'map_enzyme_commission_id_to_uniprot_ids': 'uniprot/?query=({id})&format=tab&columns=id',
        'get_protein': 'uniprot/{id}.xml'
    }

    @staticmethod
    @CachedMethods.register
    def uniprot_id_to_reactome_pathways(uniprot_id):
        """returns a ``set`` of reactome IDs of pathways associated with a given string uniprot ID

        :param uniprot_id: a ``str`` uniprot ID, like ``"P68871"``
        :returns: a ``set`` of string Reactome IDs
        """

        payload = { 'from':   'ACC',
                    'to':     'REACTOME_ID',
                    'format': 'tab',
                    'query':  uniprot_id }
        contact = "stephen.ramsey@oregonstate.edu"
        header = {'User-Agent': 'Python %s' % contact}

        requests = CacheControlHelper()

        try:
            url =QueryUniprot.API_BASE_URL
            res = requests.post(QueryUniprot.API_BASE_URL, data=payload, headers=header)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print('Timeout in QueryUniprot for URL: ' + QueryUniprot.API_BASE_URL, file=sys.stderr)
            return None
        except KeyboardInterrupt:
            sys.exit(0)
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryUniprot for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print(QueryUniprot.API_BASE_URL, file=sys.stderr)
            print('Status code ' + str(status_code) + ' for url: ' + QueryUniprot.API_BASE_URL, file=sys.stderr)
            return None
#        assert 200 == res.status_code
        res_set = set()
        for line in res.text.splitlines():
            field_str = line.split("\t")[1]
            if field_str != "To":
                res_set.add(field_str)
        return res_set

    @staticmethod
    def __access_api(handler):

        api_base_url = 'http://www.uniprot.org'
        url = api_base_url + '/' + handler
        #print(url)
        contact = "stephen.ramsey@oregonstate.edu"
        header = {'User-Agent': 'Python %s' % contact}

        requests = CacheControlHelper()
        try:
            res = requests.get(url, timeout=QueryUniprot.TIMEOUT_SEC, headers=header)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print('Timeout in QueryUniprot for URL: ' + url, file=sys.stderr)
            return None
        except requests.exceptions.ChunkedEncodingError:
            print(url, file=sys.stderr)
            print('ChunkedEncodingError for URL: ' + url, file=sys.stderr)
            return None
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryUniprot for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print(url, file=sys.stderr)
            print('Status code ' + str(status_code) + ' for url: ' + url, file=sys.stderr)
            return None
        return res.text

    @staticmethod
    def map_enzyme_commission_id_to_uniprot_ids(ec_id):
        res_set = set()
        if not isinstance(ec_id, str):
            return res_set
        ec_id_encoded = urllib.parse.quote_plus(ec_id)
        handler = QueryUniprot.HANDLER_MAP['map_enzyme_commission_id_to_uniprot_ids'].format(id=ec_id_encoded)
        res = QueryUniprot.__access_api(handler)
        if res is not None:
            res = res[res.find('\n')+1:]
            for line in res.splitlines():
                res_set.add(line)
        return res_set

    @staticmethod
    def __get_entity(entity_type, entity_id):
        if entity_id[:10] == 'UniProtKB:':
            entity_id = entity_id[10:]
        handler = QueryUniprot.HANDLER_MAP[entity_type].format(id=entity_id)
        results = QueryUniprot.__access_api(handler)
        entity = None
        if results is not None:
            obj = xmltodict.parse(results)
            if 'uniprot' in obj.keys():
                if 'entry' in obj['uniprot'].keys():
                    entity = obj['uniprot']['entry']
        return entity

    @staticmethod
    def get_protein_gene_symbol(entity_id):
        ret_symbol = "None"
        if not isinstance(entity_id, str):
            return ret_symbol
        entity_obj = QueryUniprot.__get_entity("get_protein", entity_id)
        if entity_obj is not None:
            if 'gene' in entity_obj.keys():
                if "name" in entity_obj["gene"].keys():
                    gene_name_obj = entity_obj["gene"]["name"]
                    if not type(gene_name_obj) == list:
                        gene_name_obj = [gene_name_obj]
                    for name_dict in gene_name_obj:
                        #                        print(name_dict)
                        if "primary" in name_dict.values() and "#text" in name_dict.keys():
                            ret_symbol = name_dict["#text"]
        return ret_symbol

    @staticmethod
    def __get_name(entity_type, entity_id):
        entity_obj = QueryUniprot.__get_entity(entity_type, entity_id)
        name = "UNKNOWN"
        if entity_obj is not None:
            if 'protein' in entity_obj.keys():
                if 'recommendedName' in entity_obj['protein'].keys():
                    if 'fullName' in entity_obj['protein']['recommendedName'].keys():
                        name = entity_obj['protein']['recommendedName']['fullName']
                        if isinstance(name, dict):
                            name = name['#text']
        return name

    @staticmethod
    def get_protein_name(protein_id):
        if not isinstance(protein_id, str):
            return "UNKNOWN"
        return QueryUniprot.__get_name("get_protein", protein_id)

    @staticmethod
    def get_citeable_accession_for_accession(accession_number):
        res_acc = None
        res_tab = QueryUniprot.__access_api("uniprot/" + accession_number + ".tab")
        if res_tab is None:
            return res_acc
        res_lines = res_tab.splitlines()
        if len(res_lines) > 1:
            res_acc = res_lines[1].split("\t")[0]
        return res_acc

if __name__ == '__main__':
    print(QueryUniprot.get_citeable_accession_for_accession("P35354"))
    print(QueryUniprot.get_citeable_accession_for_accession("A8K802"))
    print(QueryUniprot.get_citeable_accession_for_accession("Q16876"))
    # print(QueryUniprot.uniprot_id_to_reactome_pathways("P68871"))
    # print(QueryUniprot.uniprot_id_to_reactome_pathways("Q16621"))
    # print(QueryUniprot.uniprot_id_to_reactome_pathways("P09601"))
    print(CachedMethods.cache_info())
    print(QueryUniprot.map_enzyme_commission_id_to_uniprot_ids("ec:1.4.1.17"))  # small results
    print(QueryUniprot.map_enzyme_commission_id_to_uniprot_ids("ec:1.3.1.110")) # empty result
    print(QueryUniprot.map_enzyme_commission_id_to_uniprot_ids("ec:1.2.1.22"))  # large results
    print(QueryUniprot.map_enzyme_commission_id_to_uniprot_ids("ec:4.4.1.xx"))  # fake id
    print(QueryUniprot.map_enzyme_commission_id_to_uniprot_ids("R-HSA-1912422"))   # wrong id
    print(QueryUniprot.get_protein_gene_symbol('UniProtKB:P20848'))
    print(QueryUniprot.get_protein_gene_symbol("UniProtKB:P01358"))
    print(QueryUniprot.get_protein_gene_symbol("UniProtKB:Q96P88"))
    print(QueryUniprot.get_protein_name('UniProtKB:P01358'))
    print(QueryUniprot.get_protein_name('UniProtKB:P20848'))
    print(QueryUniprot.get_protein_name('UniProtKB:Q9Y471'))
    print(QueryUniprot.get_protein_name('UniProtKB:O60397'))
    print(QueryUniprot.get_protein_name('UniProtKB:Q8IZJ3'))
    print(QueryUniprot.get_protein_name('UniProtKB:Q7Z2Y8'))
    print(QueryUniprot.get_protein_name('UniProtKB:Q8IWN7'))
    print(QueryUniprot.get_protein_name('UniProtKB:Q156A1'))


# code/reasoningtool/kg-construction/QueryUniprotExtended.py


''' This module defines the class QueryUniprotExtended. QueryUniprotExtended class is designed
to communicate with Uniprot APIs and their corresponding data sources. The
available methods include:

*   get_protein_name(protein_id)

    Description:
        search for protein name

    Args:
        protein_id (str): The ID of the protein entity, e.g., "UniProt:P01358"

    Returns:
        name (str): the name of the protein entity, or 'None' if no protein object can be obtained.
'''


__author__ = 'Deqing Qu'
__copyright__ = 'Oregon State University'
__credits__ = ['Deqing Qu', 'Stephen Ramsey']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'

# import requests
# import requests_cache
from cache_control_helper import CacheControlHelper
import sys
import xmltodict


class QueryUniprotExtended:
    TIMEOUT_SEC = 120
    API_BASE_URL = 'http://www.uniprot.org/uniprot'
    HANDLER_MAP = {
        'get_protein': '{id}.xml'
    }

    @staticmethod
    def __access_api(handler):

        requests = CacheControlHelper()
        url = QueryUniprotExtended.API_BASE_URL + '/' + handler
        # print(url)
        try:
            res = requests.get(url, timeout=QueryUniprotExtended.TIMEOUT_SEC)
        except requests.exceptions.Timeout:
            print(url, file=sys.stderr)
            print('Timeout in QueryUniprot for URL: ' + url, file=sys.stderr)
            return None
        except BaseException as e:
            print(url, file=sys.stderr)
            print('%s received in QueryUniprot for URL: %s' % (e, url), file=sys.stderr)
            return None
        status_code = res.status_code
        if status_code != 200:
            print(url, file=sys.stderr)
            print('Status code ' + str(status_code) + ' for url: ' + url, file=sys.stderr)
            return None

        return res.text

    @staticmethod
    def __get_entity(entity_type, entity_id):
        if entity_id[:10] == 'UniProtKB:':
            entity_id = entity_id[10:]
        handler = QueryUniprotExtended.HANDLER_MAP[entity_type].format(id=entity_id)
        results = QueryUniprotExtended.__access_api(handler)
        entity = None
        if results is not None:
            obj = xmltodict.parse(results)
            if 'uniprot' in obj.keys():
                if 'entry' in obj['uniprot'].keys():
                    entity = obj['uniprot']['entry']
        return entity

    @staticmethod
    def get_protein_gene_symbol(entity_id):
        ret_symbol = ""
        entity_obj = QueryUniprotExtended.__get_entity("get_protein", entity_id)
        if entity_obj is not None:
            if 'gene' in entity_obj.keys():
                if "name" in entity_obj["gene"].keys():
                    gene_name_obj = entity_obj["gene"]["name"]
                    if not type(gene_name_obj) == list:
                        gene_name_obj = [ gene_name_obj ]
                    for name_dict in gene_name_obj:
                        #                        print(name_dict)
                        if "primary" in name_dict.values() and "#text" in name_dict.keys():
                            ret_symbol = name_dict["#text"]
        return ret_symbol
        
    @staticmethod
    def __get_name(entity_type, entity_id):
        entity_obj = QueryUniprotExtended.__get_entity(entity_type, entity_id)
        name = "UNKNOWN"
        if entity_obj is not None:
            if 'protein' in entity_obj.keys():
                if 'recommendedName' in entity_obj['protein'].keys():
                    if 'fullName' in entity_obj['protein']['recommendedName'].keys():
                        name = entity_obj['protein']['recommendedName']['fullName']
                        if isinstance(name, dict):
                            name = name['#text']
        return name

    @staticmethod
    def get_protein_name(protein_id):
        return QueryUniprotExtended.__get_name("get_protein", protein_id)

if __name__ == '__main__':
    print(QueryUniprotExtended.get_protein_gene_symbol('UniProtKB:P20848'))
    print(QueryUniprotExtended.get_protein_gene_symbol("UniProtKB:P01358"))
    print(QueryUniprotExtended.get_protein_gene_symbol("UniProtKB:Q96P88"))
    print(QueryUniprotExtended.get_protein_name('UniProtKB:P01358'))
    print(QueryUniprotExtended.get_protein_name('UniProtKB:P20848'))
    print(QueryUniprotExtended.get_protein_name('UniProtKB:Q9Y471'))
    print(QueryUniprotExtended.get_protein_name('UniProtKB:O60397'))
    print(QueryUniprotExtended.get_protein_name('UniProtKB:Q8IZJ3'))
    print(QueryUniprotExtended.get_protein_name('UniProtKB:Q7Z2Y8'))
    print(QueryUniprotExtended.get_protein_name('UniProtKB:Q8IWN7'))
    print(QueryUniprotExtended.get_protein_name('UniProtKB:Q156A1'))


# code/reasoningtool/kg-construction/SynonymMapper.py

import sys
import os
#new_path = os.path.join(os.getcwd(), '..', 'SemMedDB')
#sys.path.insert(0, new_path)

from NormGoogleDistance import NormGoogleDistance
#from SemMedInterface import SemMedInterface
from QueryMyGene import QueryMyGene
import mygene
import requests
from QueryMyChem import QueryMyChem
import requests_cache
import pandas
#import _mysql_exceptions


class SynonymMapper():

    def __init__(self):
        #try:
        #    self.smi = SemMedInterface()
        #except _mysql_exceptions.OperationalError:
        #    print('Warning: No connection was made to the SemMEdDB MySQL server.')
        #    self.smi = None
        self.biothings_url = "http://c.biothings.io/v1/query?q="
        self.mygene_obj = mygene.MyGeneInfo()
        self.qmg = QueryMyGene()

    def prot_to_gene(self, curie_id):
        """
        This takes a uniprot curie id and converts it into a few different gene ids
        """
        if len(curie_id.split(':'))>1:
            uniprot_id = curie_id.split(':')[1]
        else:
            return None
        entrez_ids = self.qmg.convert_uniprot_id_to_entrez_gene_ID(uniprot_id)
        if entrez_ids is not None:
            entrez_ids = set(entrez_ids)
        else:
            entrez_ids = set()
        hgnc_ids = set()
        mim_ids = set()
        vega_ids = set()
        ensembl_ids = set()
        synonyms = []

        symbols = self.qmg.convert_uniprot_id_to_gene_symbol(uniprot_id)
        for symbol in symbols:
            synonyms += ['HGNC.Symbol:' + symbol]

        for gene_id in entrez_ids:
            synonyms += ['NCBIGene:' + str(gene_id)]
            try:
                res = self.mygene_obj.getgene(int(gene_id), fields = 'HGNC,MIM,Vega,ensembl', verbose = False)
            except requests.exceptions.HTTPError:
                print('HTTP error for querying uniprot to gene symbol mygene: ' + uniprot_id, file=sys.stderr)
                res = None
            if res is not None:
                hgnc_res = res.get('HGNC', None)
                mim_res = res.get('MIM', None)
                vega_res = res.get('Vega', None)
                ensembl_res = res.get('ensembl', None)
            else:
                hgnc_res = None
                mim_res = None
                vega_res = None
                ensembl_res = None
            if hgnc_res is not None:
                hgnc_ids |= set([hgnc_res])
            if mim_res is not None:
                mim_ids |= set([mim_res])
            if vega_res is not None:
                vega_ids |= set([vega_res])
            if ensembl_res is not None:
                if type(ensembl_res) == list:
                    for ens_res in ensembl_res:
                        ensembl_gene_res = ens_res.get('gene', None)
                        if ensembl_gene_res is not None:
                            ensembl_ids |= set([ensembl_gene_res])
                else:
                    ensembl_gene_res = ensembl_res.get('gene', None)
                    if ensembl_gene_res is not None:
                        ensembl_ids |= set([ensembl_gene_res])

        for hgnc_id in hgnc_ids:
            synonyms += ['HGNC:' + str(hgnc_id)]
        for mim_id in mim_ids:
            synonyms += ['OMIM:' + str(mim_id)]
        for vega_id in vega_ids:
            synonyms += ['Vega:' + str(vega_id)]
        for ensembl_id in ensembl_ids:
            synonyms += ['ensembl:' + str(ensembl_id)]

        if len(synonyms)>0:
            return synonyms
        else:
            return None

    def get_all_from_oxo(self, curie_id, map_to = None):
        """
        this takes a curie id and gets all the mappings that oxo has for the given id
        
        :param curie_id: The string for the curie id to submit to OXO (e.g. 'HP:0001947')
        :param map_to: A string containing the prefix for the resulting ids. If set to None it will return all mappings. (default is none)
        
        :return: A list of strings containing the found mapped ids or None if none where found
        """
        if map_to is None:
            map_to = ''
        if type(curie_id) != str:
            curie_id = str(curie_id)
        if curie_id.startswith('REACT:'):
            curie_id = curie_id.replace('REACT', 'Reactome')
        prefix = curie_id.split(':')[0]
        res = NormGoogleDistance.query_oxo(curie_id)
        synonym_ids=None
        if res is not None:
            res = res.json()
            synonym_ids = set()
            n_res = res['page']['totalElements']
            if int(n_res) > 0:
                mappings = res['_embedded']['mappings']
                for mapping in mappings:
                    if type(map_to) == list:
                        for elm in map_to:
                            if mapping['fromTerm']['curie'].startswith(prefix):
                                if mapping['toTerm']['curie'].startswith(elm):
                                    synonym_ids |= set([mapping['toTerm']['curie']])
                            elif mapping['toTerm']['curie'].startswith(prefix):
                                if mapping['fromTerm']['curie'].startswith(elm):
                                    synonym_ids |= set([mapping['fromTerm']['curie']])
                    else:
                        if mapping['fromTerm']['curie'].startswith(prefix):
                            if mapping['toTerm']['curie'].startswith(map_to):
                                synonym_ids |= set([mapping['toTerm']['curie']])
                        elif mapping['toTerm']['curie'].startswith(prefix):
                            if mapping['fromTerm']['curie'].startswith(map_to):
                                synonym_ids |= set([mapping['fromTerm']['curie']])
            if len(synonym_ids) == 0:
                synonym_ids = None
            else:
                synonym_ids = list(synonym_ids)
        return synonym_ids

    #def id_to_cui(self, curie_id):
    #    """
    #    this takes a currie id and finds a UMLS cui for it
    #    """
    #    assert self.smi is not None, "No connection was made to the MySQL SemMedDB server on rtxdev.saramsey.org if you want to try to connect again reinitialize the class."
    #    cuis = self.smi.get_cui_for_id(curie_id)
    #    return cuis

    def chembl_to_chebi(self, chemical_substance_id):
        """
        This takes a chembl curie id and return a chebi curie id
        """
        if chemical_substance_id[:7] == "ChEMBL:":
            chemical_substance_id = chemical_substance_id.replace("ChEMBL:", "CHEMBL")
        if chemical_substance_id.startswith('CHEMBL:CHEMBL'):
            chemical_substance_id = chemical_substance_id.replace("CHEMBL:", "")
        handler = 'chem/' + chemical_substance_id + '?fields=chebi.chebi_id'

        url = QueryMyChem.API_BASE_URL + '/' + handler

        try:
            res = requests.get(url, timeout=QueryMyChem.TIMEOUT_SEC)
        except requests.exceptions.Timeout:
            #print(url, file=sys.stderr)
            #print('Timeout in QueryMyChem for URL: ' + url, file=sys.stderr)
            return None
        if res is None:
            return None
        status_code = res.status_code
        if status_code != 200:
            #print(url, file=sys.stderr)
            #print('Status code ' + str(status_code) + ' for url: ' + url, file=sys.stderr)
            return None
        id_json = res.json()
        if 'chebi' in id_json.keys():
            return id_json['chebi']['chebi_id']
        else:
            return None






# code/reasoningtool/kg-construction/UpdateIndex.py

import uuid
import itertools
import pprint
import neo4j.v1
import sys
import timeit
import argparse
import os

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../")  # code directory
from RTXConfiguration import RTXConfiguration

parser = argparse.ArgumentParser()
# parser.add_argument("-u", "--user", type=str, help="The username used to connect to the neo4j instance", default='')
# parser.add_argument("-p", "--password", type=str, help="The password used to connect to the neo4j instance", default='')
# parser.add_argument("-a", "--url", type=str, help="The bolt url and port used to connect to the neo4j instance. (default:bolt://localhost:7687)", default="bolt://localhost:7687")
parser.add_argument('--live', help="The container name, which can be one of the following: Production, KG2, rtxdev, "
                                   "staging. (default: Production)", default='Production')
args = parser.parse_args()

class UpdateIndex():
    """
    This class connects to a neo4j instance with our KG in it
    then can set/drop all needed indexes

    :param url: a string containing the bolt url of the neo4j instance, ``bolt://your.neo4j.url:7687``
    :param user: a string containing the username used to access the neo4j instance
    :param password: a string containing the password used to access the neo4j instance
    :param debug: a boolian indicating weither or not to use debug mode (will print out queries sent)
    """

    def __init__(self, user, password, url ='bolt://localhost:7687', debug = False):
        self.debug = debug
        self.neo4j_url = url
        self.neo4j_user = user
        self.neo4j_password = password

        self.driver = neo4j.v1.GraphDatabase.driver(self.neo4j_url,
                                                    auth=(self.neo4j_user,
                                                          self.neo4j_password))

    def neo4j_shutdown(self):
        """
        shuts down the Orangeboard by disconnecting from the Neo4j database   
        :returns: nothing
        """
        self.driver.close()

    def neo4j_run_cypher_query(self, query, parameters=None):
        """
        runs a single cypher query in the neo4j database (without a transaction) and returns the result object
        :param query: a ``str`` object containing a single cypher query (without a semicolon)
        :param parameters: a ``dict`` object containing parameters for this query
        :returns: a `neo4j.v1.SessionResult` object resulting from executing the neo4j query
        """
        if self.debug:
            print(query)
        assert ';' not in query

        # Lazily initialize the driver
        if self.driver is None:
            self.neo4j_connect()

        session = self.driver.session()
        res = session.run(query, parameters)
        session.close()
        return res


    def set_index(self):
        """
        adds a hardcoded list on idexes and contraints to a neo4j instance
        :return: nothing
        """

        # This gets a list of all labels on the KG then removes the Base label
        res = self.neo4j_run_cypher_query('match (n) with distinct labels(n) as label_sets unwind(label_sets) as labels return distinct labels')
        label_list = res.value()
        label_list.remove('Base')


        # These are the indexes and constraints on the base label
        index_commands = [
            'CREATE CONSTRAINT ON (n:Base) ASSERT n.id IS UNIQUE',
            'CREATE CONSTRAINT ON (n:Base) ASSERT n.UUID IS UNIQUE',
            'CREATE CONSTRAINT ON (n:Base) ASSERT n.uri IS UNIQUE',
            'CREATE INDEX ON :Base(name)',
            'CREATE INDEX ON :Base(seed_node_uuid)'
            ]

        # These create label specific indexes and constraints
        index_commands += ['CREATE CONSTRAINT ON (n:' + label + ') ASSERT n.id IS UNIQUE' for label in label_list]
        index_commands += ['CREATE INDEX ON :' + label + '(name)' for label in label_list]

        for command in index_commands:
            self.neo4j_run_cypher_query(command)



    def drop_index(self):
        """
        requests lists of all contraints and idexes on the neo4j server then drops all of them.
        :return: nothing
        """


        res = self.neo4j_run_cypher_query('CALL db.constraints()')
        constraints = res.value()

        index_commands = ['DROP ' + constraint for constraint in constraints]

        for command in index_commands:
            self.neo4j_run_cypher_query(command)

        res = self.neo4j_run_cypher_query('CALL db.indexes()')
        indexes = res.value()

        index_commands = ['DROP ' + index for index in indexes]

        for command in index_commands:
            self.neo4j_run_cypher_query(command)

    def replace(self):
        self.drop_index()
        self.set_index()

    def set_test(self):
        """
        Sets the idexes up to test for an error
        """
        self.drop_index()
        index_commands = [
            'CREATE INDEX ON :Base(UUID)',
            'CREATE INDEX ON :Base(seed_node_uuid)',
            'CREATE CONSTRAINT ON (n:biological_process) ASSERT n.id IS UNIQUE',
            'CREATE CONSTRAINT ON (n:microRNA) ASSERT n.id IS UNIQUE',
            'CREATE CONSTRAINT ON (n:protein) ASSERT n.id IS UNIQUE'
            ]

        for command in index_commands:
            self.neo4j_run_cypher_query(command)
        

if __name__ == '__main__':
    # create the RTXConfiguration object
    rtxConfig = RTXConfiguration()

    ui = UpdateIndex(rtxConfig.neo4j_username, rtxConfig.neo4j_password, rtxConfig.neo4j_bolt)
    ui.replace()



# code/reasoningtool/kg-construction/UpdateNodesInfo.py

"""
This module defines the class UpdateNodesInfo. UpdateNodesInfo class is designed
to retrieve the node properties and update the properties on the Graphic model object.
The available methods include:

    update_anatomy_nodes : retrieve data from BioLink and update all anatomy nodes
    update_phenotype_nodes : retrieve data from BioLink and update all phenotype nodes
    update_microRNA_nodes : retrieve data from MyGene and update all microRNA nodes
    update_pathway_nodes : retrieve data from Reactome and update all pathway nodes
    update_protein_nodes : retrieve data from MyGene and update all protein nodes
    update_disease_nodes : retrieve data from BioLink and update all disease nodes

    update_anatomy_nodes_desc : update the descriptions of anatomical_entity nodes
    update_phenotype_nodes_desc : update the descriptions of phenotypic_feature nodes
    update_disease_nodes_desc : update the descriptions of disease nodes
    update_bio_process_nodes_desc : update the descriptions of biological_process nodes
    update_microRNA_nodes_desc : update the descriptions of microRNA nodes
    update_protein_nodes_desc : update the descriptions of protein nodes
    update_chemical_substance_desc : update the descriptions of chemical_substance nodes
    update_pathway_nodes_desc : update the descriptions of pathway nodes
    update_cellular_component_nodes_desc : update the descriptions of cellular_component nodes
    update_molecular_function_nodes_desc : update the descriptions of molecular_function nodes
    update_metabolite_nodes_desc : update the descriptions of metabolite nodes

Example of method name used from other packages.
    example of get_nodes_mtd_name : get_anatomy_nodes
    example of get_entity_mtd_name : get_anatomy_entity
    example of update_nodes_mtd_name : update_anatomy_nodes

How to run this module:
If you want to update the descriptions of all types of nodes, please use the default value of runfunc argument:
        $ cd [git repo]/code/reasoningtool/kg-construction
        $ python3 UpdateNodesInfo.py -u xxx -p xxx 1>stdout_desc.log 2>stderr_desc.log

If you want to update the descriptions of the specified nodes, please use the runfunc argument to specify the method:
        $ cd [git repo]/code/reasoningtool/kg-construction
        $ python3 UpdateNodesInfo.py -u xxx -p xxx --runfunc=update_disease_nodes_desc 1>stdout_desc.log 2>stderr_desc.log
"""

__author__ = 'Deqing Qu'
__copyright__ = 'Oregon State University'
__credits__ = ['Deqing Qu', 'Stephen Ramsey']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'

import argparse
import sys
import os

from Neo4jConnection import Neo4jConnection
from QueryEBIOLS import QueryEBIOLS
from QueryOMIM import QueryOMIM
from QueryMyGene import QueryMyGene
from QueryMyChem import QueryMyChem
from QueryReactome import QueryReactome
from QueryKEGG import QueryKEGG
from QueryHMDB import QueryHMDB

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../")  # code directory
from RTXConfiguration import RTXConfiguration

class UpdateNodesInfo:

    GET_QUERY_CLASS = {
        'anatomy': 'QueryBioLink',
        'phenotype': 'QueryBioLink',
        'microRNA': 'QueryMyGene',
        'pathway': 'QueryReactome',
        'protein': 'QueryMyGene',
        'disease': 'QueryBioLink',
        'chemical_substance': 'QueryMyChem',
        'bio_process': 'QueryBioLink'
    }

    def __init__(self, user, password, url ='bolt://localhost:7687'):
        self.neo4j_user = user
        self.neo4j_password = password
        self.neo4j_url = url

    def __update_nodes(self, node_type):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        get_nodes_mtd_name = "get_" + node_type + "_nodes"
        get_nodes_mtd = getattr(conn, get_nodes_mtd_name)
        nodes = get_nodes_mtd()
        print(len(nodes))

        from time import time
        t = time()

        nodes_array = []
        query_class_name = UpdateNodesInfo.GET_QUERY_CLASS[node_type]
        query_class = getattr(__import__(query_class_name), query_class_name)
        get_entity_mtd_name = "get_" + node_type + "_entity"
        get_entity_mtd = getattr(query_class, get_entity_mtd_name)
        query_instance = query_class()
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            if node_type == "protein" or node_type == "microRNA":
                get_entity_mtd = getattr(query_instance, get_entity_mtd_name)
            node['extended_info_json'] = get_entity_mtd(node_id)
            nodes_array.append(node)
            print(node_type + " node No. %d : %s" % (i, node_id))

        print("api pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            update_nodes_mtd_name = "update_" + node_type + "_nodes"
            update_nodes_mtd = getattr(conn, update_nodes_mtd_name)
            update_nodes_mtd(nodes_array[start:end])

        print("total time: %f" % (time() - t))

        conn.close()

    def update_anatomy_nodes(self):
        self.__update_nodes('anatomy')

    def update_phenotype_nodes(self):
        self.__update_nodes('phenotype')

    def update_microRNA_nodes(self):
        self.__update_nodes('microRNA')

    def update_pathway_nodes(self):
        self.__update_nodes('pathway')

    def update_protein_nodes(self):
        self.__update_nodes('protein')

    def update_disease_nodes(self):
        self.__update_nodes('disease')

    def update_chemical_substance_nodes(self):
        self.__update_nodes('chemical_substance')

    def update_bio_process_nodes(self):
        self.__update_nodes('bio_process')

    def update_anatomy_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_anatomy_nodes()
        print("the number of anatomy nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            node['desc'] = QueryEBIOLS.get_anatomy_description(node_id)
            nodes_array.append(node)

        print("anatomy api pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_anatomy_nodes_desc(nodes_array[start:end])

        print("anatomy total time: %f" % (time() - t))

        conn.close()

    def update_phenotype_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_phenotype_nodes()
        print("the number of phenotype nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            node['desc'] = QueryEBIOLS.get_phenotype_description(node_id)
            nodes_array.append(node)

        print("phenotype api pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_phenotype_nodes_desc(nodes_array[start:end])

        print("phenotype total time: %f" % (time() - t))

        conn.close()

    def update_microRNA_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_microRNA_nodes()
        print("the number of microRNA nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        mg = QueryMyGene()
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            node['desc'] = mg.get_microRNA_desc(node_id)
            nodes_array.append(node)

        print("microRNA api pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_microRNA_nodes_desc(nodes_array[start:end])

        print("microRNA total time: %f" % (time() - t))

        conn.close()

    def update_pathway_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_pathway_nodes()
        print("the number of pathway: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            node['desc'] = QueryReactome.get_pathway_desc(node_id)
            nodes_array.append(node)

        print("pathway api pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_pathway_nodes_desc(nodes_array[start:end])

        print("pathway total time: %f" % (time() - t))

        conn.close()

    def update_protein_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_protein_nodes()
        print("the number of protein nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        mg = QueryMyGene()
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            node['desc'] = mg.get_protein_desc(node_id)
            nodes_array.append(node)

        print("protein api pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_protein_nodes_desc(nodes_array[start:end])

        print("protein total time: %f" % (time() - t))

        conn.close()

    def update_disease_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_disease_nodes()
        print("the number of disease nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        qo = QueryOMIM()
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            if node_id[:4] == "OMIM":
                node['desc'] = qo.disease_mim_to_description(node_id)
            elif node_id[:4] == "DOID":
                node['desc'] = QueryEBIOLS.get_disease_description(node_id)
            nodes_array.append(node)

        print("disease api pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_disease_nodes_desc(nodes_array[start:end])

        print("disease total time: %f" % (time() - t))

        conn.close()

    def update_chemical_substance_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_chemical_substance_nodes()
        print("the number of chemical_substance nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            node['desc'] = QueryMyChem.get_chemical_substance_description(node_id)
            nodes_array.append(node)

        print("chemical_substance pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_chemical_substance_nodes_desc(nodes_array[start:end])

        print("chemical substance total time: %f" % (time() - t))

        conn.close()

    def update_bio_process_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_bio_process_nodes()
        print("the number of bio_process nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            node['desc'] = QueryEBIOLS.get_bio_process_description(node_id)
            nodes_array.append(node)

        print("bio_process pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_bio_process_nodes_desc(nodes_array[start:end])

        print("bio_process total time: %f" % (time() - t))

        conn.close()

    def update_cellular_component_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_cellular_component_nodes()
        print("the number of cellular_component nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        for i, node_id in enumerate(nodes):
            # print("no %d" % i)
            node = dict()
            node['node_id'] = node_id
            node['desc'] = QueryEBIOLS.get_cellular_component_description(node_id)
            nodes_array.append(node)

        print("cellular_component pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_cellular_component_nodes_desc(nodes_array[start:end])

        print("cellular_component total time: %f" % (time() - t))

        conn.close()

    def update_molecular_function_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_molecular_function_nodes()
        print("the number of molecular_function nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        for i, node_id in enumerate(nodes):
            # print("no %d" % i)
            node = dict()
            node['node_id'] = node_id
            node['desc'] = QueryEBIOLS.get_molecular_function_description(node_id)
            nodes_array.append(node)

        print("molecular_function pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_molecular_function_nodes_desc(nodes_array[start:end])

        print("molecular_function total time: %f" % (time() - t))

        conn.close()

    def update_metabolite_nodes_desc(self):
        conn = Neo4jConnection(self.neo4j_url, self.neo4j_user, self.neo4j_password)
        nodes = conn.get_metabolite_nodes()
        print("the number of metabolite nodes: %d" % len(nodes))

        from time import time
        t = time()

        success_count = 0
        nodes_array = []
        for i, node_id in enumerate(nodes):
            # if i % 100 == 0:
            #     print("no %d" % i)
            node = dict()
            node['node_id'] = node_id
            hmdb_id = QueryKEGG.map_kegg_compound_to_hmdb_id(node_id)
            if hmdb_id:
                hmdb_url = 'http://www.hmdb.ca/metabolites/' + hmdb_id
                node['desc'] = QueryHMDB.get_compound_desc(hmdb_url)
                if node['desc'] != "None":
                    success_count += 1
            else:
                node['desc'] = 'None'
            nodes_array.append(node)
        print("success_count = " + str(success_count))
        print("metabolite pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_metabolite_nodes_desc(nodes_array[start:end])

        print("metabolite total time: %f" % (time() - t))

        conn.close()

    def update_all(self):
        # UpdateNodesInfo.update_anatomy_nodes()
        # UpdateNodesInfo.update_phenotype_nodes()
        # UpdateNodesInfo.update_microRNA_nodes()
        # UpdateNodesInfo.update_pathway_nodes()
        # UpdateNodesInfo.update_protein_nodes()
        # UpdateNodesInfo.update_disease_nodes()
        # UpdateNodesInfo.update_chemical_substance_nodes()
        # UpdateNodesInfo.update_bio_process_nodes()
        self.update_anatomy_nodes_desc()
        self.update_phenotype_nodes_desc()
        self.update_disease_nodes_desc()
        self.update_bio_process_nodes_desc()
        self.update_microRNA_nodes_desc()
        self.update_protein_nodes_desc()
        self.update_chemical_substance_desc()
        self.update_pathway_nodes_desc()
        self.update_cellular_component_nodes_desc()
        self.update_molecular_function_nodes_desc()
        self.update_metabolite_nodes_desc()


if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='update the descriptions of nodes in th knowledge graph')
    # parser.add_argument("-a", "--address", help="The bolt url and port used to connect to the neo4j instance. (default:"
    #                                             "bolt://localhost:7687)",
    #                     default="bolt://localhost:7687")
    # parser.add_argument("-u", "--username", help="The username used to connect to the neo4j instance. (default: )",
    #                     default='')
    # parser.add_argument("-p", "--password", help="The password used to connect to the neo4j instance. (default: )",
    #                     default='')
    parser.add_argument('--live', help="The container name, which can be one of the following: Production, KG2, rtxdev, "
                             "staging. (default: Production)", default='Production')

    parser.add_argument('--runfunc', dest='runfunc')
    args = parser.parse_args()

    # create the RTXConfiguration object
    rtxConfig = RTXConfiguration()

    #   create UpdateNodesInfo object
    ui = UpdateNodesInfo(rtxConfig.neo4j_username, rtxConfig.neo4j_password, rtxConfig.neo4j_bolt)

    args_dict = vars(args)
    if args_dict.get('runfunc', None) is not None:
        run_function_name = args_dict['runfunc']
    else:
        run_function_name = 'update_all'

    try:
        run_function = getattr(ui, run_function_name)
    except AttributeError:
        sys.exit('In module UpdateNodesInfo.py, unable to find function named: ' + run_function_name)

    run_function()



# code/reasoningtool/kg-construction/UpdateNodesName.py

''' This module defines the class UpdateNodesName. UpdateNodesName class is designed
to retrieve the node name and update the name on the Graphic model object.
The available methods include:

*   update_protein_names

    Description: retrieve names from Uniprot and update protein nodes

How to run this module
        $ cd [git repo]/code/reasoningtool/kg-construction
        $ python3 UpdateNodesName.py
'''

# BEGIN config.json format
# {
#   "url":"bolt://localhost:7687"
#   "username":"xxx",
#   "password":"xxx"
# }
# END config.json format

__author__ = 'Deqing Qu'
__copyright__ = 'Oregon State University'
__credits__ = ['Deqing Qu', 'Stephen Ramsey']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'

from Neo4jConnection import Neo4jConnection
from QueryUniprot import QueryUniprot
from QueryMyGene import QueryMyGene
import json


class UpdateNodesName:

    @staticmethod
    def update_protein_names_old(protein_ids):

        from time import time
        t = time()

        nodes_array = []
        for protein_id in protein_ids:
            node = dict()
            node['node_id'] = protein_id
            node['name'] = QueryUniprot.get_protein_name(protein_id)
            nodes_array.append(node)

        print("Uniprot api pulling time: %f" % (time() - t))

        f = open('config.json', 'r')
        config_data = f.read()
        f.close()
        config = json.loads(config_data)

        conn = Neo4jConnection(config['url'], config['username'], config['password'])
        conn.update_protein_nodes_name(nodes_array)
        conn.close()

        print("total time: %f" % (time() - t))

    @staticmethod
    def update_protein_nodes_name():
        f = open('config.json', 'r')
        config_data = f.read()
        f.close()
        config = json.loads(config_data)

        conn = Neo4jConnection(config['url'], config['username'], config['password'])
        nodes = conn.get_protein_nodes()
        print("the number of protein nodes: %d" % len(nodes))

        from time import time
        t = time()

        nodes_array = []
        mg = QueryMyGene()
        for i, node_id in enumerate(nodes):
            node = dict()
            node['node_id'] = node_id
            node['name'] = mg.get_protein_name(node_id)
            nodes_array.append(node)

        print("protein api pulling time: %f" % (time() - t))

        nodes_nums = len(nodes_array)
        chunk_size = 10000
        group_nums = nodes_nums // chunk_size + 1
        for i in range(group_nums):
            start = i * chunk_size
            end = (i + 1) * chunk_size if (i + 1) * chunk_size < nodes_nums else nodes_nums
            conn.update_protein_nodes_name(nodes_array[start:end])

        print("protein total time: %f" % (time() - t))

        conn.close()


if __name__ == '__main__':
    # protein_nodes_ids = ['UniProtKB:P01358', 'UniProtKB:P20848', 'UniProtKB:Q9Y471', 'UniProtKB:O60397',
    #                      'UniProtKB:Q8IZJ3', 'UniProtKB:Q7Z2Y8', 'UniProtKB:Q8IWN7', 'UniProtKB:Q156A1']
    # UpdateNodesName.update_protein_names_old(protein_nodes_ids)

    UpdateNodesName.update_protein_nodes_name()

# code/reasoningtool/kg-construction/backup_test.py

from neo4j.v1 import GraphDatabase
import json
import sys
import os

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../")  # code directory
from RTXConfiguration import RTXConfiguration

### BEGIN HOW TO RUN
#   Function: The script is used to test the consistency of the dumped database file
#   Instance: This python script needs to be run on both 'kgdump' container and the machine which is loaded the dumped
#             file. If the outputs on both machines are the same, the test is passed
#   how to load the dumped database file:
#               tar -zxvf xxxx.tar.gz
#               neo4j-admin load --from=xxxx.cypher --database=graph --force
#   how to run the python script:
#               python3 backup_test.py
#   output example:
#               nodes counter : 68800
#               relationships counter : 2508369
### END HOW TO RUN

### BEGIN user_pass.json format
# {
#   "username":"xxx",
#   "password":"xxx"
# }
### END user_pass.json format

class TestBackup(object):

    def __init__(self, uri, user, password):
        self._driver = GraphDatabase.driver(uri, auth=(user, password))

    def close(self):
        self._driver.close()

    def print_node_count(self):
        with self._driver.session() as session:
            counter = session.write_transaction(self._get_node_count)
            print('nodes counter : %d' % counter)

    def print_relation_count(self):
        with self._driver.session() as session:
            counter = session.write_transaction(self._get_relation_count)
            print('relationships counter : %d' % counter)

    @staticmethod
    def _get_node_count(tx):
        result = tx.run("START n=node(*) RETURN count(n)")
        return result.single()[0]

    @staticmethod
    def _get_relation_count(tx):
        result = tx.run("START r=relationship(*) RETURN count(r)")
        return result.single()[0]


if __name__ == '__main__':

    # create the RTXConfiguration object
    rtxConfig = RTXConfiguration()

    obj = TestBackup(rtxConfig.neo4j_bolt, rtxConfig.neo4j_username, rtxConfig.neo4j_password)
    obj.print_node_count()
    obj.print_relation_count()
    obj.close()


# code/reasoningtool/kg-construction/cache_control_helper.py

import calendar
from cachecontrol.heuristics import BaseHeuristic
from datetime import datetime, timedelta
from email.utils import parsedate, formatdate
import requests
from cachecontrol import CacheControl
from cachecontrol.caches.file_cache import FileCache


class CustomHeuristic(BaseHeuristic):

    def __init__(self, days: float = ...):
        self.days = days

    def update_headers(self, response):
        date = parsedate(response.headers['date'])
        expires = datetime(*date[:6]) + timedelta(days=self.days)
        # print(formatdate(calendar.timegm(expires.timetuple())))
        return {
            'expires': formatdate(calendar.timegm(expires.timetuple())),
            'cache-control': 'public',
        }

    def warning(self, response):
        msg = 'Automatically cached! Response is Stale.'
        return '110 - "%s"' % msg


class CacheControlHelper(object):

    def __init__(self):
        self.sess = CacheControl(requests.session(), heuristic=CustomHeuristic(days=30), cache=FileCache('.web_cache'))
        self.exceptions = requests.exceptions

    def get(self, url, params=None, timeout=120, cookies=None, headers={'Accept': 'application/json'}):
        if cookies:
            return self.sess.get(url, params=params, timeout=timeout, cookies=cookies, headers=headers)
        else:
            return self.sess.get(url, params=params, timeout=timeout, headers=headers)

    def post(self, url, data, timeout=120, headers={'Accept': 'application/json'}):
        return self.sess.post(url, data=data, timeout=timeout, headers=headers)


# code/reasoningtool/kg-construction/request_cache_helper.py

import requests
import requests_cache
import hashlib
import time
import re, os

_DEFAULT_HEADERS = requests.utils.default_headers()

#requests_cache.install_cache("orangeboard")
# specifiy the path of orangeboard database
tmppath = re.compile(".*/RTX/")
dbpath = tmppath.search(os.path.realpath(__file__)).group(0) + 'data/orangeboard'
requests_cache.install_cache(dbpath)

def get_timestamp(url):
    """
    get the timestamp of an HTTP get request
    :param url: the URL of the request
    :return the timestamp of the request, of None if the request is not in the cache
    """
    def _to_bytes(s, encoding='utf-8'):
        return bytes(s, encoding)

    def create_key(request):
        url, body = request.url, request.body
        key = hashlib.sha256()
        key.update(_to_bytes(request.method.upper()))
        key.update(_to_bytes(url))
        if request.body:
            key.update(_to_bytes(body))
        return key.hexdigest()

    def url_to_key(url):
        session = requests.Session()
        return create_key(session.prepare_request(requests.Request('GET', url)))

    #   get the cache from request_cache
    results = requests_cache.get_cache()
    #   create the key according to the url
    key_url = url_to_key(url)
    #   results.responses is a dictionary and follows the following format:
    #   { 'key': (requests_cache.backends objects, timestamp), ..., }
    #   for example: '4c28e3e4a61e325e520d9c02e0caee99e30c00951a223e67':
    #                       (<requests_cache.backends.base._Store object at 0x12697e630>,
    #                           datetime.datetime(2018, 10, 16, 0, 19, 8, 130204)),
    if key_url in results.responses:
        back_obj, timestamp = results.responses[key_url]
        return timestamp
    return None


if __name__ == '__main__':

    url = 'http://cohd.io/api/association/obsExpRatio?dataset_id=1&concept_id_1=192855&domain=Procedure'
    url1 = 'http://cohd.io/api/association/obsExpRatio?dataset_id=1&concept_id_1=192853&domain=Procedure'
    url2 = 'http://cohd.io/api/association/obsExpRatio?dataset_id=1&concept_id_1=192854&domain=Procedure'

    res = requests.get(url)
    res = requests.get(url1)

    t = time.time()
    print(get_timestamp(url))
    print(get_timestamp(url1))
    print(get_timestamp(url2))
    print("Time used: ", time.time() - t)


# code/reasoningtool/tests/QueryBioLinkTests.py

import unittest
from QueryBioLink import QueryBioLink as QBL


class QueryBioLinkTestCase(unittest.TestCase):
    def test_get_label_for_disease(self):
        # unknown_resp = QBL.get_label_for_disease('XXX')
        # self.assertEqual(unknown_resp, 'UNKNOWN')

        chlr_label = QBL.get_label_for_disease('DOID:1498')  # cholera
        self.assertIsNone(chlr_label)

        pd_label = QBL.get_label_for_disease('OMIM:605543')  # Parkinson’s disease 4
        self.assertEqual(pd_label, "Parkinson Disease 4, Autosomal Dominant")

    def test_get_phenotypes_for_disease_desc(self):
        ret_dict = QBL.get_phenotypes_for_disease_desc('OMIM:605543')  # Parkinson’s disease 4

        known_dict = {'HP:0000726': 'Dementia',
                      'HP:0001824': 'Weight loss',
                      'HP:0002459': 'Dysautonomia',
                      'HP:0100315': 'Lewy bodies',
                      'HP:0011999': 'Paranoia',
                      'HP:0001278': 'Orthostatic hypotension',
                      'HP:0000738': 'Hallucinations',
                      'HP:0001300': 'Parkinsonism'}

        self.assertDictEqual(ret_dict, known_dict)

    def test_get_diseases_for_gene_desc(self):
        empty_dict = QBL.get_diseases_for_gene_desc('NCBIGene:407053')  # MIR96
        self.assertEqual(len(empty_dict), 0)

        # TODO find an NCBIGene that `get_diseases_for_gene_desc` would return a non-empty result

    def test_get_genes_for_disease_desc(self):
        pd_genes = QBL.get_genes_for_disease_desc('OMIM:605543')  # Parkinson’s disease 4
        self.assertEqual(len(pd_genes), 1)
        self.assertEqual(pd_genes[0], 'HGNC:11138')

    def test_get_label_for_phenotype(self):
        mcd_label = QBL.get_label_for_phenotype('HP:0000003')  # Multicystic kidney dysplasia
        self.assertEqual(mcd_label, "Multicystic kidney dysplasia")

    def test_get_phenotypes_for_gene(self):
        # NEK1, NIMA related kinase 1
        nek1_list = QBL.get_phenotypes_for_gene('NCBIGene:4750')

        known_list = ['HP:0000003', 'HP:0000054', 'HP:0000062', 'HP:0000105',
                      'HP:0000110', 'HP:0000171', 'HP:0000204', 'HP:0000248',
                      'HP:0000773', 'HP:0000774', 'HP:0000888', 'HP:0000895',
                      'HP:0001274', 'HP:0001302', 'HP:0001320', 'HP:0001395',
                      'HP:0001629', 'HP:0001631', 'HP:0001762', 'HP:0001789',
                      'HP:0002023', 'HP:0002089', 'HP:0002350', 'HP:0002566',
                      'HP:0002980', 'HP:0003016', 'HP:0003022', 'HP:0003038',
                      'HP:0005054', 'HP:0005257', 'HP:0005349', 'HP:0005766',
                      'HP:0005817', 'HP:0005873', 'HP:0006426', 'HP:0006956',
                      'HP:0010454', 'HP:0010579', 'HP:0100259']

        # Sequence does not matter here
        self.assertSetEqual(set(nek1_list), set(known_list))

    def test_get_phenotypes_for_gene_desc(self):
        # Test for issue #22
        # CFTR, cystic fibrosis transmembrane conductance regulator
        cftr_dict = QBL.get_phenotypes_for_gene_desc('NCBIGene:1080')

        known_dict = {'HP:0000952': 'Jaundice',
                      'HP:0011227': 'Elevated C-reactive protein level',
                      'HP:0030247': 'Splanchnic vein thrombosis',
                      'HP:0012379': 'Abnormal enzyme/coenzyme activity',
                      'HP:0001974': 'Leukocytosis'}

        self.assertDictEqual(cftr_dict, known_dict)

    def test_get_anatomies_for_gene(self):
        mir96_dict = QBL.get_anatomies_for_gene('NCBIGene:407053')  # MIR96

        known_dict = {'UBERON:0000007': 'pituitary gland',
                      'UBERON:0001301': 'epididymis',
                      'UBERON:0000074': 'renal glomerulus',
                      'UBERON:0000006': 'islet of Langerhans'}

        self.assertDictEqual(mir96_dict, known_dict)

    def test_get_genes_for_anatomy(self):
        iol_list = QBL.get_genes_for_anatomy('UBERON:0000006')  # islet of Langerhans

        known_list = ['MGI:1929735', 'HGNC:28826', 'HGNC:31579', 'HGNC:28995',
                      'HGNC:24172', 'HGNC:6130', 'MGI:2429955', 'MGI:1922935',
                      'MGI:1927126', 'HGNC:11513', 'MGI:3647725', 'HGNC:1960',
                      'MGI:1922977', 'HGNC:6172', 'MGI:1922469', 'HGNC:3518',
                      'MGI:97010', 'ENSEMBL:ENSG00000237404', 'HGNC:20253',
                      'HGNC:14676', 'MGI:1915416', 'MGI:1891697', 'HGNC:30237',
                      'HGNC:12970', 'HGNC:30766', 'HGNC:51534', 'ENSEMBL:ENSG00000271946',
                      'HGNC:17013', 'HGNC:25481', 'MGI:104755', 'MGI:1336199',
                      'ENSEMBL:ENSG00000261159', 'MGI:98003', 'MGI:1916344', 'HGNC:28412',
                      'MGI:88082', 'MGI:2656825', 'MGI:1918345', 'HGNC:20189', 'HGNC:33526',
                      'HGNC:31481', 'ENSEMBL:ENSG00000248632', 'MGI:3615306', 'MGI:109177',
                      'HGNC:2697', 'MGI:1342304', 'HGNC:3816', 'HGNC:11656', 'HGNC:29332',
                      'HGNC:6294', 'HGNC:13787', 'HGNC:18994', 'MGI:2387188', 'MGI:3584508',
                      'ENSEMBL:ENSG00000244306', 'HGNC:29101', 'MGI:1919247', 'HGNC:23433',
                      'HGNC:31393', 'MGI:1347084', 'MGI:1316650', 'MGI:2179507', 'MGI:96163',
                      'MGI:2146012', 'MGI:1916043', 'HGNC:10435', 'HGNC:48629', 'HGNC:23094',
                      'HGNC:25139', 'MGI:2444946', 'HGNC:34236', 'HGNC:26466', 'HGNC:3725',
                      'MGI:2141207', 'HGNC:50580', 'HGNC:18294', 'HGNC:33754', 'MGI:1924150',
                      'HGNC:12499', 'HGNC:17451', 'NCBIGene:100506691', 'HGNC:2522',
                      'MGI:106199', 'HGNC:17811', 'HGNC:8001', 'ENSEMBL:ENSG00000167765',
                      'HGNC:33520', 'HGNC:7200', 'HGNC:11996', 'HGNC:29503', 'HGNC:30021',
                      'MGI:2139593', 'HGNC:24825', 'ENSEMBL:ENSMUSG00000093459',
                      'ENSEMBL:ENSG00000265179', 'HGNC:18696', 'MGI:5504148', 'HGNC:1553',
                      'MGI:1353654', 'MGI:88139']

        # Sequence does not matter here
        self.assertSetEqual(set(iol_list), set(known_list))

    def test_get_anatomies_for_phenotype(self):
        mcd_dict = QBL.get_anatomies_for_phenotype('HP:0000003')  # Multicystic kidney dysplasia

        known_dict = {'UBERON:0002113': 'kidney'}

        self.assertDictEqual(mcd_dict, known_dict)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryDisGeNetTests.py

import unittest
from QueryDisGeNet import QueryDisGeNet


class QueryDisGeNetTestCase(unittest.TestCase):
    def test_query_mesh_id_to_uniprot_ids_desc(self):
        ret_dict = QueryDisGeNet.query_mesh_id_to_uniprot_ids_desc('D016779')

        known_dict = {'P35228': 'NOS2', 'P17927': 'CR1', 'P01375': 'TNF',
                      'P09601': 'HMOX1', 'P15260': 'IFNGR1', 'P29460': 'IL12B',
                      'Q96D42': 'HAVCR1', 'P05112': 'IL4', 'P17181': 'IFNAR1',
                      'P16671': 'CD36', 'P17948': 'FLT1', 'P03956': 'MMP1',
                      'P15692': 'VEGFA', 'P39060': 'COL18A1', 'P12821': 'ACE',
                      'P01584': 'IL1B', 'P14778': 'IL1R1', 'P29474': 'NOS3',
                      'Q9NR96': 'TLR9', 'P01889': 'HLA-B', 'P03989': 'HLA-B',
                      'P10319': 'HLA-B'}

        self.assertDictEqual(ret_dict, known_dict)

if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryDisontTests.py

import unittest
from QueryDisont import QueryDisont as QD


class QueryDisontTestCase(unittest.TestCase):
    def test_query_disont_to_child_disonts(self):
        ret_set = QD.query_disont_to_child_disonts('DOID:9352')
        known_set = {11712, 1837, 10182}
        self.assertSetEqual(ret_set, known_set)

    def test_query_disont_to_label(self):
        ret_label = QD.query_disont_to_label("DOID:0050741")
        self.assertEqual(ret_label, "alcohol dependence")

    def test_query_disont_to_child_disonts_desc(self):
        ret_dict = QD.query_disont_to_child_disonts_desc("DOID:9352")  # type 2 diabetes mellitus
        known_dict = {'DOID:1837': 'diabetic ketoacidosis',
                      'DOID:10182': 'diabetic peripheral angiopathy',
                      'DOID:11712': 'lipoatrophic diabetes'}

        self.assertDictEqual(ret_dict, known_dict)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryGeneProfTests.py

import unittest
from QueryGeneProf import QueryGeneProf as QGP


class QueryGeneProfTestCase(unittest.TestCase):
    def test_gene_symbol_to_geneprof_ids(self):
        ret_set = QGP.gene_symbol_to_geneprof_ids('HMOX1')
        known_set = {16269}
        self.assertSetEqual(ret_set, known_set)

    def test_geneprof_id_to_transcription_factor_gene_symbols(self):
        ret_set = QGP.geneprof_id_to_transcription_factor_gene_symbols(16269)
        known_set = {'IRF1', 'ELK4', 'WRNIP1', 'FLI1', 'E2F4', 'KDM4A', 'GATA6',
                     'SPI1', 'GATA1', 'CDX2', 'ETV1', 'WHSC1', 'SOX2', 'CTCF',
                     'CUX1', 'ZNF143', 'HDAC1', 'MAFF', 'SREBF1', 'BACH1', 'MAZ',
                     'TRIM28', 'USF2', 'KDM5B', 'BRCA1', 'SMARCC1', 'CCNT2', 'TBP',
                     'ZMIZ1', 'CHD1', 'UBTF', 'SAP30', 'STAT3', 'RBBP5', 'BRD7',
                     'POU5F1', 'MYC', 'MAX', 'KLF4', 'RAD21', 'PPARG', 'HSF1',
                     'ZC3H11A', 'HCFC1', 'SMARCB1', 'JUN', 'SMARCA4', 'RCOR1',
                     'TAL1', 'TFAP2A', 'CHD7', 'CEBPB', 'BHLHE40', 'JUND', 'BCL6',
                     'E2F1', 'MAFK', 'STAT1', 'ZNF384', 'CREBBP', 'ZNF263', 'SMARCC2',
                     'E2F6', 'MXI1', 'POU2F1', 'KDM1A', 'HMGN3', 'POLR3A', 'SETDB1',
                     'PHF8', 'TBL1XR1', 'GTF2F1', 'RFX5', 'ESRRA', 'HNF4A', 'FOXA1',
                     'NFE2', 'HDAC2', 'EOMES', 'EP300', 'FOS'}

        self.assertSetEqual(ret_set, known_set)

    def test_gene_symbol_to_transcription_factor_gene_symbols(self):
        ret_set = QGP.gene_symbol_to_transcription_factor_gene_symbols('IRF1')
        known_set = {'POU5F1', 'CREBBP', 'SMARCC1', 'BRD7', 'SIRT6', 'SPI1',
                     'SUZ12', 'CDX2', 'ELK1', 'EP300', 'GATA2', 'E2F6', 'ESRRA',
                     'RBBP5', 'JUN', 'TFAP2A', 'TAF2', 'HCFC1', 'MAZ', 'CUX1', 'MAX',
                     'HNF4A', 'EOMES', 'ETS1', 'KDM4A', 'TBL1XR1', 'ZNF143', 'ZC3H11A',
                     'BRCA1', 'ETV1', 'GATA3', 'CTNNB1', 'POLR3A', 'PHF8', 'FOS', 'USF2',
                     'RUNX1', 'RFX5', 'WRNIP1', 'ZNF263', 'UBTF', 'CEBPB', 'SMARCA4', 'GATA1',
                     'CHD7', 'TAL1', 'BHLHE40', 'E2F4', 'FOXA1', 'SMC3', 'MAFF', 'DDX5', 'BACH1',
                     'EZH2', 'CTCF', 'CHD1', 'MYC', 'KDM5B', 'HDAC2', 'ZNF384', 'RAD21', 'WHSC1',
                     'PRDM14', 'NFATC1', 'IRF1', 'KDM1A', 'HDAC6', 'SAP30', 'CCNT2', 'KLF4',
                     'SMARCB1', 'TRIM28', 'HMGN3', 'GTF2F1', 'STAT3', 'JUND', 'MXI1', 'CHD2',
                     'TBP', 'E2F1', 'SREBF1', 'HDAC1', 'SETDB1', 'ZMIZ1', 'STAT1', 'RCOR1'}

        self.assertSetEqual(ret_set, known_set)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryMiRBaseTests.py

import unittest
from QueryMiRBase import QueryMiRBase as QMB


class QueryMiRBaseTestCase(unittest.TestCase):
    def test_convert_mirbase_id_to_mir_gene_symbol(self):
        res_symbol = QMB.convert_mirbase_id_to_mir_gene_symbol('MIMAT0027666')
        self.assertEqual(res_symbol, "MIR6883")

    def test_convert_mirbase_id_to_mature_mir_ids(self):
        ret_set = QMB.convert_mirbase_id_to_mature_mir_ids('MI0014240')
        known_set = {'MIMAT0015079'}
        self.assertSetEqual(ret_set, known_set)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryMiRGateTests.py

import unittest
from QueryMiRGate import QueryMiRGate as QMG


class QueryMiRGateTestCase(unittest.TestCase):
    def test_get_microrna_ids_that_regulate_gene_symbol(self):
        res_ids = QMG.get_microrna_ids_that_regulate_gene_symbol('HMOX1')
        known_ids = {'MIMAT0002174', 'MIMAT0000077', 'MIMAT0021021', 'MIMAT0023252',
                     'MIMAT0016901', 'MIMAT0019723', 'MIMAT0019941', 'MIMAT0018996',
                     'MIMAT0015029', 'MIMAT0019227', 'MIMAT0014989', 'MIMAT0015078',
                     'MIMAT0005920', 'MIMAT0014978', 'MIMAT0024616', 'MIMAT0019012',
                     'MIMAT0022726', 'MIMAT0018965', 'MIMAT0019913', 'MIMAT0014990',
                     'MIMAT0019028', 'MIMAT0019806', 'MIMAT0015238', 'MIMAT0025852',
                     'MIMAT0019844', 'MIMAT0005905', 'MIMAT0023696', 'MIMAT0005871',
                     'MIMAT0019829', 'MIMAT0019041', 'MIMAT0019699', 'MIMAT0004776',
                     'MIMAT0019218'}

        self.assertSetEqual(res_ids, known_ids)

    def test_get_gene_symbols_regulated_by_microrna(self):
        res_ids = QMG.get_gene_symbols_regulated_by_microrna('MIMAT0018979')

        known_ids = {'NISCH', 'MED16', 'FAM53C', 'DGKA', 'GFAP', 'TMEM208', 'COPS7A',
                     'ELSPBP1', 'FKTN', 'RP11-617B3.2', 'ELP5', 'AC025335.1', 'PCBP1-AS1',
                     'TRAF3', 'P4HA2', 'RABL2A', 'HSP90AB1', 'ST13', 'GPX4', 'HMGN2',
                     'RP11-317J19.1', 'DNAJA4', 'TUBA1B', 'COL1A1', 'C22orf25', 'DHX30',
                     'LINC00469', 'PROM2', 'CTD-2062F14.3', 'SSBP1', 'BIRC6', 'RP11-998D10.7',
                     'NPTX2', 'GPR85', 'CDCA5', 'ATF7IP', 'AC010148.1', 'AGAP3', 'BMPER',
                     'MAGED1', 'REG1A', 'DUSP11', 'TCF12', 'SPTBN2', 'SEMA4G', 'DRG2', 'DUS4L',
                     'AC012067.1', 'BMF', 'PPM1M', 'TPM1', 'AC105344.2', 'LPIN1', 'PHKG2',
                     'RP11-84C10.2', 'IFI27', 'AP001442.2', 'CDK14', 'LRIG2', 'EEFSEC', 'C17orf75',
                     'IKBKB', 'MIR655', 'RP11-473I1.10', 'CDH18', 'NSD1', 'ZNF330', 'NCAM1',
                     'TCAIM', 'LINC00665', 'ILF3', 'SCRN1', 'PELI3', 'FAR1', 'EWSR1', 'RP11-116K4.1',
                     'MAOA', 'OR9Q2', 'LDHC', 'MTRR', 'IL36G', 'MUS81', 'MS4A7', 'AC010731.3',
                     'GCNT3', 'RAB26', 'IFT122', 'MORC3', 'RP11-526N18.1', 'SRR', 'EIF3B', 'PTPRA',
                     'CTD-2014E2.6', 'TRGV3', 'TRAFD1', 'BBS1', 'ABCC3', 'POLR2J', 'EGFL8.1', 'DECR1',
                     'IL20RB', 'METTL21D', 'C1QTNF6', 'CTD-2354A18.1', 'HOOK2', 'NAT9', 'EIF4E2',
                     'PABPN1', 'C16orf13', 'LRRC53', 'TRGV7', 'DDX19A', 'CHTOP', 'ZNF707', 'BCAS3',
                     'WDR46', 'RP11-467D18.2', 'ART3'}

        self.assertSetEqual(res_ids, known_ids)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryModuleTestSuite.py

import unittest
from tests.QueryBioLinkTests import QueryBioLinkTestCase
from tests.QueryChEMBLTests import QueryChEMBLTestCase
from tests.QueryDisGeNetTests import QueryDisGeNetTestCase
from tests.QueryDisontTests import QueryDisontTestCase
from tests.QueryGeneProfTests import QueryGeneProfTestCase
from tests.QueryMiRBaseTests import QueryMiRBaseTestCase
from tests.QueryMiRGateTests import QueryMiRGateTestCase
from tests.QueryMyGeneTests import QueryMyGeneTestCase
from tests.QueryNCBIeUtilsTests import QueryNCBIeUtilsTestCase
from tests.QueryOMIMTests import QueryOMIMTestCase
from tests.QueryPC2Tests import QueryPC2TestCase
from tests.QueryPharosTests import QueryPharosTestCase
from tests.QueryReactomeTests import QueryReactomeTestCase
from tests.QuerySciGraphTests import QuerySciGraphTestCase
from tests.QueryUniprotTests import QueryUniprotTestCase


class QueryModuleTestSuite(unittest.TestSuite):
    def suite(self):
        suite = unittest.TestSuite()

        suite.addTest(QueryBioLinkTestCase())
        suite.addTest(QueryChEMBLTestCase())
        suite.addTest(QueryDisGeNetTestCase())
        suite.addTest(QueryDisontTestCase())
        suite.addTest(QueryGeneProfTestCase())

        suite.addTest(QueryMiRBaseTestCase())
        suite.addTest(QueryMiRGateTestCase())
        suite.addTest(QueryMyGeneTestCase())
        suite.addTest(QueryNCBIeUtilsTestCase())
        suite.addTest(QueryOMIMTestCase())

        suite.addTest(QueryPC2TestCase())
        suite.addTest(QueryPharosTestCase())
        suite.addTest(QueryReactomeTestCase())
        suite.addTest(QuerySciGraphTestCase())
        suite.addTest(QueryUniprotTestCase())

        self.run(suite)


if __name__ == '__main__':
    """
    Run this module outside `tests` folder.
    
        John@Workstation ~/Documents/Git-repo/NCATS/code/reasoningtool (master)
        $ python -m unittest tests/QueryModuleTestSuite.py

    """
    unittest.main()


# code/reasoningtool/tests/QueryMyGeneTests.py

import unittest
from QueryMyGene import QueryMyGene


class QueryMyGeneTestCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.mg = QueryMyGene()

    def test_convert_gene_symbol_to_uniprot_id(self):
        uniprot_ids = self.mg.convert_gene_symbol_to_uniprot_id('A2M')
        known_ids = {'P01023'}
        self.assertSetEqual(uniprot_ids, known_ids)

    def test_convert_uniprot_id_to_gene_symbol(self):
        gene_symbols = self.mg.convert_uniprot_id_to_gene_symbol('Q05925')
        know_symbols = {'EN1'}
        self.assertSetEqual(gene_symbols, know_symbols)

    def test_convert_uniprot_id_to_entrez_gene_ID(self):
        entrez_ids = self.mg.convert_uniprot_id_to_entrez_gene_ID("P09601")
        known_ids = {3162}
        self.assertSetEqual(entrez_ids, known_ids)

    def test_convert_gene_symbol_to_entrez_gene_ID(self):
        entrez_ids = self.mg.convert_gene_symbol_to_entrez_gene_ID('MIR96')
        known_ids = {407053}
        self.assertSetEqual(entrez_ids, known_ids)

    def test_convert_entrez_gene_ID_to_mirbase_ID(self):
        mirbase_ids = self.mg.convert_entrez_gene_ID_to_mirbase_ID(407053)
        known_ids = {'MI0000098'}
        self.assertSetEqual(mirbase_ids, known_ids)


if __name__ == '__main__':


# code/reasoningtool/tests/QueryNCBIeUtilsTests.py

import unittest
from QueryNCBIeUtils import QueryNCBIeUtils


class QueryNCBIeUtilsTestCase(unittest.TestCase):
    def test_get_clinvar_uids_for_disease_or_phenotype_string(self):
        res_set = QueryNCBIeUtils.get_clinvar_uids_for_disease_or_phenotype_string("Parkinson's disease")
        # TODO too many IDs in the result; how to evaluate the correctness?
        self.assertTrue(len(res_set) > 0)

    def test_get_mesh_uids_for_mesh_term(self):
        res_uid = QueryNCBIeUtils.get_mesh_uids_for_mesh_term('Leukemia, Promyelocytic, Acute')
        known_uid = ['68015473']
        self.assertListEqual(res_uid, known_uid)

    def test_get_mesh_uid_for_medgen_uid(self):
        mesh_uid = QueryNCBIeUtils.get_mesh_uid_for_medgen_uid(41393)
        known_uid = {68003550}
        self.assertSetEqual(mesh_uid, known_uid)

    def test_get_mesh_terms_for_mesh_uid(self):
        mesh_terms = QueryNCBIeUtils.get_mesh_terms_for_mesh_uid(68003550)
        known_terms = ['Cystic Fibrosis', 'Fibrosis, Cystic', 'Mucoviscidosis',
                       'Pulmonary Cystic Fibrosis', 'Cystic Fibrosis, Pulmonary',
                       'Pancreatic Cystic Fibrosis', 'Cystic Fibrosis, Pancreatic',
                       'Fibrocystic Disease of Pancreas', 'Pancreas Fibrocystic Disease',
                       'Pancreas Fibrocystic Diseases', 'Cystic Fibrosis of Pancreas']
        self.assertSetEqual(set(mesh_terms), set(known_terms))

    def test_get_mesh_terms_for_omim_id(self):
        mesh_terms = QueryNCBIeUtils.get_mesh_terms_for_omim_id(219700)  # OMIM preferred name: "CYSTIC FIBROSIS"
        known_terms = ['Cystic Fibrosis', 'Fibrosis, Cystic', 'Mucoviscidosis',
                       'Pulmonary Cystic Fibrosis', 'Cystic Fibrosis, Pulmonary',
                       'Pancreatic Cystic Fibrosis', 'Cystic Fibrosis, Pancreatic',
                       'Fibrocystic Disease of Pancreas', 'Pancreas Fibrocystic Disease',
                       'Pancreas Fibrocystic Diseases', 'Cystic Fibrosis of Pancreas']
        self.assertSetEqual(set(mesh_terms), set(known_terms))

    def test_get_medgen_uid_for_omim_id(self):
        medgen_ids = QueryNCBIeUtils.get_medgen_uid_for_omim_id(219550)
        known_ids = {346587}
        self.assertSetEqual(medgen_ids, known_ids)

    def test_get_pubmed_hits_count(self):
        hits = QueryNCBIeUtils.get_pubmed_hits_count('"Cholera"[MeSH Terms]')
        self.assertEqual(hits, 8104)

    def test_normalized_google_distance(self):
        ngd = QueryNCBIeUtils.normalized_google_distance("Cystic Fibrosis", "Cholera")
        self.assertAlmostEqual(ngd, 0.69697006)

    def test_is_mesh_term(self):
        self.assertTrue(QueryNCBIeUtils.is_mesh_term("Cholera"))
        self.assertFalse(QueryNCBIeUtils.is_mesh_term("Foo"))





if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryOMIMTests.py

import unittest
from QueryOMIM import QueryOMIM


class QueryOMIMTestCase(unittest.TestCase):
    def setUp(self):
        self.qo = QueryOMIM()

    # test issue 1
    def test_disease_mim_to_gene_symbols_and_uniprot_ids(self):
        ret_dict = self.qo.disease_mim_to_gene_symbols_and_uniprot_ids('OMIM:603918')
        known_dict = {'gene_symbols': set(), 'uniprot_ids': set()}

        self.assertDictEqual(ret_dict, known_dict)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryPC2Tests.py

import unittest
from QueryPC2 import QueryPC2


class QueryPC2TestCase(unittest.TestCase):
    def test_pathway_id_to_uniprot_ids(self):
        pw_id = QueryPC2.pathway_id_to_uniprot_ids("R-HSA-1480926")
        # TODO find a pathway id such that the return value is not empty
        pass

    def test_uniprot_id_to_reactome_pathways(self):
        ret_set = QueryPC2.uniprot_id_to_reactome_pathways("P68871")
        known_set = {'R-HSA-5653656', 'R-HSA-109582', 'R-HSA-2173782',
                     'R-HSA-168256', 'R-HSA-382551', 'R-HSA-168249', 'R-HSA-1480926'}
        self.assertSetEqual(ret_set, known_set)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryPharosTests.py

import unittest
from QueryPharos import QueryPharos


class QueryPharosTestCase(unittest.TestCase):
    def test_query_drug_name_to_targets(self):
        targets = QueryPharos.query_drug_name_to_targets("lovastatin")
        known_targets = [{'id': 19672, 'name': '3-hydroxy-3-methylglutaryl-coenzyme A reductase'},
                         {'id': 14711, 'name': 'Integrin alpha-L'},
                         {'id': 3939, 'name': 'Farnesyl pyrophosphate synthase'},
                         {'id': 14764, 'name': 'Integrin beta-3'},
                         {'id': 13844, 'name': 'Cytochrome P450 2D6'},
                         {'id': 16824, 'name': 'Prostacyclin receptor'},
                         {'id': 8600, 'name': 'Prostaglandin G/H synthase 2'},
                         {'id': 18746, 'name': 'Cytochrome P450 3A5'},
                         {'id': 17657, 'name': 'Serine/threonine-protein kinase mTOR'},
                         {'id': 7520, 'name': 'C-C chemokine receptor type 5'}]
        self.assertListEqual(targets, known_targets)

    def test_query_target_to_diseases(self):
        diseases = QueryPharos.query_target_to_diseases("16824")
        known_diseases = [{'id': '37', 'name': 'ulcerative colitis'},
                          {'id': '2163', 'name': 'Heart Diseases'},
                          {'id': '574', 'name': 'Asthma, Aspirin-Induced'},
                          {'id': '31', 'name': 'osteosarcoma'},
                          {'id': '95', 'name': 'lung adenocarcinoma'},
                          {'id': '1516', 'name': 'severe asthma'},
                          {'id': '39', 'name': "Crohn's disease"},
                          {'id': '195', 'name': 'Hypertension'},
                          {'id': '1335', 'name': 'Primary pulmonary hypertension'},
                          {'id': '9901', 'name': 'Arteriosclerosis obliterans'},
                          {'id': '4429', 'name': 'Intermittent claudication'},
                          {'id': '192', 'name': 'Atherosclerosis'},
                          {'id': '193', 'name': 'Coronary artery disease'},
                          {'id': '245', 'name': 'pulmonary arterial hypertension'},
                          {'id': '1273', 'name': 'Pulmonary hypertension'},
                          {'id': '3746', 'name': 'Nasal discharge'},
                          {'id': '501', 'name': 'Cough'},
                          {'id': '1371', 'name': 'Nasal congestion'},
                          {'id': '142', 'name': 'Allergic rhinitis'},
                          {'id': '1350', 'name': 'Rhinitis'},
                          {'id': '1024', 'name': 'Common cold'}]
        self.assertListEqual(diseases, known_diseases)

    def test_query_target_to_drugs(self):
        drugs = QueryPharos.query_target_to_drugs("16824")
        known_drugs = [{'id': 8411409, 'name': 'selexipag', 'action': 'AGONIST'},
                       {'id': 8411420, 'name': 'iloprost', 'action': 'AGONIST'},
                       {'id': 8099182, 'name': 'epoprostenol', 'action': 'AGONIST'},
                       {'id': 8411432, 'name': 'beraprost', 'action': 'AGONIST'},
                       {'id': 8411443, 'name': 'treprostinil', 'action': 'AGONIST'}]
        self.assertListEqual(drugs, known_drugs)

    def test_query_drug_to_targets(self):
        targets = QueryPharos.query_drug_to_targets("254599")
        known_targets = [{'id': 10000655, 'name': 'HMGCR'}]
        self.assertListEqual(targets, known_targets)

    def test_query_target_name(self):
        target_name = QueryPharos.query_target_name("16824")
        self.assertEqual(target_name, "Prostacyclin receptor")

    def test_query_target_uniprot_accession(self):
        term = QueryPharos.query_target_uniprot_accession("19672")
        self.assertEqual(term, "P04035")

    def test_query_disease_name(self):
        disease_name = QueryPharos.query_disease_name("501")
        self.assertEqual(disease_name, "Cough")

    def test_query_drug_name(self):
        drug_name = QueryPharos.query_drug_name("254599")
        self.assertEqual(drug_name, "lovastatin")

    def test_query_drug_id_by_name(self):
        drug_id = QueryPharos.query_drug_id_by_name('lovastatin')
        self.assertEqual(drug_id, 254599)

    def test_query_disease_id_by_name(self):
        disease_id = QueryPharos.query_disease_id_by_name("Cough")
        self.assertEqual(disease_id, 501)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryReactomeTests.py

import unittest
from QueryReactome import QueryReactome


class QueryReactomeTestCase(unittest.TestCase):
    def test_query_uniprot_id_to_reactome_pathway_ids_desc(self):
        uniprot_ids = QueryReactome.query_uniprot_id_to_reactome_pathway_ids_desc('P68871')
        # TODO too many IDs here; how to evaluate correctness
        self.assertTrue(len(uniprot_ids) > 0)

    def test_query_reactome_pathway_id_to_uniprot_ids_desc(self):
        pw_ids = QueryReactome.query_reactome_pathway_id_to_uniprot_ids_desc('R-HSA-5423646')
        known_ids = {'P08684': 'CYP3A4',
                     'P20815': 'CYP3A5',
                     'P10620': 'MGST1',
                     'O14880': 'MGST3',
                     'Q99735': 'MGST2',
                     'Q9H4B8': 'DPEP3',
                     'Q9H4A9': 'DPEP2',
                     'P16444': 'DPEP1',
                     'Q16696': 'CYP2A13',
                     'Q03154': 'ACY1',
                     'Q96HD9': 'ACY3',
                     'Q6P531': 'GGT6',
                     'P36269': 'GGT5',
                     'A6NGU5': 'GGT3P',
                     'Q9UJ14': 'GGT7',
                     'P19440': 'GGT1',
                     'P05177': 'CYP1A2',
                     'O95154': 'AKR7A3',
                     'Q8NHP1': 'AKR7L',
                     'O43488': 'AKR7A2'}
        self.assertDictEqual(pw_ids, known_ids)

    def test_query_uniprot_id_to_interacting_uniprot_ids_desc(self):
        res_uniprot_ids = QueryReactome.query_uniprot_id_to_interacting_uniprot_ids_desc('P68871')
        known_ids = {'P69905': 'HBA', 'P02008': 'HBAZ'}

        self.assertDictEqual(res_uniprot_ids, known_ids)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QuerySciGraphTests.py

import unittest
from QuerySciGraph import QuerySciGraph


class QuerySciGraphTestCase(unittest.TestCase):
    def test_get_disont_ids_for_mesh_id(self):
        disont_ids = QuerySciGraph.get_disont_ids_for_mesh_id('MESH:D005199')
        known_ids = {'DOID:13636'}
        self.assertSetEqual(disont_ids, known_ids)

    def test_query_sub_phenotypes_for_phenotype(self):
        sub_phenotypes = QuerySciGraph.query_sub_phenotypes_for_phenotype("HP:0000107")  # Renal cyst
        known_phenotypes = {'HP:0100877': 'Renal diverticulum',
                            'HP:0000108': 'Renal corticomedullary cysts',
                            'HP:0000803': 'Renal cortical cysts',
                            'HP:0000003': 'Multicystic kidney dysplasia',
                            'HP:0008659': 'Multiple small medullary renal cysts',
                            'HP:0005562': 'Multiple renal cysts',
                            'HP:0000800': 'Cystic renal dysplasia',
                            'HP:0012581': 'Solitary renal cyst'}
        self.assertDictEqual(sub_phenotypes, known_phenotypes)


if __name__ == '__main__':
    unittest.main()


# code/reasoningtool/tests/QueryUniprotTests.py

import unittest
from QueryUniprot import QueryUniprot


class QueryUniprotTestCase(unittest.TestCase):
    def test_uniprot_id_to_reactome_pathways(self):
        res_set = QueryUniprot.uniprot_id_to_reactome_pathways("P68871")
        known_set = {'R-HSA-983231', 'R-HSA-2168880', 'R-HSA-1237044',
                     'R-HSA-6798695', 'R-HSA-1247673'}

        self.assertSetEqual(res_set, known_set)


if __name__ == '__main__':
    unittest.main()


# data/remove-duplicate-kegg-compounds.py

import pandas
import requests
import sys

# read the seed_nodes.tsv file


def get_kegg_for_chembl(chembl_id_str):
    url = 'http://mychem.info/v1/chem/' + chembl_id_str
    res = requests.get(url)
    res_kegg = None
    res_brand_names = None
    if res is not None:
        status_code = res.status_code
        if status_code != 200:
            print("status code " + str(status_code) + " for URL: " + url, file=sys.stderr)
            sys.exit()
        else:
            res_json = res.json()
            if res_json is not None:
                res_chebi = res_json.get("chebi")
                if res_chebi is not None:
                    res_kegg = res_chebi.get("kegg_compound_database_links", None)
                    if res_kegg is not None:
                        res_brand_names = res_chebi.get("brand_names", None)

    return [res_kegg, res_brand_names]


def do_conversion():
    seed_node_data = pandas.read_csv('seed_nodes.tsv',
                                     sep="\t",
                                     names=['type', 'rtx_name', 'term', 'purpose'],
                                     header=0,
                                     dtype={'rtx_name': str})

    chemical_substance_terms = set()
    kegg_ids = dict()

    for index, row in seed_node_data.iterrows():
        entity_type = row['type']
        term = row['term']
        id = row['rtx_name']
        if entity_type == 'chemical_substance':
            chemical_substance_terms.add(term)
            kegg_id, brand_names = get_kegg_for_chembl(id)
            if kegg_id is not None:
                if (type(kegg_id) == str):
                    kegg_ids[kegg_id] = brand_names
                else:
                    if (type(kegg_id) == list):
                        for single_id in kegg_id:
                            kegg_ids[single_id] = brand_names
        else:
            if entity_type == 'metabolite':
                # need to check if the ID maps
                assert id.startswith('KEGG:')
                kegg_compound_id = id.split('KEGG:')[1]
                if kegg_compound_id in kegg_ids and kegg_ids[kegg_compound_id] is not None:
                    continue
        print(entity_type + "\t" + id + "\t" + term + "\t" + row['purpose'])

do_conversion()


# notes/test_semmed.py

from SemMedInterface import SemMedInterface
import pandas

# The value entered on initialization dictates the timeout (seconds) on large mysql queries
# This defaults to 30
smi = SemMedInterface(20)

######################
# get_edges_for_node #
######################

print('default get_edges_for_node:')
print(smi.get_edges_for_node("ChEMBL:154", "naproxen"),'\n----------\n')

print('predicate = TREATS get_edges_for_node:')
print(smi.get_edges_for_node("ChEMBL:154", "naproxen", predicate = 'TREATS'),'\n----------\n')

############################################
# get_shortest_path_between_subject_object #
############################################

# This finds the shortest path between two nodes and then returns that path
print('get_shortest_path_between_subject_object:')
print(smi.get_shortest_path_between_subject_object("ChEMBL:154", "naproxen", "DOID:8398", "osteoarthritis", max_length = 2), '\n----------\n')
# The max_length value is 3 by default so it will return None if No path is found at that length

###########################
# get_edges_between_nodes #
###########################

# This function finds all edges between 2 nodes
print('default get_edges_between_nodes:')
print(smi.get_edges_between_nodes("ChEMBL:154", "naproxen", "DOID:8398", "osteoarthritis"), '\n----------\n')

# If set to false it can be a directional relationship i.e. naproxen is the subject and osteoarthritis is the object
print('Not bidirectional get_edges_between_nodes:')
print(smi.get_edges_between_nodes("ChEMBL:154", "naproxen", "DOID:8398", "osteoarthritis", bidirectional = False), '\n----------\n')

# You can also restrict the predicate that gets returned
print('predicate = TREATS get_edges_between_nodes:')
print(smi.get_edges_between_nodes("ChEMBL:154", "naproxen", "DOID:8398", "osteoarthritis", predicate = "TREATS"), '\n----------\n')

# You can also add more columns to be returned. The possible options are 'PMID', 'PREDICATE', 'SUBJECT__CUI', 'SUBJECT_NAME', 'SUBJECT_SEMTYPE', 'OBJECT__CUI', 'OBJECT_NAME', and 'OBJECT_SEMTYPE'
print('extra result columns get_edges_between_nodes:')
print(smi.get_edges_between_nodes("ChEMBL:154", "naproxen", "DOID:8398", "osteoarthritis", result_col = ['PMID', 'SUBJECT_NAME', 'SUBJECT_CUI', 'PREDICATE', 'OBJECT_NAME', 'OBJECT_CUI']), '\n----------\n')

# Note that this returns distinct results so if we remove the PMID result column we get much fewer rows. 
print('fewer columns get_edges_between_nodes:')
print(smi.get_edges_between_nodes("ChEMBL:154", "naproxen", "DOID:8398", "osteoarthritis", result_col = ['SUBJECT_NAME', 'PREDICATE', 'OBJECT_NAME']), '\n----------\n')

###############################################
# get_edges_between_subject_object_with_pivot #
###############################################

# This is really only if you want to specify a specific number of hops instead of finding the shortest path
print('default get_edges_between_subject_object_with_pivot:')
print(smi.get_edges_between_subject_object_with_pivot("ChEMBL:154", "naproxen", "DOID:8398", "osteoarthritis", 1), '\n----------\n')




# code/UI/ClientExamples/Python/ExampleQuery_ARAXi_1.py

""" This example sends a simple set of DSL commands to the ARAX API.
"""

# Import minimal requirements
import requests
import json
import re

# Set the base URL for the ARAX reasoner and its endpoint
endpoint_url = 'https://arax.ncats.io/api/arax/v1.1/query'

# Create a dict of the request, specifying the list of DSL commands
request = { "message": {}, "operations": { "actions": [
            "add_qnode(name=acetaminophen, key=n00)",
            "add_qnode(categories=biolink:Protein, key=n01)",
            "add_qedge(subject=n01, object=n00, key=e00)",
            "expand()",
            "overlay(action=compute_ngd, virtual_relation_label=N1, subject_qnode_key=n00, object_qnode_key=n01)",
            "resultify()",
            "filter_results(action=limit_number_of_results, max_results=10)",
            ] } }

# Send the request to RTX and check the status
print(f"INFO: Sending DSL program to {endpoint_url}")
response_content = requests.post(endpoint_url, json=request, headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    response_dict = response_content.json()
    print(json.dumps(response_dict, indent=2, sort_keys=True))
    exit()

# Unpack the response content into a dict
response_dict = response_content.json()
#print(json.dumps(response_dict, indent=2, sort_keys=True))

# Display the information log
for message in response_dict['logs']:
    if message['level'] != 'DEBUG':
        print(f"{message['timestamp']}: {message['level']}: {message['message']}")

# Display the results
print("Results:")
for result in response_dict['message']['results']:
    confidence = result['confidence']
    if confidence is None:
        confidence = 0.0
    print("  -" + '{:6.3f}'.format(confidence) + f"\t{result['essence']}")

# These URLs provide direct access to resulting data and GUI
print(f"Data: {response_dict['id']}")
if response_dict['id'] is not None:
    match = re.search(r'(\d+)$', response_dict['id'])
    if match:
        print(f"GUI: https://arax.ncats.io/?r={match.group(1)}")


# code/UI/ClientExamples/Python/ExampleQuery_ARAXi__Workflow_Combo.py

""" This example sends a simple set of DSL commands to the ARAX API.
"""

# Import minimal requirements
import requests
import json
import re

# Set the base URL for the ARAX reasoner and its endpoint
endpoint_url = 'https://arax.ncats.io/api/arax/v1.1/query'

# Create a dict of the request, specifying the list of DSL commands
request = {
    "message": {},
        "operations": { "actions": [
            "add_qnode(name=acetaminophen, key=n00)",
            "add_qnode(categories=biolink:Protein, key=n01)",
            "add_qedge(subject=n01, object=n00, key=e00)",
            "expand()",
            "overlay(action=compute_ngd, virtual_relation_label=N1, subject_qnode_key=n00, object_qnode_key=n01)",
            "resultify()",
            ] },
        "workflow": [
            { "id": "filter_results_top_n", "parameters": { "max_results": 17 } }
            ]
}

# Send the request to RTX and check the status
print(f"INFO: Sending ARAXi + workflow combo program to {endpoint_url}")
response_content = requests.post(endpoint_url, json=request, headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    response_dict = response_content.json()
    print(json.dumps(response_dict, indent=2, sort_keys=True))
    exit()

# Unpack the response content into a dict
response_dict = response_content.json()
#print(json.dumps(response_dict, indent=2, sort_keys=True))

# Display the information log
for message in response_dict['logs']:
    if True or message['level'] != 'DEBUG':
        print(f"{message['timestamp']}: {message['level']}: {message['message']}")

# Display the results
print(f"Results ({len(response_dict['message']['results'])}):")
for result in response_dict['message']['results']:
    confidence = 0.0
    if 'confidence' in result:
        confidence = result['confidence']
    if confidence is None:
        confidence = 0.0
    essence = '?'
    if 'essence' in result:
        essence = result['essence']
    print("  -" + '{:6.3f}'.format(confidence) + f"\t{essence}")

# These URLs provide direct access to resulting data and GUI
print(f"Data: {response_dict['id']}")
if response_dict['id'] is not None:
    match = re.search(r'(\d+)$', response_dict['id'])
    if match:
        print(f"GUI: https://arax.ncats.io/NewFmt/?r={match.group(1)}")


# code/UI/ClientExamples/Python/ExampleQuery_Operations_1.py

""" This example sends a simple set of DSL commands to the ARAX API.
"""

# Import minimal requirements
import requests
import json
import re

# Set the base URL for the ARAX reasoner and its endpoint
endpoint_url = 'https://arax.ncats.io/api/arax/v1.1/query'

# Create a dict of the request, specifying the list of DSL commands
request = { "message": {}, "operations": { 
          "message_uris": [ "https://arax.ncats.io/api/arax/v1.1/response/9852" ],
          "actions": [
            "overlay(action=compute_ngd, virtual_relation_label=N1, subject_qnode_key=n0, object_qnode_key=n1)",
            "resultify()",
            ] } }

# Send the request to RTX and check the status
response_content = requests.post(endpoint_url, json=request, headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    response_dict = response_content.json()
    print(json.dumps(response_dict, indent=2, sort_keys=True))
    exit()

# Unpack the response content into a dict
response_dict = response_content.json()
#print(json.dumps(response_dict, indent=2, sort_keys=True))

# Display the information log
for message in response_dict['logs']:
    if True or message['level'] != 'DEBUG':
        print(f"{message['timestamp']}: {message['level']}: {message['message']}")

# Display the results
print("Results:")
for result in response_dict['message']['results']:
    confidence = result['confidence']
    if confidence is None:
        confidence = 0.0
    print("  -" + '{:6.3f}'.format(confidence) + f"\t{result['essence']}")

# These URLs provide direct access to resulting data and GUI
print(f"Data: {response_dict['id']}")
if response_dict['id'] is not None:
    match = re.search(r'(\d+)$', response_dict['id'])
    if match:
        print(f"GUI: https://arax.ncats.io/?r={match.group(1)}")


# code/UI/ClientExamples/Python/ExampleQuery_Operations_2.py

""" This example sends a simple set of DSL commands to the ARAX API.
"""

# Import minimal requirements
import requests
import json
import re

# Set the base URL for the ARAX reasoner and its endpoint
endpoint_url = 'https://arax.ncats.io/api/arax/v1.1'

# Create a dict of the request, includes a reference to a previous response and a filter action
request = { "message": {}, "operations": { 
          "message_uris": [ f"https://arax.ncats.io/api/arax/v1.1/response/9857" ],
          "actions": [
            "filter_results(action=limit_number_of_results, max_results=7)",
            ] } }

# Send the request to RTX and check the status
response_content = requests.post(endpoint_url + '/query', json=request, headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    print(response_content)
    exit()

# Unpack the response content into a dict
response_dict = response_content.json()

# Display the information log
for message in response_dict['logs']:
    if True or message['level'] != 'DEBUG':
        print(f"{message['timestamp']}: {message['level']}: {message['message']}")

# Display the results
print(f"Results ({len(response_dict['message']['results'])}):")
for result in response_dict['message']['results']:
    confidence = 0.0
    if 'confidence' in result:
        confidence = result['confidence']
    if confidence is None:
        confidence = 0.0
    essence = '?'
    if 'essence' in result:
        essence = result['essence']
    print("  -" + '{:6.3f}'.format(confidence) + f"\t{essence}")

# These URLs provide direct access to resulting data and GUI
print(f"Data: {response_dict['id']}")
if response_dict['id'] is not None:
    match = re.search(r'(\d+)$', response_dict['id'])
    if match:
        print(f"GUI: https://arax.ncats.io/?r={match.group(1)}")


# code/UI/ClientExamples/Python/ExampleQuery_Operations_3.py

""" This example sends a simple set of DSL commands to the ARAX API.
"""

# Import minimal requirements
import requests
import json
import re

# Set the base URL for the ARAX reasoner and its endpoint
endpoint_url = 'https://arax.ncats.io/api/arax/v1.1'

# Get a message to work on
response_content = requests.get(endpoint_url + '/response/1515f73a-ae31-48f1-b2fa-e4a955247d5d', headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    print(response_content)
    exit()

# Unpack the response content into a dict
response_dict = response_content.json()
print(f"Have a message with {len(response_dict['message']['results'])} results")
#del response_dict['message']['query_graph']['nodes']['n01']['id']
#print(json.dumps(response_dict['message']['query_graph'], indent=2, sort_keys=True))

# Create a dict of the request, includes a reference to a previous response and a filter action
request = { "message": response_dict['message'], "operations": { 
          "actions": [
            "filter_results(action=limit_number_of_results, max_results=7)",
            ] } }

# Send the request to RTX and check the status
response_content = requests.post(endpoint_url + '/query', json=request, headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    print(response_content)
    exit()

# Unpack the response content into a dict
response_dict = response_content.json()

# Display the information log
for message in response_dict['logs']:
    if True or message['level'] != 'DEBUG':
        print(f"{message['timestamp']}: {message['level']}: {message['message']}")

# Display the results
print(f"Results ({len(response_dict['message']['results'])}):")
for result in response_dict['message']['results']:
    confidence = 0.0
    if 'confidence' in result:
        confidence = result['confidence']
    if confidence is None:
        confidence = 0.0
    essence = '?'
    if 'essence' in result:
        essence = result['essence']
    print("  -" + '{:6.3f}'.format(confidence) + f"\t{essence}")

# These URLs provide direct access to resulting data and GUI
print(f"Data: {response_dict['id']}")
if response_dict['id'] is not None:
    match = re.search(r'(\d+)$', response_dict['id'])
    if match:
        print(f"GUI: https://arax.ncats.io/?r={match.group(1)}")


# code/UI/ClientExamples/Python/ExampleQuery_QueryGraph_1.py

""" This example sends a simple query to the ARAX API.

A one-line curl equivalent is:
curl -X POST "https://arax.ncats.io/api/arax/v1.1/query" -H  "accept: application/json" -H  "Content-Type: application/json" -d "{\"bypass_cache\":false,\"enforce_edge_directionality\":false,\"log_level\":\"DEBUG\",\"max_results\":100,\"message\":{\"query_graph\":{\"edges\":{\"e00\":{\"object\":\"n01\",\"predicates\":[\"biolink:physically_interacts_with\"],\"subject\":\"n00\"}},\"nodes\":{\"n00\":{\"categories\":[\"biolink:ChemicalSubstance\"],\"ids\":[\"CHEMBL.COMPOUND:CHEMBL112\"]},\"n01\":{\"categories\":[\"biolink:Protein\"]}}}},\"operations\":null,\"page_number\":1,\"page_size\":100,\"return_minimal_metadata\":false,\"stream_progress\":false,\"workflow\":null}"

To trigger a streaming response of progress, try this:
curl -X POST "https://arax.ncats.io/api/arax/v1.1/query" -H  "accept: application/json" -H  "Content-Type: application/json" -d "{\"bypass_cache\":false,\"enforce_edge_directionality\":false,\"log_level\":\"DEBUG\",\"max_results\":100,\"message\":{\"query_graph\":{\"edges\":{\"e00\":{\"object\":\"n01\",\"predicates\":[\"biolink:physically_interacts_with\"],\"subject\":\"n00\"}},\"nodes\":{\"n00\":{\"categories\":[\"biolink:ChemicalSubstance\"],\"ids\":[\"CHEMBL.COMPOUND:CHEMBL112\"]},\"n01\":{\"categories\":[\"biolink:Protein\"]}}}},\"operations\":null,\"page_number\":1,\"page_size\":100,\"return_minimal_metadata\":false,\"stream_progress\":true,\"workflow\":null}"

"""

#### Import some needed modules
import requests
import json
import re

# Set the base URL for the ARAX reasoner and its endpoint
endpoint_url = 'https://arax.ncats.io/api/arax/v1.1/query'

#### Create a dict of the request, specifying the query type and its parameters
#### Note that predicates and categories must have the biolink: prefix to be valid
query = { "message": {
    "query_graph": {
        "edges": {
            "e00": {
                "subject": "n00",
                "object": "n01",
                "predicates": [ "biolink:physically_interacts_with" ]
            }
        },
        "nodes": {
            "n00": {
                "ids": [ "CHEMBL.COMPOUND:CHEMBL112" ],
                "categories": [ "biolink:ChemicalSubstance" ]
            },
            "n01": {
                "categories": [ "biolink:Protein" ]
            }
        }
    }
}}

# Send the request to RTX and check the status
response_content = requests.post(endpoint_url, json=query, headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    print(response_content.json())
    exit()

#### Unpack the response content into a dict
response_dict = response_content.json()

#print(json.dumps(response_dict, indent=4, sort_keys=True))

# Display the information log
for message in response_dict['logs']:
    if message['level'] != 'DEBUG':
        print(f"{message['timestamp']}: {message['level']}: {message['message']}")

# Display the results
print("Results:")
for result in response_dict['message']['results']:
    confidence = result['confidence']
    if confidence is None:
        confidence = 0.0
    print("  -" + '{:6.3f}'.format(confidence) + f"\t{result['essence']}")

# These URLs provide direct access to resulting data and GUI
print(f"Data: {response_dict['id']}")
if response_dict['id'] is not None:
    match = re.search(r'(\d+)$', response_dict['id'])
    if match:
        print(f"GUI: https://arax.ncats.io/?r={match.group(1)}")

# Also print the operations actions
print(f"Executed operations:")
if 'operations' in response_dict and response_dict['operations'] is not None:
    if 'actions' in response_dict['operations'] and response_dict['operations']['actions'] is not None:
        for action in response_dict['operations']['actions']:
            print(f"  - {action}")


# code/UI/ClientExamples/Python/ExampleQuery_ReRankTest.py

""" This example sends a simple workflow request to the ARAX API.
"""

# Import minimal requirements
import requests
import json
import re
import argparse
import os
import sys

pathlist = os.path.realpath(__file__).split(os.path.sep)
RTXindex = pathlist.index("RTX")
sys.path.append(os.path.sep.join([*pathlist[:(RTXindex + 1)], 'code','ARAX','ARAXQuery']))
from ARAX_query import ARAXQuery

parser = argparse.ArgumentParser()
parser.add_argument("-l", "--local", action='store_true')
arguments = parser.parse_args()

def _do_arax_query(query: dict):
    araxq = ARAXQuery()
    response = araxq.query(query)
    if response.status != 'OK':
        print(response.show(level=response.DEBUG))
    return [response, response.envelope.message]

# Set the base URL for the ARAX reasonaer TRAPI 1.1 base
endpoint_url = 'https://arax.ncats.io/beta/api/arax/v1.2'

# Get an existing message to work on
# One of our own results
#response_content = requests.get(endpoint_url + '/response/35965', headers={'accept': 'application/json'})
# Try a BTE result from a recent standup
#response_content = requests.get(endpoint_url + '/response/843990fb-4cd0-46c0-95cb-644990b226e5', headers={'accept': 'application/json'})
# Try reranking a Chris example
# clinical DCP
# results: https://arax.ncats.io/beta/?r=36704
# rerank_endpoint_url = 'https://raw.githubusercontent.com/TranslatorSRI/RankingComparison/main/inputs/clinical_DCP/result.json'
# genes_genetically_associated_to_asthma
# Results: https://arax.ncats.io/beta/?r=36703
# rerank_endpoint_url = 'https://raw.githubusercontent.com/TranslatorSRI/RankingComparison/main/inputs/genes_genetically_associated_to_asthma/result.json'
# treats_hyperlipidemia
# Results: https://arax.ncats.io/beta/?r=36702
# rerank_endpoint_url = 'https://raw.githubusercontent.com/TranslatorSRI/RankingComparison/main/inputs/treats_hyperlipidemia/result.json'
# two_hop_acromegaly
# Results: https://arax.ncats.io/beta/?r=36742
rerank_endpoint_url = 'https://raw.githubusercontent.com/TranslatorSRI/RankingComparison/main/inputs/two_hop_acromegaly/result.json'
# two_hop_hypothyroidism (not up yet)
# Results: 
# rerank_endpoint_url = ''
response_content = requests.get(rerank_endpoint_url, headers={'accept': 'application/json'})
# Error test
# response_content = requests.get(endpoint_url + '/response/99adc6b5-0803-4c52-972e-fe587744a7aa', headers={'accept': 'application/json'})

status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    print(response_content)
    exit()

# Unpack the response content into a dict
response_dict = response_content.json()
print(f"Retrieved a message with {len(response_dict['message']['results'])} results:")

# Summarize the results
for result in response_dict['message']['results']:
    if 'score' in result:
        score = result['score']
        if score is None:
            score = str(score)
        else:
            score = '{:6.3f}'.format(score)
        result['score'] = None
    else:
        score = '???'
    essence = '?'
    if 'essence' in result:
        essence = result['essence']
    print("  -" + score + f"\t{essence}")

print(f"\nAfter score removal:")
for result in response_dict['message']['results']:
    if 'score' in result:
        score = result['score']
    else:
        score = '???'
    essence = '?'
    if 'essence' in result:
        essence = result['essence']
    print("  -" + str(score) + f"\t{essence}")


# Create a new request, including previous response message and a simple filter_results_top_n action
print("\nCreate a new request with the previous message and a workflow to rerank (overlay_connect_knodes,complete_results,score)")
request = {
    "message": response_dict['message'],
    "workflow": [
        {
        "id": "overlay_connect_knodes"
        },
        {
        "id": "score"
        },
  ]
 }

if arguments.local:
    [response_dict, message_dict] = _do_arax_query(request)
    message_dict = message_dict.to_dict()
    response_dict = {"message":message_dict}
else:
    # Send the request to ARAX and check the status
    response_content = requests.post(endpoint_url + '/query', json=request, headers={'accept': 'application/json'})
    status_code = response_content.status_code
    if status_code != 200:
        print("ERROR returned with status "+str(status_code))
        print(response_content)
        exit()

    # Unpack the JSON response content into a dict
    response_dict = response_content.json()

# Display a summary of the results
print(f"Results ({len(response_dict['message']['results'])}):")
for result in response_dict['message']['results']:
    if 'score' in result:
        score = result['score']
    else:
        score = '???'
    essence = '?'
    if 'essence' in result:
        essence = result['essence']
    print("  -" + '{:6.3f}'.format(score) + f"\t{essence}")


if not arguments.local:
    # These URLs provide direct access to resulting data and GUI
    print(f"Data: {response_dict['id']}")
    if response_dict['id'] is not None:
        match = re.search(r'(\d+)$', response_dict['id'])
        if match:
            print(f"GUI: https://arax.ncats.io/beta/?r={match.group(1)}")


# code/UI/ClientExamples/Python/ExampleQuery_Workflow_1.py

""" This example sends a simple workflow request to the ARAX API.
"""

# Import minimal requirements
import requests
import json
import re

# Set the base URL for the ARAX reasonaer TRAPI 1.1 base
endpoint_url = 'https://arax.ncats.io/api/arax/v1.1'

# Get an existing message to work on
response_content = requests.get(endpoint_url + '/response/1515f73a-ae31-48f1-b2fa-e4a955247d5d', headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    print(response_content)
    exit()

# Unpack the response content into a dict
response_dict = response_content.json()
print(f"Retrieved a message with {len(response_dict['message']['results'])} results")

# Create a new request, including previous response message and a simple filter_results_top_n action
request = {
    "message": response_dict['message'],
    "workflow": [
        { "id": "filter_results_top_n", "parameters": { "max_results": 15 } }
        ] }

# Send the request to ARAX and check the status
response_content = requests.post(endpoint_url + '/query', json=request, headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    print(response_content)
    exit()

# Unpack the JSON response content into a dict
response_dict = response_content.json()

# Display the information log
for message in response_dict['logs']:
    if True or message['level'] != 'DEBUG':
        print(f"{message['timestamp']}: {message['level']}: {message['message']}")

# Display a summary of the results
print(f"Results ({len(response_dict['message']['results'])}):")
for result in response_dict['message']['results']:
    confidence = 0.0
    if 'confidence' in result:
        confidence = result['confidence']
    if confidence is None:
        confidence = 0.0
    essence = '?'
    if 'essence' in result:
        essence = result['essence']
    print("  -" + '{:6.3f}'.format(confidence) + f"\t{essence}")

# These URLs provide direct access to resulting data and GUI
print(f"Data: {response_dict['id']}")
if response_dict['id'] is not None:
    match = re.search(r'(\d+)$', response_dict['id'])
    if match:
        print(f"GUI: https://arax.ncats.io/?r={match.group(1)}")


# code/UI/ClientExamples/Python/ExampleQuery_Workflow_2.py

""" This example sends a simple workflow request to the ARAX API.
"""

# Import minimal requirements
import requests
import json
import re

# Set the base URL for the ARAX reasonaer TRAPI 1.1 base
endpoint_url = 'https://arax.ncats.io/api/arax/v1.1'


# Create a new request, with a query graph and workflow steps
request = {
  "message": {
    "query_graph": {
      "edges": {
        "e00": {
          "object": "n01",
          "predicates": [
            "biolink:physically_interacts_with"
          ],
          "subject": "n00"
        }
      },
      "nodes": {
        "n00": {
          "categories": [
            "biolink:ChemicalSubstance"
          ],
          "ids": [
            "CHEMBL.COMPOUND:CHEMBL112"
          ]
        },
        "n01": {
          "categories": [
            "biolink:Protein"
          ]
        }
      }
    }
  },
  "workflow": [
      { "id": "fill" },
      { "id": "bind" },
      { "id": "filter_results_top_n", "parameters": { "max_results": 15 } }
    ]
}

# Send the request to ARAX and check the status
print(f"INFO: Sending QueryGraph + workflow combo program to {endpoint_url}")
response_content = requests.post(endpoint_url + '/query', json=request, headers={'accept': 'application/json'})
status_code = response_content.status_code
if status_code != 200:
    print("ERROR returned with status "+str(status_code))
    print(response_content)
    exit()

# Unpack the JSON response content into a dict
response_dict = response_content.json()

# Display the information log
for message in response_dict['logs']:
    if True or message['level'] != 'DEBUG':
        print(f"{message['timestamp']}: {message['level']}: {message['message']}")

# Display a summary of the results
print(f"Results ({len(response_dict['message']['results'])}):")
for result in response_dict['message']['results']:
    confidence = 0.0
    if 'confidence' in result:
        confidence = result['confidence']
    if confidence is None:
        confidence = 0.0
    essence = '?'
    if 'essence' in result:
        essence = result['essence']
    print("  -" + '{:6.3f}'.format(confidence) + f"\t{essence}")

# These URLs provide direct access to resulting data and GUI
print(f"Data: {response_dict['id']}")
if response_dict['id'] is not None:
    match = re.search(r'(\d+)$', response_dict['id'])
    if match:
        print(f"GUI: https://arax.ncats.io/?r={match.group(1)}")


# code/autocomplete/create_load_db.py

#!/bin/env python3

import os
import re
import sqlite3
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-o", "--output", type=str, help="Output database path", default="autocomplete.sqlite", required=False)
parser.add_argument("-i", "--input", type=str, help="Input file path", default="../../data/KGmetadata/NodeNamesDescriptions_KG2.tsv", required=False)
arguments = parser.parse_args()

database_name = arguments.output
try:
    os.remove(database_name)
except:
    pass

#create a data structure
conn = sqlite3.connect(database_name)
conn.text_factory = str
c = conn.cursor()

print(f"Creating tables")
#c.execute(f"CREATE TABLE {tablename}(curie TEXT, name TEXT, type TEXT, rank INTEGER)")
c.execute(f"CREATE TABLE terms(term VARCHAR(255) COLLATE NOCASE)")
c.execute(f"CREATE TABLE cached_fragments(fragment VARCHAR(255) COLLATE NOCASE)")
c.execute(f"CREATE TABLE cached_fragment_terms(fragment_id INT, term VARCHAR(255) COLLATE NOCASE)")

rank = 1
row_count = 0
uc_terms = {}

with open(arguments.input, 'r', encoding="latin-1", errors="replace") as nodeData:
    print("Loading node names")
    for line in nodeData:
        curie, name, full_name, type = line[:-1].split("\t")
        #c.execute("INSERT INTO term(curie,name,type,rank) VALUES(?,?,?,?)" % (tablename), (curie,name,type,rank,))

        for term in [ name, curie ]:

            uc_term = term.upper()
            if uc_term not in uc_terms:
                c.execute("INSERT INTO terms(term) VALUES(?)", (term,))
                uc_terms[uc_term] = 1

        row_count += 1
        if row_count == int(row_count/1000000) * 1000000:
            print(f"{row_count}...", end='', flush=True)
            #break

print()
print(f"Creating indexes")
c.execute(f"CREATE INDEX idx_terms_term ON terms(term)")
c.execute(f"CREATE INDEX idx_cached_fragments_fragment ON cached_fragments(fragment)")

conn.commit()
conn.close()


# code/autocomplete/quick_def_creator.py

import json

def get_data_for_json(source):
    data = {}

    # read data for instant definitions here
    # either from source (knowledge graph, db, etc) or somewhere else
    # all keys should be lower case
    # definition can contain html for formatting
    
    #dummy data for testing
    data["lovastatin"] = "<strong>Lovastatin</strong> is a statin drug, used for lowering cholesterol in those with hypercholesterolemia to reduce risk of cardiovascular disease."
    data["hemoglobin"] = "<strong>Hemoglobin</strong> or haemoglobin, abbreviated Hb or Hgb, is the iron-containing oxygen-transport metalloprotein in the red blood cells of all vertebrates (with the exception of the fish family Channichthyidae) as well as the tissues of some invertebrates."
    data["cancer"] = "<strong>Cancer</strong> is a group of diseases involving abnormal cell growth with the potential to invade or spread to other parts of the body."

    return data

def write_data_to_json(data):
    with open('./data/quick_def.json','w') as output:
        output.write("var quick_def = ")
        output.write(json.dumps(data))

data = get_data_for_json(None)
write_data_to_json(data)


# code/autocomplete/rtxcomplete.py

import sqlite3
import re
import timeit
import sys
import os
def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs)

pathlist = os.path.realpath(__file__).split(os.path.sep)
RTXindex = pathlist.index("RTX")
sys.path.append(os.path.sep.join([*pathlist[:(RTXindex + 1)], 'code']))
from RTXConfiguration import RTXConfiguration

RTXConfig = RTXConfiguration()
autocomplete_filepath = os.path.sep.join([*pathlist[:(RTXindex + 1)], 'code', 'autocomplete'])


conn = None
cursor = None
cache_conn = None
cache_cursor = None


def load():
    global conn
    global cursor
    global cache_conn
    global cache_cursor
    database_name = f"{autocomplete_filepath}{os.path.sep}{RTXConfig.autocomplete_path.split('/')[-1]}"
    conn = sqlite3.connect(database_name)
    cursor = conn.cursor()
    print(f"INFO: Connected to {database_name}",file=sys.stderr)

    cache_database_name = os.path.dirname(os.path.abspath(__file__)) + '/rtxcomplete_cache.sqlite'
    cache_conn = sqlite3.connect(cache_database_name)
    cache_cursor = cache_conn.cursor()
    print(f"INFO: Connected to {cache_database_name}",file=sys.stderr)
    cache_cursor.execute("CREATE TABLE IF NOT EXISTS cached_fragments(fragment VARCHAR(1024))")
    cache_cursor.execute("CREATE TABLE IF NOT EXISTS cached_fragment_terms(fragment_id VARCHAR(1024), term VARCHAR(1024))")

    return True


def get_nodes_like(word,requested_limit):

    debug = True

    t0 = timeit.default_timer()
    requested_limit = int(requested_limit)

    values = []
    n_values = 0

    if len(word) < 2:
        return values

    #### Try to avoid SQL injection exploits by sanitizing input #1823
    word = word.replace('"','')

    floor = word[:-1]
    ceiling = floor + 'zz'

    #### Get a list of matching node names that begin with these letters
    if debug:
        eprint(f"INFO: Query 1")
    #cursor.execute("SELECT term FROM term WHERE term LIKE \"%s%%\" ORDER BY length(term),term LIMIT %s" % (word,1000))
    cursor.execute(f"SELECT term FROM terms WHERE term > \"{floor}\" AND term < \"{ceiling}\" AND term LIKE \"{word}%%\" ORDER BY length(term),term LIMIT {requested_limit}")
    rows = cursor.fetchall()
    values_dict = {}
    for row in rows:
        term = row[0]
        if term.upper() not in values_dict:
            if debug:
                eprint(f"    - {term}")
            properties = { "curie": '??', "name": term, "type": '??' }
            values.append(properties)
            values_dict[term.upper()] = 1
            n_values += 1
            if n_values >= requested_limit:
                break
    t1 = timeit.default_timer()
    if debug:
        eprint(f"INFO: Query 1 in {t1-t0} sec")

    #### If we haven't reached the limit yet, add a list of matching terms that contain this string
    if n_values < requested_limit:
        if debug:
            eprint(f"INFO: Query 2")

        #### See if there is a cached entry already
        word_part = word
        found_fragment = None
        while len(word_part) > 2:
            cache_cursor.execute(f"SELECT rowid, fragment FROM cached_fragments WHERE fragment == \"{word_part}\"")
            rows = cache_cursor.fetchall()
            if len(rows) > 0:
                fragment_id = rows[0][0]
                found_fragment = rows[0][1]
                break
            word_part = word_part[:-1]

        if found_fragment:
            if debug:
                eprint(f"Found matching fragment {found_fragment} as fragment_id {fragment_id}")

            cache_cursor.execute(f"SELECT term FROM cached_fragment_terms WHERE fragment_id = {fragment_id} AND term LIKE \"%%{word}%%\"")
            rows = cache_cursor.fetchall()

            for row in rows:
                term = row[0]
                if term.upper() not in values_dict:

                    if n_values < requested_limit:
                        if debug:
                            print(f"    - {term}")
                        properties = { "curie": '??', "name": term, "type": '??' }
                        values.append(properties)
                        n_values += 1


        if found_fragment is None:

            #### Cache this fragment in the database
            try:
                cache_cursor.execute("INSERT INTO cached_fragments(fragment) VALUES(?)", (word,))
                fragment_id = cache_cursor.lastrowid
            except:
                eprint(f"ERROR: Unable to INSERT into cached_fragments(fragment)",file=sys.stderr)
                fragment_id = 0
            if debug:
                eprint(f"fragment_id = {fragment_id}")

            #### Execute an expensive LIKE query
            cursor.execute("SELECT term FROM terms WHERE term LIKE \"%%%s%%\" ORDER BY length(term),term LIMIT %s" % (word,10000))
            rows = cursor.fetchall()

            for row in rows:
                term = row[0]
                if term.upper() not in values_dict:

                    if n_values < requested_limit:
                        if debug:
                            eprint(f"    - {term}")
                        properties = { "curie": '??', "name": term, "type": '??' }
                        values.append(properties)
                        n_values += 1

                    values_dict[term.upper()] = 1
                    cache_cursor.execute("INSERT INTO cached_fragment_terms(fragment_id, term) VALUES(?,?)", (fragment_id, term,))
            cache_conn.commit()

        t2 = timeit.default_timer()
        if debug:
            eprint(f"INFO: Query 2 in {t2-t1} sec")


    return(values)



# code/autocomplete/sample.py

"""
Example External App that uses rtxcomplete
"""

import rtxcomplete

def test_prefix():
  completions = rtxcomplete.prefix("NF", 10)
  print(completions)
  # add assertion here

def test_fuzzy():
  matches = rtxcomplete.fuzzy("NF", 10)
  print(matches)
  # add assertion here
  
if rtxcomplete.load():
  test_prefix()
  test_fuzzy()


# code/autocomplete/server.py

import tornado.ioloop
import tornado.web
import os
#import sqlite3
import json
import sys
import rtxcomplete
import traceback
import re

root = os.path.dirname(os.path.abspath(__file__))
rtxcomplete.load()


#### Sanitize the client-provided callback function name
def sanitize_callback(callback):
    if callback is None or not isinstance(callback,str):
        return 'autocomplete_callback'
    match = re.match(r'([a-zA-Z0-9_]+).*$', callback)
    if match:
         callback = match.group(1)
    else:
         callback = 'autocomplete_callback'
    return callback


class autoSearch(tornado.web.RequestHandler):

    def get(self, arg,word=None):
        try:
            limit = self.get_argument("limit")
            word = self.get_argument("word")
            callback = sanitize_callback(self.get_argument("callback"))

            result = rtxcomplete.prefix(word,limit)
            
            result = callback+"("+json.dumps(result)+");"
            
            self.write(result)
            
        except:
            print(sys.exc_info()[:])
            traceback.print_tb(sys.exc_info()[-1])
            #print sys.exc_info()[2]
            self.write("error")

class fuzzySearch(tornado.web.RequestHandler):
    def get(self, arg,word=None):
        #print "matched fuzzy"
        try:
            limit = self.get_argument("limit")
            word = self.get_argument("word")
            callback = sanitize_callback(self.get_argument("callback"))
            #print word
            #cursor.execute("SELECT word FROM spell WHERE word MATCH \"" + word + "\" LIMIT " + limit)
            #cursor.execute("SELECT word FROM spell WHERE word MATCH \"" + word + "*\" LIMIT " + limit)
            result = rtxcomplete.fuzzy(word,limit);
            #rows = cursor.fetchall()
            #print type(rows)
            result = callback+"("+json.dumps(result)+");"
            #print arg, result
            #if (len(rows) > 0):
            self.write(result)
            #else:
            #self.write(callback+"("+json.dumps([["NO SUGGESTIONS"]])+");")
            #self.write(json.dumps(rows))
        except:
            print(sys.exc_info()[:])
            traceback.print_tb(sys.exc_info()[-1])
            #print sys.exc_info()[:]
            self.write("error")

class autofuzzySearch(tornado.web.RequestHandler):
    def get(self, arg,word=None):
        #print "matched autofuzzy"
        try:
            limit = self.get_argument("limit")
            word = self.get_argument("word")
            callback = sanitize_callback(self.get_argument("callback"))
            #print word
            #cursor.execute("SELECT word FROM spell WHERE word MATCH \"" + word + "\" LIMIT " + limit)
            #cursor.execute("SELECT word FROM spell WHERE word MATCH \"" + word + "*\" LIMIT " + limit)
            result = rtxcomplete.autofuzzy(word,limit);
            #rows = cursor.fetchall()
            #print type(rows)
            result = callback+"("+json.dumps(result)+");"
            #print arg, result
            #if (len(rows) > 0):
            self.write(result)
            #else:
            #self.write(callback+"("+json.dumps([["NO SUGGESTIONS"]])+");")
            #self.write(json.dumps(rows))
        except:
            print(sys.exc_info()[:])
            traceback.print_tb(sys.exc_info()[-1])
            #print sys.exc_info()[:]
            self.write("error")


class nodesLikeSearch(tornado.web.RequestHandler):
    def get(self, arg,word=None):
        try:
            limit = self.get_argument("limit")
            word = self.get_argument("word")
            callback = sanitize_callback(self.get_argument("callback"))
            result = rtxcomplete.get_nodes_like(word,limit);
            result = callback+"("+json.dumps(result)+");"
            self.write(result)
        except:
            print(sys.exc_info()[:])
            traceback.print_tb(sys.exc_info()[-1])
            self.write("error")


class defineSearch(tornado.web.RequestHandler):
    def get(self, arg,word=None):
        print("matched define search: not implemented")
        self.write("")
            
def make_https_app():
    return tornado.web.Application([
        #(r"/", MainHandler),
        (r"/autofuzzy(.*)", autofuzzySearch),
        (r"/auto(.*)", autoSearch),
        (r"/fuzzy(.*)", fuzzySearch),
        (r"/define(.*)", defineSearch),
        (r"/nodeslike(.*)", nodesLikeSearch),
        (r"/(.*)", tornado.web.StaticFileHandler,
         {"path": root, "default_filename": "rtxcomplete.html"}),
    ],
        compress_response= True)

class redirect_handler(tornado.web.RequestHandler):
    def prepare(self):
        if self.request.protocol == 'http':
            if self.request.host == "rtxcomplete.ixlab.org":
                self.redirect('https://'+self.request.host, permanent=False)
                
    def get(self):
        self.write("Looks like you're trying to access rtxcomplete at the wrong host name.")
        self.write("<br>Please make sure the address is correct: 'rtxcomplete.ixlab.org'")

def make_redirect_app():
    return tornado.web.Application([
        (r'/', redirect_handler)
    ])

if __name__ == "__main__":
    print("root: " + root)

    if True: #FW/EWD: clean this up later
        http_app = make_https_app()
        http_server = tornado.httpserver.HTTPServer(http_app)
        http_server.listen(4999)

    else:
        redirect_app = make_redirect_app()
        redirect_app.listen(80)

        https_app = make_https_app()
        https_server = tornado.httpserver.HTTPServer(https_app, ssl_options={
            "certfile": "/etc/letsencrypt/live/rtxcomplete.ixlab.org/fullchain.pem",
            "keyfile" : "/etc/letsencrypt/live/rtxcomplete.ixlab.org/privkey.pem",
            })
        https_server.listen(443)

    tornado.ioloop.IOLoop.current().start()


# code/autocomplete/testFuzzy1.py

#!/env python3

import os
import sys
import timeit

import rtxcomplete

rtxcomplete.load()
limit = 20
word = "mala"
word = " insulin"
#word = "aceta"
#word = "acetam"
#word = "acetamino"
#word = "profe"
word = "profer"
#word = "malarials"
#word = "P0123"
#word = "ABCG"

t0 = timeit.default_timer()
result = rtxcomplete.get_nodes_like(word,limit)
t1 = timeit.default_timer()

print("======================")
if 1:
    for term in result:
        print(f" - {term['name']}")
else:
    print(result)

print(f"INFO: Information retrieved in {t1-t0} sec")


# code/kg2c/build_kg2c.py

#!/bin/env python3
"""
This script creates a canonicalized version of KG2 stored in various file formats, including TSVs ready for import
into Neo4j. Files are created in the directory this script is in. It relies on the options you specify in
kg2c_config.json; in particular, the KG2c will be built off of the KG2 endpoint you specify in that config file.
Usage: python3 build_kg2c.py [--test]
"""
import argparse
import logging
import pathlib
import json
import os
import subprocess
import sys
import time

sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from create_kg2c_files import create_kg2c_files
from record_kg2c_meta_info import record_meta_kg_info
sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../")  # code directory
from RTXConfiguration import RTXConfiguration

KG2C_DIR = f"{os.path.dirname(os.path.abspath(__file__))}"
CODE_DIR = f"{KG2C_DIR}/.."


def _setup_config_dbs_file(kg2pre_neo4j_endpoint: str, synonymizer_name: str):
    """
    This function locally modifies config_dbs.json to point to the right KG2pre Neo4j and synonymizer.
    """
    logging.info("Creating a config_dbs.json file pointed to the right synonymizer and KG2pre Neo4j..")
    config_dbs_file_path = f"{CODE_DIR}/config_dbs.json"

    # Save a copy of any pre-existing config_dbs.json so we don't overwrite it
    original_config_dbs_file = pathlib.Path(config_dbs_file_path)
    if original_config_dbs_file.exists():
        subprocess.check_call(["mv", config_dbs_file_path, f"{config_dbs_file_path}_KG2CBUILDTEMP"])
        subprocess.check_call(["cp", f"{config_dbs_file_path}_KG2CBUILDTEMP", config_dbs_file_path])

    RTXConfiguration()  # Regenerates config_secrets.json with the latest version
    with open(config_dbs_file_path) as config_dbs_file:
        rtx_config_dbs_dict = json.load(config_dbs_file)
    # Point to the 'right' KG2 Neo4j (the one specified in the KG2c config) and synonymizer (we always use simple name)
    rtx_config_dbs_dict["neo4j"]["KG2pre"] = kg2pre_neo4j_endpoint
    rtx_config_dbs_dict["database_downloads"]["node_synonymizer"] = f"/something/{synonymizer_name}"  # Only need name, not full path

    # Save our new config_dbs.json file
    with open(config_dbs_file_path, "w+") as revised_config_dbs_file:
        json.dump(rtx_config_dbs_dict, revised_config_dbs_file)


def _upload_output_files_to_s3():
    logging.info("Uploading KG2c json and TSV files to S3..")
    tarball_path = f"{KG2C_DIR}/kg2c-tsv.tar.gz"
    json_lite_file_path = f"{KG2C_DIR}/kg2c_lite.json"
    subprocess.check_call(["tar", "-czvf", tarball_path, "nodes_c.tsv", "nodes_c_header.tsv", "edges_c.tsv", "edges_c_header.tsv"])
    subprocess.check_call(["aws", "s3", "cp", "--no-progress", "--region", "us-west-2", tarball_path, "s3://rtx-kg2/"])
    subprocess.check_call(["gzip", "-f", json_lite_file_path])
    subprocess.check_call(["aws", "s3", "cp", "--no-progress", "--region", "us-west-2", f"{json_lite_file_path}.gz", "s3://rtx-kg2/"])


def main():
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s %(levelname)s: %(message)s',
                        handlers=[logging.FileHandler("build.log"),
                                  logging.StreamHandler()])
    start = time.time()
    # Grab any parameters passed to this script
    arg_parser = argparse.ArgumentParser()
    arg_parser.add_argument("--test", dest="test", action='store_true', default=False)
    args = arg_parser.parse_args()
    logging.info(f"STARTING {'TEST ' if args.test else ''}KG2c BUILD")

    # Load the KG2c config file
    with open(f"{KG2C_DIR}/kg2c_config.json") as config_file:
        kg2c_config_info = json.load(config_file)
    kg2_version = kg2c_config_info["kg2pre_version"]
    kg2pre_endpoint = kg2c_config_info["kg2pre_neo4j_endpoint"]
    biolink_version = kg2c_config_info["biolink_version"]
    build_kg2c = kg2c_config_info["kg2c"]["build"]
    upload_to_s3 = kg2c_config_info["kg2c"]["upload_to_s3"]
    build_synonymizer = kg2c_config_info["synonymizer"]["build"]
    synonymizer_name = kg2c_config_info["synonymizer"]["name"]
    upload_to_arax_databases_rtx_ai = kg2c_config_info["upload_to_arax_databases.rtx.ai"]
    upload_directory = kg2c_config_info["upload_directory"]
    logging.info(f"KG2pre version to use is {kg2_version}")
    logging.info(f"KG2pre neo4j endpoint to use is {kg2pre_endpoint}")
    logging.info(f"Biolink model version to use is {biolink_version}")
    logging.info(f"Synonymizer to use is {synonymizer_name}")
    synonymizer_dir = f"{CODE_DIR}/ARAX/NodeSynonymizer"
    synonymizer_file = pathlib.Path(f"{synonymizer_dir}/{synonymizer_name}")
    # Make sure synonymizer settings are valid
    if build_synonymizer and not args.test:
        if not synonymizer_name:
            raise ValueError(f"You must specify the name to give the new synonymizer in kg2c_config.json.")
        elif synonymizer_file.exists():
            raise ValueError(f"kg2c_config.json specifies that a new synonymizer should be built, but the "
                             f"synonymizer name provided already exists in {synonymizer_dir}. You must enter a new "
                             f"synonymizer name.")
        if upload_to_arax_databases_rtx_ai and not upload_directory:
            raise ValueError(f"You must specify the path of the directory on arax.ncats.io to upload synonymizer "
                             f"artifacts to in kg2c_config.json.")
    else:
        if not synonymizer_name:
            raise ValueError(f"You must specify the name of the synonymizer to use in kg2c_config.json since you are "
                             f"not building a new synonymizer.")
        elif not synonymizer_file.exists():
            raise ValueError(f"The synonymizer specified in kg2c_config.json does not exist in {synonymizer_dir}. You "
                             f"must put a copy of it there or use a different synonymizer.")

    # Set up an RTX config_local.json file that points to the right KG2 and synonymizer
    _setup_config_dbs_file(kg2pre_endpoint, synonymizer_name)

    # Build a new node synonymizer, if we're supposed to
    if build_synonymizer and not args.test:
        logging.info("Building node synonymizer off of specified KG2..")
        subprocess.check_call(["bash", "-x", f"{KG2C_DIR}/build-synonymizer.sh"])
        if upload_to_arax_databases_rtx_ai:
            logging.info(f"Uploading synonymizer artifacts to arax-databases.rtx.ai:{upload_directory}")
            subprocess.check_call(["bash", "-x", f"{KG2C_DIR}/upload-synonymizer-artifacts.sh", RTXConfiguration().db_host, upload_directory, synonymizer_name])
        logging.info("Done building synonymizer.")

    # Actually build KG2c
    if build_kg2c:
        logging.info("Calling create_kg2c_files.py..")
        create_kg2c_files(args.test)
        logging.info("Calling record_kg2c_meta_info.py..")
        record_meta_kg_info(args.test)
        if not args.test:
            if upload_to_s3:
                _upload_output_files_to_s3()
            if upload_to_arax_databases_rtx_ai:
                logging.info(f"Uploading KG2c artifacts to arax-databases.rtx.ai:{upload_directory}")
                subprocess.check_call(["bash", "-x", f"{KG2C_DIR}/upload-kg2c-artifacts.sh", RTXConfiguration().db_host, upload_directory])

        logging.info(f"DONE WITH {'TEST ' if args.test else ''}KG2c BUILD! Took {round(((time.time() - start) / 60) / 60, 1)} hours")

    # Undo the revisions we made to config_dbs.json
    config_dbs_file_path = f"{CODE_DIR}/config_dbs.json"
    temp_config_dbs_file_path = f"{config_dbs_file_path}_KG2CBUILDTEMP"
    temp_config_dbs_path = pathlib.Path(temp_config_dbs_file_path)
    if temp_config_dbs_path.exists():
        subprocess.check_call(["mv", temp_config_dbs_file_path, config_dbs_file_path])


if __name__ == "__main__":
    main()


# code/kg2c/create_indexes_constraints_canonicalized.py

#!/usr/bin/env python3
''' Creates Neo4j index and constraints for Canonicalized KG2

    Usage: create_indexes_constraints_canonicalized.py [--passwordFile=<password-file-name>] <Neo4j Username> [<Neo4j Password>]
'''
import argparse
import neo4j
import getpass
import sys
import json

__author__ = 'Erica Wood'
__copyright__ = 'Oregon State University'
__credits__ = ['Stephen Ramsey', 'Erica Wood']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'


def run_query(query):
    """
    :param query: a cypher statement as a string to run
    """
    # Start a neo4j session, run a query, then close the session
    session = driver.session()
    query = session.run(query)
    session.close()
    return query


def node_labels():
    # Create a list of dictionaries where each key is "labels(n)"
    # and each value is a list containing a node label
    labels = "MATCH (n) RETURN distinct labels(n)"
    query = run_query(labels)
    data = query.data()
    label_list = []
    # Iterate through the list and dicitionaries to create a list
    # of node labels
    for dictionary in data:
        for key in dictionary:
            value = dictionary[key]
            value_string = value[0]
            label_list.append(value_string)
    return label_list


def create_index(label_list, property_name):
    """
    :param label_list: a list of the node labels in Neo4j
    """
    # For every label in the label list, create an index
    # on the given property name
    for label in label_list:
        run_query(f"CREATE INDEX ON :`{label}` ({property_name})")


def constraint(label_list):
    """
    :param label_list: a list of the node labels in Neo4j
    """
    # For every label in the label list, create a unique constraint
    # on the node id property
    constraint_query = "CREATE CONSTRAINT ON (n:Base) ASSERT n.id IS UNIQUE"
    run_query(constraint_query)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--configFile", type=str, help="RTXConfiguration JSON file containing the password", required=False, default=None)
    parser.add_argument("-u", "--user", type=str, help="Neo4j Username", default=None, required=False)
    parser.add_argument("-p", "--password", help="Neo4j Password", type=str, default=None, required=False)
    arguments = parser.parse_args()
    config_file_name = arguments.configFile
    if arguments.password is not None and arguments.configFile is not None:
        print("Not allowed to specify both password_file and password command-line options", file=sys.stderr)
        sys.exit()
    if arguments.user is None and arguments.configFile is None:
        print("Must specify a username on the command-line or via the RTXConfiguration config file", file=sys.stderr)
        sys.exit()
    if arguments.user is not None and arguments.configFile is not None:
        print("Cannot specify the username on both the command-line and the RTXConfiguration config file", file=sys.stderr)
        sys.exit()
    password = None
    neo4j_password = None
    neo4j_user = None
    if config_file_name is not None:
        print(config_file_name)
        config_data = json.load(open(config_file_name, 'r'))
        config_data_kg2_neo4j = config_data['KG2']['neo4j']
        neo4j_user = config_data_kg2_neo4j['username']
        neo4j_password = config_data_kg2_neo4j['password']
    if neo4j_password is None:
        neo4j_password = arguments.password
    if neo4j_password is None:
        neo4j_password = getpass.getpass("Please enter the Neo4j database password: ")
    if arguments.user is not None:
        neo4j_user = arguments.user
    bolt = 'bolt://127.0.0.1:7687'
    driver = neo4j.GraphDatabase.driver(bolt, auth=(neo4j_user, neo4j_password))
    node_label_list = node_labels() + ['Base']

    print("NOTE: If you are running create_indexes_constraints.py standalone and not via tsv-to-neo4j-canonicalized.sh, please make sure to re-set the read-only status of" +
          " the Neo4j database to TRUE", file=sys.stderr)

    # Create Indexes on Node Properties
    create_index(node_label_list, "name")
    create_index(node_label_list, "category")

    constraint(node_label_list)
    driver.close()


# code/kg2c/create_kg2c_files.py

#!/bin/env python3
"""
This script creates a canonicalized version of KG2 stored in various formats, including TSV files ready for import
into neo4j. The files are created in the directory this script is in.
Usage: python3 create_kg2c_files.py [--test]
"""
import argparse
import ast
import csv
import gc
import json
import logging
import os
import pathlib
import pickle
import sqlite3
import subprocess
import sys
import time
from collections import defaultdict

from datetime import datetime
from multiprocessing import Pool
from typing import List, Dict, Tuple, Union, Optional, Set

sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from utils import select_best_description
sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../ARAX/NodeSynonymizer/")
from node_synonymizer import NodeSynonymizer
sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../ARAX/BiolinkHelper/")
from biolink_helper import BiolinkHelper

KG2C_ARRAY_DELIMITER = "ǂ"  # Need to use a delimiter that does not appear in any list items (strings)
KG2PRE_ARRAY_DELIMITER = ";"
KG2C_DIR = os.path.dirname(os.path.abspath(__file__))
csv.field_size_limit(sys.maxsize)  # Required because some KG2pre fields are massive


PROPERTIES_LOOKUP = {
    "nodes": {
        "id": {"type": str, "in_kg2pre": True, "in_kg2c_lite": True},
        "name": {"type": str, "in_kg2pre": True, "in_kg2c_lite": True},
        "category": {"type": str, "in_kg2pre": True, "in_kg2c_lite": True},
        "iri": {"type": str, "in_kg2pre": True, "in_kg2c_lite": False},
        "description": {"type": str, "in_kg2pre": True, "in_kg2c_lite": False},
        "all_categories": {"type": list, "in_kg2pre": False, "in_kg2c_lite": True, "use_as_labels": True},
        "publications": {"type": list, "in_kg2pre": True, "in_kg2c_lite": False},
        "equivalent_curies": {"type": list, "in_kg2pre": False, "in_kg2c_lite": False},
        "all_names": {"type": list, "in_kg2pre": False, "in_kg2c_lite": False}
    },
    "edges": {
        "id": {"type": str, "in_kg2pre": True, "in_kg2c_lite": True},
        "subject": {"type": str, "in_kg2pre": True, "in_kg2c_lite": True},
        "object": {"type": str, "in_kg2pre": True, "in_kg2c_lite": True},
        "predicate": {"type": str, "in_kg2pre": True, "in_kg2c_lite": True},
        "knowledge_source": {"type": list, "in_kg2pre": True, "in_kg2c_lite": True},
        "publications": {"type": list, "in_kg2pre": True, "in_kg2c_lite": False},
        "kg2_ids": {"type": list, "in_kg2pre": False, "in_kg2c_lite": False},
        "publications_info": {"type": dict, "in_kg2pre": True, "in_kg2c_lite": False},
        "qualified_predicate": {"type": str, "in_kg2pre": True, "in_kg2c_lite": True},
        "qualified_object_aspect" : {"type": str, "in_kg2pre": True, "in_kg2c_lite": True},
        "qualified_object_direction": {"type": str, "in_kg2pre": True, "in_kg2c_lite": True}
    }
}


def _convert_list_to_string_encoded_format(input_list_or_str: Union[List[str], str]) -> Union[str, List[str]]:
    if isinstance(input_list_or_str, list):
        filtered_list = [item for item in input_list_or_str if item]  # Get rid of any None items
        str_items = [item for item in filtered_list if isinstance(item, str)]
        if len(str_items) < len(filtered_list):
            logging.warning(f"  List contains non-str items (this is unexpected; I'll exclude them)")
        return KG2C_ARRAY_DELIMITER.join(str_items)
    else:
        return input_list_or_str


def _prep_for_sqlite(item: Union[str, List[str], Dict[str, any]]) -> str:
    if isinstance(item, str):
        return item
    elif not item:
        return ""
    elif isinstance(item, list):
        return _convert_list_to_string_encoded_format(item)
    elif isinstance(item, dict):
        return json.dumps(item)
    else:
        raise ValueError(f"Unknown data type - don't know how to prep for sqlite. Type is: {type(item)},"
                         f"value is: {item}")


def _merge_two_lists(list_a: List[any], list_b: List[any]) -> List[any]:
    unique_items = list(set(list_a + list_b))
    return [item for item in unique_items if item]


def _get_edge_key(subject: str, object: str, predicate: str, qualified_predicate: str, qualified_object_aspect: str, qualified_object_direction) -> str:
        return f"{subject}--{predicate}--{qualified_predicate}--{qualified_object_aspect}--{qualified_object_direction}--{object}"


def _get_kg2pre_headers(header_file_path: str) -> List[str]:
    with open(header_file_path) as header_file:
        reader = csv.reader(header_file, delimiter="\t")
        headers = [row for row in reader][0]
    processed_headers = [header.split(":")[0] for header in headers]
    return processed_headers


def _load_property(raw_property_value_from_tsv: str, property_type: any) -> Union[list, str, dict]:
    if property_type is str:
        return raw_property_value_from_tsv
    elif property_type is list:
        split_string = raw_property_value_from_tsv.split(KG2PRE_ARRAY_DELIMITER)
        processed_list = [item.strip() for item in split_string if item]
        return processed_list
    elif property_type is dict:
        # For now, publications_info is the only dict property
        return _load_publications_info(raw_property_value_from_tsv, "none")
    else:
        return raw_property_value_from_tsv


def _get_array_properties(kind_of_item: Optional[str] = None) -> Set[str]:
    node_array_properties = {property_name for property_name, property_info in PROPERTIES_LOOKUP["nodes"].items()
                             if property_info["type"] is list}
    edge_array_properties = {property_name for property_name, property_info in PROPERTIES_LOOKUP["edges"].items()
                             if property_info["type"] is list}
    if kind_of_item and kind_of_item.startswith("node"):
        return node_array_properties
    elif kind_of_item and kind_of_item.startswith("edge"):
        return edge_array_properties
    else:
        return node_array_properties.union(edge_array_properties)


def _get_lite_properties(kind_of_item: Optional[str] = None) -> Set[str]:
    node_lite_properties = {property_name for property_name, property_info in PROPERTIES_LOOKUP["nodes"].items()
                            if property_info["in_kg2c_lite"]}
    edge_lite_properties = {property_name for property_name, property_info in PROPERTIES_LOOKUP["edges"].items()
                            if property_info["in_kg2c_lite"]}
    if kind_of_item and kind_of_item.startswith("node"):
        return node_lite_properties
    elif kind_of_item and kind_of_item.startswith("edge"):
        return edge_lite_properties
    else:
        return node_lite_properties.union(edge_lite_properties)


def _get_kg2pre_properties(kind_of_item: Optional[str] = None) -> Set[str]:
    node_kg2pre_properties = {property_name for property_name, property_info in PROPERTIES_LOOKUP["nodes"].items()
                              if property_info["in_kg2pre"]}
    edge_kg2pre_properties = {property_name for property_name, property_info in PROPERTIES_LOOKUP["edges"].items()
                              if property_info["in_kg2pre"]}
    if kind_of_item and kind_of_item.startswith("node"):
        return node_kg2pre_properties
    elif kind_of_item and kind_of_item.startswith("edge"):
        return edge_kg2pre_properties
    else:
        return node_kg2pre_properties.union(edge_kg2pre_properties)


def _get_node_labels_property() -> str:
    labels_properties = [property_name for property_name, property_info in PROPERTIES_LOOKUP["nodes"].items()
                         if property_info.get("use_as_labels")]
    return labels_properties[0]  # Should only ever be one, so return the first item


def _get_best_description_nlp(descriptions_list: List[str]) -> Optional[str]:
    candidate_descriptions = [description for description in descriptions_list if description and len(description) < 10000]
    if len(candidate_descriptions) == 1:
        return candidate_descriptions[0]
    else:
        # Use Chunyu's NLP-based method to select the best description out of the coalesced nodes
        description_finder = select_best_description(candidate_descriptions)
        return description_finder.get_best_description


def _get_best_description_length(descriptions_list: List[str]) -> Optional[str]:
    candidate_descriptions = [description for description in descriptions_list if description and len(description) < 10000]
    if not candidate_descriptions:
        return None
    elif len(candidate_descriptions) == 1:
        return candidate_descriptions[0]
    else:
        return max(candidate_descriptions, key=len)


def _load_publications_info(raw_publications_info: Union[str, dict], kg2_edge_id: str) -> Dict[str, any]:
    if isinstance(raw_publications_info, str):
        try:
            publications_info = ast.literal_eval(raw_publications_info)
        except Exception:
            logging.warning(f"Failed to load publications_info string for edge {kg2_edge_id}.")
            with open("problem_publications_info.tsv", "a+") as problem_file:
                writer = csv.writer(problem_file, delimiter="\t")
                writer.writerow([kg2_edge_id, raw_publications_info])
            publications_info = dict()
    else:
        publications_info = raw_publications_info
    assert isinstance(publications_info, dict)
    # This field currently has a non-standard structure in KG2pre; keep only the PMID-organized info
    pubs_info_keys_to_remove = [key for key in publications_info if not key.upper().startswith("PMID")]
    for key in pubs_info_keys_to_remove:
        del publications_info[key]
    return publications_info


def _load_kg2pre_tsv(local_tsv_dir_path: str, nodes_or_edges: str, is_test: bool) -> List[Dict[str, any]]:
    tsv_path = f"{local_tsv_dir_path}/{nodes_or_edges}.tsv"
    tsv_header_path = f"{local_tsv_dir_path}/{nodes_or_edges}_header.tsv"
    kg2pre_objects = []
    logging.info(f"Loading {nodes_or_edges} from KG2pre TSV ({tsv_path})..")
    headers = _get_kg2pre_headers(tsv_header_path)
    kg2pre_property_names = _get_kg2pre_properties(nodes_or_edges)
    counter = 0
    with open(tsv_path) as kg2pre_file:
        reader = csv.reader(kg2pre_file, delimiter="\t")
        for row in reader:
            counter += 1
            new_object = dict()
            for property_name in kg2pre_property_names:
                property_info = PROPERTIES_LOOKUP[nodes_or_edges][property_name]
                raw_property_value = row[headers.index(property_name)]
                new_object[property_name] = _load_property(raw_property_value, property_info["type"])
            kg2pre_objects.append(new_object)
            if is_test and counter > 100000:
                break
    return kg2pre_objects


def _modify_column_headers_for_neo4j(plain_column_headers: List[str], file_name_root: str) -> List[str]:
    modified_headers = []
    all_array_column_names = _get_array_properties()
    for header in plain_column_headers:
        if header in all_array_column_names:
            header = f"{header}:string[]"
        elif header == 'id' and "node" in file_name_root:  # Skip setting ID for edges
            header = f"{header}:ID"
        elif header == 'node_labels':
            header = ":LABEL"
        elif header == 'subject_for_conversion':
            header = ":START_ID"
        elif header == 'object_for_conversion':
            header = ":END_ID"
        elif header == 'predicate_for_conversion':
            header = ":TYPE"
        modified_headers.append(header)
    return modified_headers


def _create_node(preferred_curie: str, name: Optional[str], category: str, all_categories: List[str],
                 equivalent_curies: List[str], publications: List[str], all_names: List[str], iri: Optional[str],
                 description: Optional[str], descriptions_list: List[str]) -> Dict[str, any]:
    node_properties_lookup = PROPERTIES_LOOKUP["nodes"]
    assert isinstance(preferred_curie, node_properties_lookup["id"]["type"])
    assert isinstance(name, node_properties_lookup["name"]["type"]) or not name
    assert isinstance(category, node_properties_lookup["category"]["type"])
    assert isinstance(all_categories, node_properties_lookup["all_categories"]["type"])
    assert isinstance(equivalent_curies, node_properties_lookup["equivalent_curies"]["type"])
    assert isinstance(publications, node_properties_lookup["publications"]["type"])
    assert isinstance(all_names, node_properties_lookup["all_names"]["type"])
    assert isinstance(iri, node_properties_lookup["iri"]["type"]) or not iri
    assert isinstance(description, node_properties_lookup["description"]["type"]) or not description
    assert isinstance(descriptions_list, list)
    return {
        "id": preferred_curie,
        "name": name,
        "category": category,
        "all_names": all_names,
        "all_categories": all_categories,
        "iri": iri,
        "description": description,
        "descriptions_list": descriptions_list,
        "equivalent_curies": equivalent_curies,
        "publications": publications
    }


def _create_edge(subject: str, object: str, predicate: str, knowledge_source: List[str], publications: List[str],
                 publications_info: Dict[str, any], kg2_ids: List[str],
                 qualified_predicate, qualified_object_aspect, qualified_object_direction) -> Dict[str, any]:
    edge_properties_lookup = PROPERTIES_LOOKUP["edges"]
    assert isinstance(subject, edge_properties_lookup["subject"]["type"])
    assert isinstance(object, edge_properties_lookup["object"]["type"])
    assert isinstance(predicate, edge_properties_lookup["predicate"]["type"])
    assert isinstance(knowledge_source, edge_properties_lookup["knowledge_source"]["type"])
    assert isinstance(publications, edge_properties_lookup["publications"]["type"])
    assert isinstance(publications_info, edge_properties_lookup["publications_info"]["type"])
    assert isinstance(kg2_ids, edge_properties_lookup["kg2_ids"]["type"])
    assert isinstance(qualified_predicate, edge_properties_lookup["qualified_predicate"]["type"])
    assert isinstance(qualified_object_aspect, edge_properties_lookup["qualified_object_aspect"]["type"])
    assert isinstance(qualified_object_direction, edge_properties_lookup["qualified_object_direction"]["type"])

    
    return {
        "subject": subject,
        "object": object,
        "predicate": predicate,
        "knowledge_source": knowledge_source,
        "publications": publications,
        "publications_info": publications_info,
        "kg2_ids": kg2_ids,
        "qualified_predicate": qualified_predicate, 
        "qualified_object_aspect": qualified_object_aspect,
        "qualified_object_direction": qualified_object_direction
    }


def _write_list_to_neo4j_ready_tsv(input_list: List[Dict[str, any]], file_name_root: str, is_test: bool):
    # Converts a list into the specific format Neo4j wants (string with delimiter)
    logging.info(f"  Creating {file_name_root} header file..")
    column_headers = list(input_list[0].keys())
    modified_headers = _modify_column_headers_for_neo4j(column_headers, file_name_root)
    with open(f"{KG2C_DIR}/{'test_' if is_test else ''}{file_name_root}_header.tsv", "w+") as header_file:
        dict_writer = csv.DictWriter(header_file, modified_headers, delimiter='\t')
        dict_writer.writeheader()
    logging.info(f"  Creating {file_name_root} file..")
    with open(f"{KG2C_DIR}/{'test_' if is_test else ''}{file_name_root}.tsv", "w+") as data_file:
        dict_writer = csv.DictWriter(data_file, column_headers, delimiter='\t')
        dict_writer.writerows(input_list)


def create_kg2c_json_file(canonicalized_nodes_dict: Dict[str, Dict[str, any]],
                          canonicalized_edges_dict: Dict[str, Dict[str, any]],
                          meta_info_dict: Dict[str, str], is_test: bool):
    logging.info(f" Creating KG2c JSON file..")
    kgx_format_json = {"nodes": list(canonicalized_nodes_dict.values()),
                       "edges": list(canonicalized_edges_dict.values())}
    kgx_format_json.update(meta_info_dict)
    with open(f"{KG2C_DIR}/kg2c{'_test' if is_test else ''}.json", "w+") as output_file:
        json.dump(kgx_format_json, output_file)


def create_kg2c_lite_json_file(canonicalized_nodes_dict: Dict[str, Dict[str, any]],
                               canonicalized_edges_dict: Dict[str, Dict[str, any]],
                               meta_info_dict: Dict[str, str], is_test: bool):
    logging.info(f" Creating KG2c lite JSON file..")
    # Filter out all except these properties so we create a lightweight KG
    node_lite_properties = _get_lite_properties("node")
    edge_lite_properties = _get_lite_properties("edge")
    lite_kg = {"nodes": [], "edges": []}
    for node in canonicalized_nodes_dict.values():
        lite_node = dict()
        for lite_property in node_lite_properties:
            lite_node[lite_property] = node[lite_property]
        lite_kg["nodes"].append(lite_node)
    for edge in canonicalized_edges_dict.values():
        lite_edge = dict()
        for lite_property in edge_lite_properties:
            lite_edge[lite_property] = edge[lite_property]
        lite_kg["edges"].append(lite_edge)
    lite_kg.update(meta_info_dict)

    # Save this lite KG to a JSON file
    logging.info(f"  Saving lite json...")
    with open(f"{KG2C_DIR}/kg2c_lite{'_test' if is_test else ''}.json", "w+") as output_file:
        json.dump(lite_kg, output_file)


def create_kg2c_tsv_files(canonicalized_nodes_dict: Dict[str, Dict[str, any]],
                          canonicalized_edges_dict: Dict[str, Dict[str, any]],
                          biolink_version: str, is_test: bool):
    bh = BiolinkHelper(biolink_version)
    # Convert array fields into the format neo4j wants and do some final processing
    array_node_columns = _get_array_properties("node").union({"node_labels"})
    array_edge_columns = _get_array_properties("edge")
    node_labels_property = _get_node_labels_property()
    for canonicalized_node in canonicalized_nodes_dict.values():
        canonicalized_node['node_labels'] = bh.get_ancestors(canonicalized_node[node_labels_property], include_mixins=True)
        for list_node_property in array_node_columns:
            canonicalized_node[list_node_property] = _convert_list_to_string_encoded_format(canonicalized_node[list_node_property])
    for canonicalized_edge in canonicalized_edges_dict.values():
        if not is_test:  # Make sure we don't have any orphan edges
            assert canonicalized_edge['subject'] in canonicalized_nodes_dict
            assert canonicalized_edge['object'] in canonicalized_nodes_dict
        for list_edge_property in array_edge_columns:
            canonicalized_edge[list_edge_property] = _convert_list_to_string_encoded_format(canonicalized_edge[list_edge_property])
        canonicalized_edge['predicate_for_conversion'] = canonicalized_edge['predicate']
        canonicalized_edge['subject_for_conversion'] = canonicalized_edge['subject']
        canonicalized_edge['object_for_conversion'] = canonicalized_edge['object']

    # Finally dump all our nodes/edges into TSVs (formatted for neo4j)
    logging.info(f" Creating TSVs for Neo4j..")
    _write_list_to_neo4j_ready_tsv(list(canonicalized_nodes_dict.values()), "nodes_c", is_test)
    _write_list_to_neo4j_ready_tsv(list(canonicalized_edges_dict.values()), "edges_c", is_test)


def create_kg2c_sqlite_db(canonicalized_nodes_dict: Dict[str, Dict[str, any]],
                          canonicalized_edges_dict: Dict[str, Dict[str, any]], is_test: bool):
    logging.info(" Creating KG2c sqlite database..")
    db_name = f"kg2c{'_test' if is_test else ''}.sqlite"
    # Remove any preexisting version of this database
    if os.path.exists(db_name):
        os.remove(db_name)
    connection = sqlite3.connect(db_name)

    # Add all nodes (node object is dumped into a JSON string)
    logging.info(f"  Creating nodes table..")
    sqlite_node_properties = list(set(PROPERTIES_LOOKUP["nodes"]).difference(_get_lite_properties("nodes")).union({"id", _get_node_labels_property()}))
    logging.info(f"   Node properties to store in sqlite db are: {sqlite_node_properties}")
    cols_with_types_string = ", ".join([f"{property_name} TEXT" for property_name in sqlite_node_properties])
    question_marks_string = ", ".join(["?" for _ in range(len(sqlite_node_properties))])
    cols_string = ", ".join(sqlite_node_properties)
    connection.execute(f"CREATE TABLE nodes ({cols_with_types_string})")
    node_rows = [[_prep_for_sqlite(node[property_name]) for property_name in sqlite_node_properties]
                 for node in canonicalized_nodes_dict.values()]
    connection.executemany(f"INSERT INTO nodes ({cols_string}) VALUES ({question_marks_string})", node_rows)
    connection.execute("CREATE UNIQUE INDEX node_id_index ON nodes (id)")
    connection.commit()
    cursor = connection.execute(f"SELECT COUNT(*) FROM nodes")
    logging.info(f"  Done creating nodes table; contains {cursor.fetchone()[0]} rows.")
    cursor.close()

    # Add all edges (edge object is dumped into a JSON string)
    logging.info(f"  Creating edges table..")
    sqlite_edge_properties = list(set(PROPERTIES_LOOKUP["edges"]).difference(_get_lite_properties("edges")).union({"knowledge_source"}))
    logging.info(f"   Edge properties to store in sqlite db are: {sqlite_edge_properties}")
    cols_with_types_string = ", ".join([f"{property_name} TEXT" for property_name in sqlite_edge_properties])
    question_marks_string = ", ".join(["?" for _ in range(len(sqlite_edge_properties))])
    cols_string = ", ".join(sqlite_edge_properties)
    connection.execute(f"CREATE TABLE edges (triple TEXT, node_pair TEXT, {cols_with_types_string})")
    edge_rows = [[_get_edge_key(edge['subject'], edge['object'], edge['predicate'], edge['qualified_predicate'], edge['qualified_object_aspect'], edge['qualified_object_direction']),
                  f"{edge['subject']}--{edge['object']}"] + [_prep_for_sqlite(edge[property_name]) for property_name in sqlite_edge_properties]

                 for edge in canonicalized_edges_dict.values()]
    connection.executemany(f"INSERT INTO edges (triple, node_pair, {cols_string}) VALUES (?, ?, {question_marks_string})", edge_rows)
    connection.execute("CREATE UNIQUE INDEX triple_index ON edges (triple)")
    connection.execute("CREATE INDEX node_pair_index ON edges (node_pair)")
    connection.commit()
    cursor = connection.execute(f"SELECT COUNT(*) FROM edges")
    logging.info(f"  Done creating edges table; contains {cursor.fetchone()[0]} rows.")
    cursor.close()

    connection.close()


def _create_build_node(kg2_version: str, biolink_version: str) -> Dict[str, any]:
    description_dict = {"kg2_version": kg2_version,
                        "biolink_version": biolink_version,
                        "build_date": datetime.now().strftime('%Y-%m-%d %H:%M')}
    description = f"{description_dict}"
    name = f"RTX-KG{kg2_version}c"
    kg2c_build_node = _create_node(preferred_curie="RTX:KG2c",
                                   name=name,
                                   all_categories=["biolink:InformationContentEntity"],
                                   category="biolink:InformationContentEntity",
                                   equivalent_curies=[],
                                   publications=[],
                                   iri="http://rtx.ai/identifiers#KG2c",
                                   all_names=[name],
                                   description=description,
                                   descriptions_list=[description])
    return kg2c_build_node


def _canonicalize_nodes(kg2pre_nodes: List[Dict[str, any]]) -> Tuple[Dict[str, Dict[str, any]], Dict[str, str]]:
    logging.info(f"Canonicalizing nodes..")
    synonymizer = NodeSynonymizer()
    node_ids = [node.get('id') for node in kg2pre_nodes if node.get('id')]
    logging.info(f"  Sending NodeSynonymizer.get_canonical_curies() {len(node_ids)} curies..")
    canonicalized_info = synonymizer.get_canonical_curies(curies=node_ids, return_all_categories=True)
    all_canonical_curies = {canonical_info['preferred_curie'] for canonical_info in canonicalized_info.values() if canonical_info}
    logging.info(f"  Sending NodeSynonymizer.get_equivalent_nodes() {len(all_canonical_curies)} curies..")
    equivalent_curies_info = synonymizer.get_equivalent_nodes(all_canonical_curies)
    recognized_curies = {curie for curie in equivalent_curies_info if equivalent_curies_info.get(curie)}
    equivalent_curies_dict = {curie: list(equivalent_curies_info.get(curie)) for curie in recognized_curies}
    with open(f"{KG2C_DIR}/equivalent_curies.pickle", "wb") as equiv_curies_dump:  # Save these for use by downstream script
        pickle.dump(equivalent_curies_dict, equiv_curies_dump, protocol=pickle.HIGHEST_PROTOCOL)
    logging.info(f"  Creating canonicalized nodes..")
    curie_map = dict()
    canonicalized_nodes = dict()
    for kg2pre_node in kg2pre_nodes:
        # Grab relevant info for this node and its canonical version
        canonical_info = canonicalized_info.get(kg2pre_node['id'])
        canonicalized_curie = canonical_info.get('preferred_curie', kg2pre_node['id']) if canonical_info else kg2pre_node['id']
        publications = kg2pre_node['publications'] if kg2pre_node.get('publications') else []
        descriptions_list = [kg2pre_node['description']] if kg2pre_node.get('description') else []
        if canonicalized_curie in canonicalized_nodes:
            # Merge this node into its corresponding canonical node
            existing_canonical_node = canonicalized_nodes[canonicalized_curie]
            existing_canonical_node['publications'] = _merge_two_lists(existing_canonical_node['publications'], publications)
            existing_canonical_node['all_names'] = _merge_two_lists(existing_canonical_node['all_names'], [kg2pre_node['name']])
            existing_canonical_node['descriptions_list'] = _merge_two_lists(existing_canonical_node['descriptions_list'], descriptions_list)
            # Make sure any nodes subject to #1074-like problems still appear in equivalent curies
            existing_canonical_node['equivalent_curies'] = _merge_two_lists(existing_canonical_node['equivalent_curies'], [kg2pre_node['id']])
            # Add the IRI for the 'preferred' curie, if we've found that node
            if kg2pre_node['id'] == canonicalized_curie:
                existing_canonical_node['iri'] = kg2pre_node.get('iri')
        else:
            # Initiate the canonical node for this synonym group
            name = canonical_info['preferred_name'] if canonical_info else kg2pre_node['name']
            category = canonical_info['preferred_category'] if canonical_info else kg2pre_node['category']
            all_categories = list(canonical_info['all_categories']) if canonical_info else [kg2pre_node['category']]
            iri = kg2pre_node['iri'] if kg2pre_node['id'] == canonicalized_curie else None
            all_names = [kg2pre_node['name']]
            canonicalized_node = _create_node(preferred_curie=canonicalized_curie,
                                              name=name,
                                              category=category,
                                              all_categories=all_categories,
                                              publications=publications,
                                              equivalent_curies=equivalent_curies_dict.get(canonicalized_curie, [canonicalized_curie]),
                                              iri=iri,
                                              description=None,
                                              descriptions_list=descriptions_list,
                                              all_names=all_names)
            canonicalized_nodes[canonicalized_node['id']] = canonicalized_node
        curie_map[kg2pre_node['id']] = canonicalized_curie  # Record this mapping for easy lookup later
    logging.info(f"Number of KG2pre nodes was reduced to {len(canonicalized_nodes)} "
                 f"({round((len(canonicalized_nodes) / len(kg2pre_nodes)) * 100)}%)")
    return canonicalized_nodes, curie_map


def _canonicalize_edges(kg2pre_edges: List[Dict[str, any]], curie_map: Dict[str, str], is_test: bool) -> Dict[str, Dict[str, any]]:
    logging.info(f"Canonicalizing edges..")
    canonicalized_edges = dict()
    for kg2pre_edge in kg2pre_edges:
        kg2_edge_id = kg2pre_edge['id']
        original_subject = kg2pre_edge['subject']
        original_object = kg2pre_edge['object']
        if not is_test:  # Make sure we have the mappings we expect
            assert original_subject in curie_map
            assert original_object in curie_map
        canonicalized_subject = curie_map.get(original_subject, original_subject)
        canonicalized_object = curie_map.get(original_object, original_object)
        edge_publications = kg2pre_edge['publications'] if kg2pre_edge.get('publications') else []
        edge_knowledge_source = kg2pre_edge['knowledge_source'] if kg2pre_edge.get('knowledge_source') else []
        edge_qualified_predicate = kg2pre_edge['qualified_predicate'] if (kg2pre_edge.get('qualified_predicate') and kg2pre_edge.get('qualified_predicate') != "None") else ""
        edge_qualified_object_aspect = kg2pre_edge['qualified_object_aspect'] if (kg2pre_edge.get('qualified_object_aspect') and kg2pre_edge.get('qualified_object_aspect') != "None") else ""
        edge_qualified_object_direction = kg2pre_edge['qualified_object_direction'] if (kg2pre_edge.get('qualified_object_direction') and kg2pre_edge.get('qualified_object_direction') != "None") else ""

        '''Patch for lack of qualified_predicate when qualified_object_direction is present'''
        predicate = kg2pre_edge['predicate']
        if(predicate == "biolink:regulates" and edge_qualified_predicate == "" and edge_qualified_object_direction != ""):
            edge_qualified_predicate = "biolink:causes"
            edge_qualified_object_aspect = "activity_or_abundance"


        edge_publications_info = _load_publications_info(kg2pre_edge['publications_info'], kg2_edge_id) if kg2pre_edge.get('publications_info') else dict()
        if canonicalized_subject != canonicalized_object:  # Don't allow self-edges
            canonicalized_edge_key = _get_edge_key(canonicalized_subject, canonicalized_object, kg2pre_edge['predicate'], edge_qualified_predicate, edge_qualified_object_aspect, edge_qualified_object_direction)
            if canonicalized_edge_key in canonicalized_edges:
                canonicalized_edge = canonicalized_edges[canonicalized_edge_key]
                canonicalized_edge['knowledge_source'] = _merge_two_lists(canonicalized_edge['knowledge_source'], edge_knowledge_source)
                canonicalized_edge['publications'] = _merge_two_lists(canonicalized_edge['publications'], edge_publications)
                canonicalized_edge['publications_info'].update(edge_publications_info)
                canonicalized_edge['kg2_ids'].append(kg2_edge_id)
            else:
                new_canonicalized_edge = _create_edge(subject=canonicalized_subject,
                                                      object=canonicalized_object,
                                                      predicate=kg2pre_edge['predicate'],
                                                      knowledge_source=edge_knowledge_source,
                                                      publications=edge_publications,
                                                      publications_info=edge_publications_info,
                                                      kg2_ids=[kg2_edge_id],
                                                      qualified_predicate=edge_qualified_predicate,
                                                      qualified_object_aspect=edge_qualified_object_aspect,
                                                      qualified_object_direction= edge_qualified_object_direction
                                                      )
                canonicalized_edges[canonicalized_edge_key] = new_canonicalized_edge
    logging.info(f"Number of KG2pre edges was reduced to {len(canonicalized_edges)} "
                 f"({round((len(canonicalized_edges) / len(kg2pre_edges)) * 100)}%)")
    return canonicalized_edges


def _post_process_nodes(canonicalized_nodes_dict: Dict[str, Dict[str, any]], kg2c_config_info: Dict[str, any]) -> Dict[str, Dict[str, any]]:
    # Choose best descriptions for each cluster
    use_nlp_to_choose_descriptions = kg2c_config_info["kg2c"].get("use_nlp_to_choose_descriptions")
    node_ids = list(canonicalized_nodes_dict)
    description_lists = [canonicalized_nodes_dict[node_id]["descriptions_list"] for node_id in node_ids]
    num_cpus = os.cpu_count()
    logging.info(f"Detected {num_cpus} cpus; will use all of them to choose best descriptions")
    start = time.time()
    with Pool(num_cpus) as pool:
        if use_nlp_to_choose_descriptions:
            logging.info(f" Starting to use Chunyu's NLP-based method to choose best descriptions..")
            best_descriptions = pool.map(_get_best_description_nlp, description_lists)
        else:
            logging.info(f" Choosing best descriptions (longest under 10,000 characters)..")
            best_descriptions = pool.map(_get_best_description_length, description_lists)
    logging.info(f" Choosing best descriptions took {round(((time.time() - start) / 60) / 60, 2)} hours")

    # Actually decorate nodes with their 'best' description
    for num in range(len(node_ids)):
        node_id = node_ids[num]
        best_description = best_descriptions[num]
        canonicalized_nodes_dict[node_id]["description"] = best_description
        del canonicalized_nodes_dict[node_id]["descriptions_list"]
    del description_lists
    del best_descriptions
    gc.collect()

    # Do some final clean-up/formatting of nodes, now that all merging is done
    logging.info(f"Doing final clean-up/formatting of nodes")
    for node_id, node in canonicalized_nodes_dict.items():
        node["publications"] = node["publications"][:10]  # We don't need a ton of publications, so truncate them

    return canonicalized_nodes_dict


def _post_process_edges(canonicalized_edges_dict: Dict[str, Dict[str, any]]) -> Dict[str, Dict[str, any]]:
    logging.info(f"Doing final clean-up/formatting of edges")
    # Convert our edge IDs to integers (to save space downstream) and add them as actual properties on the edges
    edge_num = 1
    for edge_id, edge in canonicalized_edges_dict.items():
        edge["id"] = edge_num
        edge_num += 1
        edge["publications"] = edge["publications"][:20]  # We don't need a ton of publications, so truncate them
        if len(edge["publications_info"]) > 20:
            pubs_info_to_remove = list(edge["publications_info"])[20:]
            for pmid in pubs_info_to_remove:
                del edge["publications_info"][pmid]
    return canonicalized_edges_dict


def remove_overly_general_nodes(canonicalized_nodes_dict: Dict[str, Dict[str, any]],
                                canonicalized_edges_dict: Dict[str, Dict[str, any]],
                                biolink_version: str,
                                is_test: bool) -> Tuple[Dict[str, Dict[str, any]], Dict[str, Dict[str, any]]]:
    logging.info(f"Removing overly general nodes from the graph..")
    bh = BiolinkHelper(biolink_version)
    # Remove all nodes that have a biolink category as an equivalent identifier, as well as a few others
    all_biolink_categories = set(bh.get_descendants("biolink:NamedThing"))
    overly_general_curies = {"MESH:D010361", "SO:0001217", "MONDO:0000001", "FMA:67257", "MESH:D002477",
                             "MESH:D005796", "UMLS:C1257890", "UMLS:C0237401", "PR:000029067", "UMLS:C1457887",
                             "biolink:Cohort", "UMLS:C1550655", "CHEBI:25212", "GO:0008150", "UMLS:C0029235",
                             "LOINC:LP7790-1"}.union(all_biolink_categories)
    # TODO: Later use some better heuristics to identify such nodes?

    node_ids_to_remove = {node_id for node_id, node in canonicalized_nodes_dict.items()
                          if set(node["equivalent_curies"]).intersection(overly_general_curies)}
    logging.info(f" Identified {len(node_ids_to_remove)} nodes to remove: {node_ids_to_remove}")
    for node_id in node_ids_to_remove:
        canonicalized_nodes_dict.pop(node_id, None)

    # Delete any now orphaned edges
    if not is_test:
        orphaned_edge_ids = {edge_id for edge_id, edge in canonicalized_edges_dict.items()
                             if edge["subject"] not in canonicalized_nodes_dict or
                             edge["object"] not in canonicalized_nodes_dict}
        logging.info(f"  Deleting {len(orphaned_edge_ids)} edges that were orphaned by the above steps..")
        for edge_id in orphaned_edge_ids:
            canonicalized_edges_dict.pop(edge_id, None)

    logging.info(f"Done removing overly general nodes: resulting KG2c now has {len(canonicalized_nodes_dict)} nodes "
                 f"and {len(canonicalized_edges_dict)} edges")
    return canonicalized_nodes_dict, canonicalized_edges_dict


def create_kg2c_files(is_test=False):
    """
    This function extracts all nodes/edges from the KG2pre TSVs, canonicalizes the nodes, merges edges
    (based on subject, object, predicate), and saves the resulting canonicalized graph in multiple file formats: JSON,
    sqlite, and TSV (ready for import into Neo4j).
    """
    with open(f"{KG2C_DIR}/kg2c_config.json") as config_file:
        kg2c_config_info = json.load(config_file)
    kg2_version = kg2c_config_info.get("kg2pre_version")
    biolink_version = kg2c_config_info.get("biolink_version")
    start_from_kg2c_json = kg2c_config_info["kg2c"].get("start_from_kg2c_json")
    use_local_kg2pre_tsvs = kg2c_config_info["kg2c"].get("use_local_kg2pre_tsvs")

    # Start with the pre-existing kg2c.json, if directed to in the config file (allows partial builds)
    if start_from_kg2c_json:
        logging.info(f"Loading KG2c from pre-existing kg2c.json..")
        with open(f"{KG2C_DIR}/kg2c{'_test' if is_test else ''}.json") as kg2c_json_file:
            kg2c = json.load(kg2c_json_file)
        canonicalized_nodes_dict = {node["id"]: node for node in kg2c["nodes"]}
        canonicalized_edges_dict = {edge["id"]: edge for edge in kg2c["edges"]}
        logging.info(f"Loaded KG2c has {len(canonicalized_nodes_dict)} nodes and {len(canonicalized_edges_dict)} edges")
    # Otherwise do a full build, starting with the KG2pre TSVs
    else:
        # First make sure the KG2pre TSV directory exists as it should
        local_tsv_dir_path = f"{KG2C_DIR}/kg2pre_tsvs"
        if not pathlib.Path(local_tsv_dir_path).exists():
            if is_test and use_local_kg2pre_tsvs:
                raise ValueError(f"You must put your own test KG2pre TSVs into place (in {local_tsv_dir_path}). They "
                                 f"must be named: nodes.tsv, nodes_header.tsv, edges.tsv, edges_header.tsv")
            elif use_local_kg2pre_tsvs:
                raise ValueError(f"No local KG2pre TSVs exist. You must put all four of them into {local_tsv_dir_path}."
                                 f" They must be named: nodes.tsv, nodes_header.tsv, edges.tsv, edges_header.tsv")
            else:
                subprocess.check_call(["mkdir", local_tsv_dir_path])
        # Download the KG2pre TSVs from the AWS S3 bucket
        if not use_local_kg2pre_tsvs:
            kg2pre_tarball_name = "kg2-tsv-for-neo4j.tar.gz"
            logging.info(f"Downloading {kg2pre_tarball_name} from the rtx-kg2 S3 bucket")
            subprocess.check_call(["aws", "s3", "cp", "--no-progress", "--region", "us-west-2", f"s3://rtx-kg2/{kg2pre_tarball_name}", KG2C_DIR])
            logging.info(f"Unpacking {kg2pre_tarball_name}..")
            subprocess.check_call(["tar", "-xvzf", kg2pre_tarball_name, "-C", local_tsv_dir_path])

        # Canonicalize nodes
        kg2pre_nodes = _load_kg2pre_tsv(local_tsv_dir_path, "nodes", is_test)
        canonicalized_nodes_dict, curie_map = _canonicalize_nodes(kg2pre_nodes)
        # Make sure that the KG2pre version matches the version we're supposed to be building a KG2c off of
        if not is_test:
            kg2pre_build_node = canonicalized_nodes_dict.get("RTX:KG2")
            if not kg2pre_build_node:
                raise ValueError(f"There is no build node (i.e., no node with the ID 'RTX:KG2' in the ingested KG2pre "
                                 f"TSVs; this means I can't verify that the KG2pre version matches what we want, "
                                 f"so I'll halt processing")
            else:
                kg2pre_version = kg2pre_build_node["name"].replace("RTX KG", "")
                if kg2pre_version != kg2_version:
                    raise ValueError(f"The version on the KG2pre build node in the ingested KG2pre TSVs is "
                                     f"{kg2pre_version}, but the KG2c version you want to build is {kg2_version}. "
                                     f"These version numbers must match. Halting the build.")

        # Add a node containing information about this KG2C build
        build_node = _create_build_node(kg2_version, biolink_version)
        canonicalized_nodes_dict[build_node['id']] = build_node
        canonicalized_nodes_dict = _post_process_nodes(canonicalized_nodes_dict, kg2c_config_info)
        del kg2pre_nodes  # Try to free up as much memory as possible for edge processing
        gc.collect()

        # Canonicalize edges
        kg2pre_edges = _load_kg2pre_tsv(local_tsv_dir_path, "edges", is_test)
        canonicalized_edges_dict = _canonicalize_edges(kg2pre_edges, curie_map, is_test)
        del kg2pre_edges
        gc.collect()
        canonicalized_edges_dict = _post_process_edges(canonicalized_edges_dict)

        # Remove overly general nodes (e.g., 'Genes', 'Disease or disorder'..)
        canonicalized_nodes_dict, canonicalized_edges_dict = remove_overly_general_nodes(canonicalized_nodes_dict,
                                                                                         canonicalized_edges_dict,
                                                                                         biolink_version, is_test)

    # Actually create all of our output files (different formats for storing KG2c)
    meta_info_dict = {"kg2_version": kg2_version, "biolink_version": biolink_version}
    logging.info(f"Saving KG2c in various file formats..")
    create_kg2c_lite_json_file(canonicalized_nodes_dict, canonicalized_edges_dict, meta_info_dict, is_test)
    create_kg2c_json_file(canonicalized_nodes_dict, canonicalized_edges_dict, meta_info_dict, is_test)
    create_kg2c_tsv_files(canonicalized_nodes_dict, canonicalized_edges_dict, biolink_version, is_test)
    create_kg2c_sqlite_db(canonicalized_nodes_dict, canonicalized_edges_dict, is_test)


def main():
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s %(levelname)s: %(message)s',
                        handlers=[logging.FileHandler("createkg2cfiles.log"),
                                  logging.StreamHandler()])
    arg_parser = argparse.ArgumentParser()
    arg_parser.add_argument('--test', dest='test', action='store_true', default=False)
    args = arg_parser.parse_args()

    logging.info(f"Starting to create KG2canonicalized..")
    start = time.time()
    create_kg2c_files(args.test)
    logging.info(f"Done! Took {round(((time.time() - start) / 60) / 60, 2)} hours.")


if __name__ == "__main__":
    main()


# code/kg2c/kg2c_tsv_to_kgx_tsv.py

#!/usr/bin/env python3

''' Loads the KG2c TSV files nodes_c.tsv and edges_c.tsv (and the header files)
and writes out a nodes.tsv and edges.tsv in Translator KGX TSV format

    Usage: python3 kg2c_tsv_to_kgx_tsv.py
'''

import json
import sys

__author__ = 'Stephen Ramsey'
__copyright__ = 'Oregon State University'
__credits__ = ['Stephen Ramsey']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'

max_edges = None  ## set to 100 for a test build

kg2c_special_delimiter = 'ǂ'

edge_fields_skip = {":START_ID",
                    ":END_ID",
                    ":TYPE"}

node_fields_skip = {":LABEL"}

edge_fields = []
edge_field_ids_drop = set()
edge_field_ids_listtype = set()
edge_fields_to_ids = dict()
node_fields_rename = {'category': 'specific_category',
                      'all_categories': 'category'}
node_fields_order = ['id', 'category', 'name', 'iri', 'description', 'publications', 'specific_category', 'equivalent_curies', 'all_names']

with open("edges_c_header.tsv", "r") as input_file:
    lines = input_file.readlines()
    if len(lines) != 1:
        sys.exit("one line of input expected in edges_c_header.tsv")
    field_id = 0
    for field in lines[0].rstrip().split("\t"):
        if field not in edge_fields_skip:
            if field.endswith(":string[]"):
                edge_field_ids_listtype.add(field_id)
            edge_fields.append(field.replace(":string[]", ""))
            edge_fields_to_ids[field] = field_id
        else:
            edge_field_ids_drop.add(field_id)
        field_id += 1

node_fields = []
node_field_ids_drop = set()
node_field_ids_listtype = set()
node_fields_to_ids = dict()

with open("nodes_c_header.tsv", "r") as input_file:
    lines = input_file.readlines()
    if len(lines) != 1:
        sys.exit("one line of input expected in nodes_c_header.tsv")
    field_id = 0
    for field in lines[0].rstrip().split("\t"):
        if field not in node_fields_skip:
            if field.endswith(":string[]"):
                node_field_ids_listtype.add(field_id)
            field_name_final = field.replace(":ID", "").replace(":string[]", "")
            node_fields.append(field_name_final)
            node_fields_to_ids[field_name_final] = field_id
        else:
            node_field_ids_drop.add(field_id)
        field_id += 1

if max_edges is None:
    node_ids_to_select = None
else:
    edge_counter = 0
    node_ids_to_select = set()

output_data = [edge_fields]
with open("edges_c.tsv", "r") as input_file:
    for line in input_file:
        output_fields = []
        field_id = 0
        for field in line.rstrip().split("\t"):
            if field_id not in edge_field_ids_drop:
                if field_id not in edge_field_ids_listtype:
                    output_fields.append(field)
                else:
                    output_fields.append("|".join(field.split(kg2c_special_delimiter)))
            else:
                pass
            field_id += 1
        output_data.append(output_fields)
        if max_edges is not None:
            for field_name in {"subject", "object"}:
                node_ids_to_select.add(output_fields[edge_fields_to_ids[field_name]])
            edge_counter += 1
            if edge_counter >= max_edges:
                break

with open("edges.tsv", "w") as output_file:
    for sublist in output_data:
        print("\t".join(sublist), file=output_file)

node_fields_renamed = [node_fields_rename.get(node_field, node_field) for node_field in node_fields]
sorted_node_fields = [node_fields_renamed.index(field) for field in node_fields_order]

output_data = [node_fields_renamed]
nodes_found = set()
with open("nodes_c.tsv", "r") as input_file:
    for line in input_file:
        output_fields = []
        field_id = 0
        for field in line.rstrip().split("\t"):
            if field_id not in node_field_ids_drop:
                if field_id not in node_field_ids_listtype:
                    output_fields.append(field)
                else:
                    output_fields.append("|".join(field.split(kg2c_special_delimiter)))
            else:
                pass
            field_id += 1
        if max_edges is not None:
            id = output_fields[node_fields_to_ids["id"]]
            if id in node_ids_to_select:
                keep_node = True
                nodes_found.add(id)
            else:
                keep_node = False
        else:
            keep_node = True
        if keep_node:
            output_data.append(output_fields)
        if max_edges is not None:
            if nodes_found == node_ids_to_select:
                break
            
with open("nodes.tsv", "w") as output_file:
    for sublist in output_data:
        print("\t".join([sublist[i] for i in sorted_node_fields]), file=output_file)

#print(json.dumps(output_data, indent=4, sort_keys=True))
#     for line in edges_header_file:
#         if edge_counter >= max_edges:
#             break
#         edge_counter += 1


# code/kg2c/record_kg2c_meta_info.py

#!/bin/env python3
"""
This script creates a 'meta knowledge graph' (per TRAPI) and records node neighbor counts by category (in the
kg2c.sqlite file generated by create_kg2c_files.py). It uses the 'lite' KG2c JSON file to derive this meta info.
Usage: python3 record_kg2c_meta_info.py [--test]
"""
import argparse
import csv
import json
import logging
import os
import pickle
import sqlite3
import sys
import time
from collections import defaultdict
from typing import Dict, Set

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../ARAX/BiolinkHelper/")
from biolink_helper import BiolinkHelper


KG2C_DIR = f"{os.path.dirname(os.path.abspath(__file__))}"


def serialize_with_sets(obj: any) -> any:
    # Thank you https://stackoverflow.com/a/60544597
    if isinstance(obj, set):
        return list(obj)
    else:
        return obj


def build_meta_kg(nodes_by_id: Dict[str, Dict[str, any]], edges_by_id: Dict[str, Dict[str, any]],
                  meta_kg_file_name: str, biolink_helper: BiolinkHelper, is_test: bool):
    logging.info(f"Building meta KG..")
    logging.info(" Gathering all meta triples..")
    meta_triples = set()
    for edge in edges_by_id.values():
        subject_node_id = edge["subject"]
        object_node_id = edge["object"]
        if not is_test or (subject_node_id in nodes_by_id and object_node_id in nodes_by_id):
            subject_node = nodes_by_id[subject_node_id]
            object_node = nodes_by_id[object_node_id]
            subject_categories = biolink_helper.add_conflations(subject_node["all_categories"])
            object_categories = biolink_helper.add_conflations(object_node["all_categories"])
            predicate = edge["predicate"]
            for subject_category in subject_categories:
                for object_category in object_categories:
                    meta_triples.add((subject_category, predicate, object_category))
    kg2_infores_curie = "infores:rtx-kg2"
    standard_attributes = [{"attribute_type_id": "biolink:knowledge_source",
                            "attribute_source": kg2_infores_curie},
                           {"attribute_type_id": "biolink:aggregator_knowledge_source",
                            "attribute_source": kg2_infores_curie}]
    meta_edges = [{"subject": triple[0], "predicate": triple[1], "object": triple[2], "attributes": standard_attributes}
                  for triple in meta_triples]
    logging.info(f" Created {len(meta_edges)} meta edges")

    logging.info(" Gathering all meta nodes..")
    with open(f"{KG2C_DIR}/equivalent_curies.pickle", "rb") as equiv_curies_file:
        equivalent_curies_dict = pickle.load(equiv_curies_file)
    meta_nodes = defaultdict(lambda: defaultdict(lambda: set()))
    for node_id, node in nodes_by_id.items():
        equivalent_curies = equivalent_curies_dict.get(node_id, [node_id])
        prefixes = {curie.split(":")[0] for curie in equivalent_curies}
        categories = biolink_helper.add_conflations(node["category"])
        for category in categories:
            meta_nodes[category]["id_prefixes"].update(prefixes)
    logging.info(f" Created {len(meta_nodes)} meta nodes")

    logging.info(" Saving meta KG to JSON file..")
    meta_kg = {"nodes": meta_nodes, "edges": meta_edges}
    with open(f"{KG2C_DIR}/{meta_kg_file_name}", "w+") as meta_kg_file:
        json.dump(meta_kg, meta_kg_file, default=serialize_with_sets, indent=2)


def add_neighbor_counts_to_sqlite(nodes_by_id: Dict[str, Dict[str, any]], edges_by_id: Dict[str, Dict[str, any]],
                                  sqlite_file_name: str, label_property_name: str, is_test: bool):
    logging.info("Counting up node neighbors by category..")
    # First gather neighbors of each node by label/category
    neighbors_by_label = defaultdict(lambda: defaultdict(lambda: set()))
    neighbors = defaultdict(set)
    for edge in edges_by_id.values():
        subject_node_id = edge["subject"]
        object_node_id = edge["object"]
        neighbors[subject_node_id].add(object_node_id)  # Used for overall neighbor counts later
        neighbors[object_node_id].add(subject_node_id)  # Used for overall neighbor counts later
        if not is_test or (subject_node_id in nodes_by_id and object_node_id in nodes_by_id):
            subject_node = nodes_by_id[subject_node_id]
            object_node = nodes_by_id[object_node_id]
            for label in object_node[label_property_name]:
                neighbors_by_label[subject_node_id][label].add(object_node_id)
            for label in subject_node[label_property_name]:
                neighbors_by_label[object_node_id][label].add(subject_node_id)

    # Then record only the counts of neighbors per label/category
    neighbor_counts = defaultdict(dict)
    for node_id, neighbors_dict in neighbors_by_label.items():
        for label, neighbor_ids in neighbors_dict.items():
            neighbor_counts[node_id][label] = len(neighbor_ids)

    # Then write these counts to the sqlite file
    logging.info(f" Saving neighbor counts (for {len(neighbor_counts)} nodes) to sqlite..")
    connection = sqlite3.connect(sqlite_file_name)
    connection.execute("DROP TABLE IF EXISTS neighbors")
    connection.execute("CREATE TABLE neighbors (id TEXT, neighbor_counts TEXT)")
    rows = [(node_id, json.dumps(neighbor_counts)) for node_id, neighbor_counts in neighbor_counts.items()]
    connection.executemany(f"INSERT INTO neighbors (id, neighbor_counts) VALUES (?, ?)", rows)
    connection.execute("CREATE UNIQUE INDEX node_neighbor_index ON neighbors (id)")
    connection.commit()
    cursor = connection.execute(f"SELECT COUNT(*) FROM neighbors")
    logging.info(f" Done adding neighbor counts to sqlite; neighbors table contains {cursor.fetchone()[0]} rows")
    cursor.close()
    connection.close()

    # Additionally record top node degrees (used for dev purposes)
    neighbors_tsv_name = "neighbor_counts.tsv"
    logging.info(f"Recording overall neighbor counts in {neighbors_tsv_name}..")
    with open(f"{KG2C_DIR}/{neighbors_tsv_name}", "w+") as neighbors_file:
        writer = csv.writer(neighbors_file, delimiter="\t")
        writer.writerow(["id", "num_neighbors", "name", label_property_name])
        for node_id, neighbor_ids in sorted(neighbors.items(), key=lambda item: len(item[1]), reverse=True)[:1000000]:
            node = nodes_by_id.get(node_id, dict())
            writer.writerow([node_id, len(neighbor_ids), node.get("name"), node.get(label_property_name)])


def add_category_counts_to_sqlite(nodes_by_id: Dict[str, Dict[str, any]], sqlite_file_name: str,
                                  label_property_name: str):
    logging.info("Counting up nodes by category..")
    # Organize node IDs by their categories/labels
    nodes_by_label = defaultdict(set)
    for node_id, node in nodes_by_id.items():
        for category in node[label_property_name]:
            nodes_by_label[category].add(node_id)

    # Then write these counts to the sqlite file
    logging.info(f" Saving category counts (for {len(nodes_by_label)} categories) to sqlite..")
    connection = sqlite3.connect(sqlite_file_name)
    connection.execute("DROP TABLE IF EXISTS category_counts")
    connection.execute("CREATE TABLE category_counts (category TEXT, count INTEGER)")
    rows = [(category, len(node_ids)) for category, node_ids in nodes_by_label.items()]
    connection.executemany(f"INSERT INTO category_counts (category, count) VALUES (?, ?)", rows)
    connection.execute("CREATE UNIQUE INDEX category_index ON category_counts (category)")
    connection.commit()
    cursor = connection.execute(f"SELECT COUNT(*) FROM category_counts")
    logging.info(f" Done adding category counts to sqlite; category_counts table contains "
                 f"{cursor.fetchone()[0]} rows")
    cursor.close()
    connection.close()


def generate_fda_approved_drugs_pickle(edges_by_id: Dict[str, Dict[str, any]], fda_approved_file_name: str):
    # Extract the IDs of FDA-approved drug nodes per DRUGBANK (they're attached to the "fda approved drug" node)
    fda_approved_drug_node = "MI:2099"
    fda_approved_drugs = set()
    for edge_id, edge in edges_by_id.items():
        node_ids = {edge["subject"], edge["object"]}
        if fda_approved_drug_node in node_ids:
            drug_node_id = next(node_id for node_id in node_ids.difference({fda_approved_drug_node}))
            fda_approved_drugs.add(drug_node_id)
    logging.info(f"Saving IDs of {len(fda_approved_drugs)} FDA-approved drugs to {fda_approved_file_name}")
    with open(fda_approved_file_name, "wb") as pickle_file:
        pickle.dump(fda_approved_drugs, pickle_file)


def record_meta_kg_info(is_test: bool):
    kg2c_lite_file_name = f"kg2c_lite{'_test' if is_test else ''}.json"
    meta_kg_file_name = f"kg2c_meta_kg{'_test' if is_test else ''}.json"
    sqlite_file_name = f"kg2c{'_test' if is_test else ''}.sqlite"
    fda_approved_file_name = f"fda_approved_drugs{'_test' if is_test else ''}.pickle"
    # Initiate a BiolinkHelper for the proper Biolink model version
    with open("kg2c_config.json") as config_file:
        config_info = json.load(config_file)
    bh = BiolinkHelper(config_info["biolink_version"])

    start = time.time()
    # Load the 'lite' KG2c file into node/edge dictionaries
    with open(f"{KG2C_DIR}/{kg2c_lite_file_name}", "r") as input_kg_file:
        logging.info(f"Loading {kg2c_lite_file_name} into memory..")
        kg2c_dict = json.load(input_kg_file)
        nodes_by_id = {node["id"]: node for node in kg2c_dict["nodes"]}
        edges_by_id = {edge["id"]: edge for edge in kg2c_dict["edges"]}
        del kg2c_dict
    # Add the 'expanded' node labels (including category ancestors) into the node dictionary
    expanded_labels_property_name = "expanded_labels"
    for node in nodes_by_id.values():
        node[expanded_labels_property_name] = bh.get_ancestors(node["all_categories"], include_mixins=True)

    build_meta_kg(nodes_by_id, edges_by_id, meta_kg_file_name, bh, is_test)
    add_neighbor_counts_to_sqlite(nodes_by_id, edges_by_id, sqlite_file_name, expanded_labels_property_name, is_test)
    add_category_counts_to_sqlite(nodes_by_id, sqlite_file_name, expanded_labels_property_name)
    generate_fda_approved_drugs_pickle(edges_by_id, fda_approved_file_name)

    logging.info(f"Recording meta KG info took {round((time.time() - start) / 60, 1)} minutes.")


def main():
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s %(levelname)s: %(message)s',
                        handlers=[logging.FileHandler("metainfo.log"),
                                  logging.StreamHandler()])
    logging.info("Starting to record KG2c meta info..")
    arg_parser = argparse.ArgumentParser()
    arg_parser.add_argument("--test", dest="test", action='store_true', default=False)
    args = arg_parser.parse_args()

    record_meta_kg_info(args.test)


if __name__ == "__main__":
    main()


# code/kg2c/setup_for_neo4j.py

#!/bin/env python3
"""
This script sets up your environment so that it can be used to host KG2c in Neo4j. It uses a script from the
RTX-KG2 repo.

Usage: python3 setup_for_neo4j.py
"""
import logging
import os
import subprocess

KG2C_DIR = f"{os.path.dirname(os.path.abspath(__file__))}"


def main():
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s %(levelname)s: %(message)s',
                        handlers=[logging.FileHandler("build.log"),
                                  logging.StreamHandler()])
    logging.info("SETTING UP FOR NEO4J")

    # Get master-config.shinc from the KG2 repo
    shinc_file_name = "master-config.shinc"
    logging.info(f"Getting {shinc_file_name} from KG2pre repo")
    rtx_kg2_repo_url = "https://github.com/RTXteam/RTX-KG2/blob/master"
    subprocess.check_call(["curl", "-L", f"{rtx_kg2_repo_url}/{shinc_file_name}?raw=true", "-o", f"{KG2C_DIR}/{shinc_file_name}"])

    # Get the system memory script from the KG2 repo
    mem_file_name = "get-system-memory-gb.sh"
    logging.info(f"Getting {mem_file_name} from KG2pre repo")
    subprocess.check_call(["curl", "-L", f"{rtx_kg2_repo_url}/{mem_file_name}?raw=true", "-o", f"{KG2C_DIR}/{mem_file_name}"])

    # Get the Neo4j setup script from the KG2 repo
    setup_script_name = "setup-kg2-neo4j.sh"
    logging.info(f"Getting {setup_script_name} from KG2pre repo")
    subprocess.check_call(["curl", "-L", f"{rtx_kg2_repo_url}/{setup_script_name}?raw=true", "-o", f"{KG2C_DIR}/{setup_script_name}"])

    # Now run the Neo4j setup script
    logging.info(f"Running {setup_script_name}")
    subprocess.check_call(["cd", KG2C_DIR])
    subprocess.check_call(["bash", "-x", setup_script_name])

    logging.info("DONE SETTING UP FOR NEO4J")


if __name__ == "__main__":
    main()


# code/kg2c/utils.py

import textstat
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained('dmis-lab/biobert-v1.1')
import numpy as np
import re

class select_best_description:
    '''
    This class is used to select the best description for the preferred curie in KG2c based on three rules (the length of description, 
    the number of detected human-readable words from biobert model and the estimated school grade level returned from textstat.text_standard[
    https://github.com/shivam5992/textstat]). For each rule, each candicate description is assigned a score based on its rank in this rule. 
    For example, based on the length of description, the longest description is assigned 0, the second longest one is assigned 1 and so on. 
    Then the final ranking score for each description is the summation of all scores from three rules. The 'best' description is the one with 
    the lowest score. 
    '''
    #### Constructor
    def __init__(self, desc_list):
        self.desc_list = list(set([x if x is not None else '' for x in desc_list]))
        self.len = len(self.desc_list)

    @staticmethod
    def _call_biobert_tokenizer(desc, len_limit=3):
        '''
        Convert a description text to token via biotokenizer
        parameter desc[string]: description text
        paramter len_limit[int]: at least token length to be considered as an effective token
        '''
        if type(desc) is str:
            desc = desc.replace('UMLS Semantic Type:','').replace('UMLS_STY:','')
            return [token for token in tokenizer.tokenize(desc) if not '#' in token and token.isalpha() and len(token)>=len_limit]
        else:
            raise TypeError(f"'desc' should be str but {type(desc)} detected")

    @staticmethod
    def _call_textstat(desc):
        '''
        Get an estimated school grade level required to understand the text from textstat package (reference: https://github.com/shivam5992/textstat)
        parameter desc[string]: description text
        '''
        if type(desc) is str:
            res = textstat.text_standard(desc)
            grade = int(re.sub('[a-z]','',res.split(' ')[0]))
            return grade
        else:
            raise TypeError(f"'desc' should be str but {type(desc)} detected")

    #### Get the socres based on the length of description
    @property
    def get_length_score(self):
        if 'length_score' in self.__dict__:
            return self.length_score
        else:
            self._desc_length = {x:len(x.replace('UMLS Semantic Type:','').replace('UMLS_STY:','')) for x in self.desc_list}
            self.length_score = {desc: index for index, (desc, _) in enumerate(sorted(self._desc_length.items(), key=lambda item: item[1], reverse=True))}
            return self.length_score

    #### Get the socres based on the number of detected human-readable words from biobert model
    @property
    def get_word_num_score(self):
        if 'word_num_score' in self.__dict__:
            return self.word_num_score
        else:
            self._desc_detected_word_num = {x:len(self._call_biobert_tokenizer(x)) for x in self.desc_list}
            self.word_num_score = {desc: index for index, (desc, _) in enumerate(sorted(self._desc_detected_word_num.items(), key=lambda item: item[1], reverse=True))}
            return self.word_num_score

    #### Get the socres based on the estimated school grade level returned from textstat.text_standard
    @property
    def get_readability_level_score(self):
        if 'readability_level_score' in self.__dict__:
            return self.readability_level_score
        else:
            self._desc_school_grade_level = {x:self._call_textstat(x) for x in self.desc_list}
            self.readability_level_score = {desc: index for index, (desc, _) in enumerate(sorted(self._desc_school_grade_level.items(), key=lambda item: -1 / (item[1]+0.0000000001), reverse=False))}
            return self.readability_level_score

    #### Get the final socres based on three rules
    @property
    def get_final_score(self):
        if 'final_score' in self.__dict__:
            return self.final_score 
        else:
            length_score = self.get_length_score
            word_num_score = self.get_word_num_score
            readability_level_score = self.get_readability_level_score
            self._sum_score = {key:length_score[key]+word_num_score[key]+readability_level_score[key] for key in length_score}
            self.final_score = {desc: index for index, (desc, _) in enumerate(sorted(self._sum_score.items(), key=lambda item: item[1], reverse=False))}
            return self.final_score

    #### Get the best description with the lowest score
    @property
    def get_best_description(self):
        if len(self.desc_list) == 0:
            return None
        elif len(self.desc_list) == 1:
            return self.desc_list[0]
        else:
            final_score = self.get_final_score
            for desc in final_score:
                if desc != '':
                    return desc
                else:
                    next
            return self.desc_list[0]


# code/reasoningtool/CmdPubMedNGD.py

'''Returns the Normalized Google semantic distance between two string MeSH terms

   Usage:    python3 CmdPubMedNGD.py term1 term2

   Example:  python3 CmdPubMedNGD.py atherosclerosis hypercholesterolemia

   Output:   JSON dump of dict (keys are "value" and "status")
'''
__author__ = 'Stephen Ramsey'
__copyright__ = 'Oregon State University'
__credits__ = ['Stephen Ramsey']
__license__ = 'MIT'
__version__ = '0.1.0'
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'

import argparse
import math
import json
from QueryNCBIeUtils import QueryNCBIeUtils

def main():
    parser = argparse.ArgumentParser(description="Returns the Normalized Google semantic distance between two string MeSH terms",
                                    formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('terms', metavar='terms', type=str, nargs=2, help='two string arguments; must be MeSH terms')
    args = parser.parse_args()
    mesh_terms = args.terms
    ngd = QueryNCBIeUtils.normalized_google_distance(*mesh_terms)
    res_dict = dict()
    res_dict['value'] = ngd
    
    if math.isnan(ngd):
        res_dict['status'] = 'unsuccessful'
        if not QueryNCBIeUtils.is_mesh_term(mesh_terms[0]):
            res_dict['status'] += '; Term 1 is not a valid MeSH term'
        if not QueryNCBIeUtils.is_mesh_term(mesh_terms[1]):
            res_dict['status'] += '; Term 2 is not a valid MeSH term'
    else:
        res_dict['status'] = 'success'
    print(json.dumps(res_dict))
    
if __name__ == "__main__":
    main()



# code/reasoningtool/Neo4jToNetworkX.py

import networkx as nx
import cypher
from collections import namedtuple

import sys, os

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../")  # code directory
from RTXConfiguration import RTXConfiguration
rtxConfig = RTXConfiguration()

# Connection information for the ipython-cypher package
connection = "http://" + rtxConfig.neo4j_username + ":" + rtxConfig.neo4j_password + "@" + rtxConfig.neo4j_database
DEFAULT_CONFIGURABLE = {
	"auto_limit": 0,
	"style": 'DEFAULT',
	"short_errors": True,
	"data_contents": True,
	"display_limit": 0,
	"auto_pandas": False,
	"auto_html": False,
	"auto_networkx": False,
	"rest": False,
	"feedback": False,  # turn off verbosity in ipython-cypher
	"uri": connection,
}
DefaultConfigurable = namedtuple(
	"DefaultConfigurable",
	", ".join([k for k in DEFAULT_CONFIGURABLE.keys()])
)
config = DefaultConfigurable(**DEFAULT_CONFIGURABLE)

# Convert neo4j subgraph (from cypher query) into a networkx graph
def get_graph(res, directed=True):
	"""
	This function takes the result (subgraph) of a ipython-cypher query and builds a networkx graph from it
	:param res: output from an ipython-cypher query
	:param directed: Flag indicating if the resulting graph should be treated as directed or not
	:return: networkx graph (MultiDiGraph or MultiGraph)
	"""
	if nx is None:
		raise ImportError("Try installing NetworkX first.")
	if directed:
		graph = nx.MultiDiGraph()
	else:
		graph = nx.MultiGraph()
	for item in res._results.graph:
		for node in item['nodes']:
			graph.add_node(node['id'], properties=node['properties'], labels=node['labels'], names=node['properties']['name'], description=node['properties']['description'])
		for rel in item['relationships']:
			graph.add_edge(rel['startNode'], rel['endNode'], id=rel['id'], properties=rel['properties'], type=rel['type'])
	return graph

def test_get_graph():
	query = "MATCH path=allShortestPaths((s:omim_disease)-[*1..%d]-(t:disont_disease)) " \
			"WHERE s.name='%s' AND t.name='%s' " \
			"RETURN path" % (4, 'OMIM:137920', 'DOID:11476')
	res = cypher.run(query, conn=connection, config=config)
	graph = get_graph(res, directed=True)
	if type(graph) is not nx.classes.MultiDiGraph:
		raise(Exception("A networkx graph was not returned"))
	if graph.number_of_nodes() < 1:
		raise(Exception("An empty graph was returned"))


# code/reasoningtool/sharedtrunk.py

#!/usr/bin/python3

# sharedtrunk.py: accept a Translator result-set in API v0.9 format and count up
# the number of times each edge appears among all the results. Print out a list
# of all edges, sorted by count in reverse order. Filter the resulting list of
# edges by minimum count (default 2). Result-set is provided as a URL (a "GET"
# assumed).

# Usage:
# sharedtrunk.py [--mincount=2] url

import requests
import requests_testadapter
import argparse
import json
import os
import collections

parser = argparse.ArgumentParser(description='sharedtrunk')
parser.add_argument('--mincount', type=int, help='whole number', nargs=1, default=2)
parser.add_argument('url', type=str, nargs=1)

# usage: sharedtrunk.py URL 
mincount = 2

from requests_testadapter import Resp

class LocalFileAdapter(requests.adapters.HTTPAdapter):
    def build_response_from_file(self, request):
        file_path = request.url[7:]
        with open(file_path, 'rb') as file:
            buff = bytearray(os.path.getsize(file_path))
            file.readinto(buff)
            resp = Resp(buff)
            r = self.build_response(request, resp)
            return r

    def send(self, request, stream=False, timeout=None,
             verify=True, cert=None, proxies=None):
        return self.build_response_from_file(request)


requests_session = requests.session()
requests_session.mount('file://', LocalFileAdapter())
args = parser.parse_args()
assert len(args.url) == 1, "need to specify url as the first argument"
url = args.url[0]
req_res = requests_session.get(url)
input_json = req_res.json()

res_list = input_json['result_list']
res_iter = iter(res_list)
master_graph = next(res_iter)['result_graph']
master_nodes_list = master_graph['node_list']
master_edges_list = master_graph['edge_list']

node_ctr = collections.Counter()
edge_ctr = collections.Counter()

master_nodes_dict = dict()
master_edges_dict = dict()

def make_key(source_curie: str, target_curie: str):
    return source_curie + '---' + target_curie

for node in master_nodes_list:
    curie = node['id']
    name = node['name']
    master_nodes_dict[curie] = node

for edge in master_edges_list:
    source_curie = edge['source_id']
    target_curie = edge['target_id']
    key = make_key(source_curie, target_curie)
    master_edges_dict[key] = edge
    
for result in res_iter:
    res_graph = result['result_graph']
    for node in res_graph['node_list']:
        curie = node['id']
        node_ctr[curie] += 1
    for edge in res_graph['edge_list']:
        source_curie = edge['source_id']
        target_curie = edge['target_id']
        key = make_key(source_curie, target_curie)
        edge_ctr[key] += 1

counts_inorder = node_ctr.most_common()
counts_inorder_filt = [(node_id, node_count) for (node_id, node_count) in counts_inorder if node_count >= mincount]
nodes_matched_filt_set = set([node_id for (node_id, node_count) in counts_inorder_filt])
shared_edges = set()

for node1 in nodes_matched_filt_set:
    for node2 in nodes_matched_filt_set:
        key = make_key(node1, node2)
        if key in master_edges_dict:
            shared_edges.add(key)

edge_count = [(key, count) for (key, count) in edge_ctr.most_common() if key in shared_edges]

res_data = {'node_count': counts_inorder_filt,
            'edge_count': edge_count}

print(json.dumps(res_data))

    









# upgrade_requirements.py



def get_package_name(package_with_version: str) -> str:
    return package_with_version.split("=")[0].replace(">", "").replace("<", "")


with open(f"requirements.txt") as old_requirements_file:
    old_requirements = tuple(old_requirements_file)
with open(f"requirements-newfreeze.txt") as new_freeze_file:
    new_requirements = tuple(new_freeze_file)
packages_dict = dict()

index_line = old_requirements[0]
for old_requirement in old_requirements[1:]:  # Skip the --index line
    old_requirement.replace("\n", "")
    package = get_package_name(old_requirement)
    old_version_str = old_requirement.replace(package, "")
    packages_dict[package] = old_version_str

for new_requirement in new_requirements:
    package = get_package_name(new_requirement)
    if package in packages_dict:
        version_str = new_requirement.replace(package, "")
        packages_dict[package] = version_str

with open(f"requirements_new.txt", "w+") as requirements_file:
    requirements_file.write(index_line)
    for package, version_str in packages_dict.items():
        requirements_file.write(f"{package}{version_str}")


# code/UI/Feedback/RTXFeedback.py

#!/usr/bin/python3
# Database definition and RTXFeedback class
import sys
def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs)

import os
import sys
import re
import json
import ast
from datetime import datetime
import pickle
import hashlib
import collections
import requests
import json
from flask import Flask,redirect

from sqlalchemy import Column, ForeignKey, Integer, Float, String, DateTime, Text, PickleType, LargeBinary
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import desc
from sqlalchemy import inspect

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../")
from RTXConfiguration import RTXConfiguration

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../OpenAPI/python-flask-server/")
from swagger_server.models.result_feedback import ResultFeedback
from swagger_server.models.feedback import Feedback
from swagger_server.models.message import Message as TxMessage
from swagger_server.models.previous_message_processing_plan import PreviousMessageProcessingPlan

#import Enricher
sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../ARAX/ARAXQuery")
from actions_parser import ActionsParser
from ARAX_filter import ARAXFilter

Base = declarative_base()

#### Define the database tables as classes
class Message(Base):
    __tablename__ = 'message09'
    message_id = Column(Integer, primary_key=True)
    message_datetime = Column(DateTime, nullable=False)
    restated_question = Column(String(255), nullable=False)
    query_type = Column(String(50), nullable=False)
    terms = Column(String(1024), nullable=False)
    tool_version = Column(String(50), nullable=False)
    result_code = Column(String(50), nullable=False)
    message = Column(Text, nullable=False)
    n_results = Column(Integer, nullable=False)
    # PickleType uses BLOB on MySQL, which is only 65k. Could not seem to work around it. Resort to LargeBinary with explicit length and my own pickling.
    #message_object = Column(PickleType, nullable=False)
    message_object = Column(LargeBinary(length=100500500), nullable=False)

class Result(Base):
    __tablename__ = 'result09'
    result_id = Column(Integer, primary_key=True)
    message_id = Column(Integer, ForeignKey('message09.message_id'))   # for backward compat, this is retained as the *first* message_id
    confidence = Column(Float, nullable=False)
    n_nodes = Column(Integer, nullable=False)
    n_edges = Column(Integer, nullable=False)
    result_text = Column(Text, nullable=False)
    result_object = Column(LargeBinary(length=16777200), nullable=False)
    result_hash = Column(String(255), nullable=False)
    message = relationship(Message)

class Message_result(Base):
    __tablename__ = 'message_result09'
    message_result_id = Column(Integer, primary_key=True)
    result_id = Column(Integer, ForeignKey('result09.result_id'))
    message_id = Column(Integer, ForeignKey('message09.message_id'))

class Commenter(Base):
    __tablename__ = 'commenter09'
    commenter_id = Column(Integer, primary_key=True)
    full_name = Column(String(255), nullable=False)
    email_address = Column(String(255), nullable=False)
    password = Column(String(255), nullable=False)

class Rating(Base):
    __tablename__ = 'rating09'
    rating_id = Column(Integer, primary_key=True)
    score = Column(Integer, nullable=False)
    tag = Column(String(50), nullable=False)
    name = Column(String(255), nullable=False)
    description = Column(String(255), nullable=False)

class Expertise_level(Base):
    __tablename__ = 'expertise_level09'
    expertise_level_id = Column(Integer, primary_key=True)
    score = Column(Integer, nullable=False)
    tag = Column(String(50), nullable=False)
    name = Column(String(255), nullable=False)
    description = Column(String(255), nullable=False)

class Result_rating(Base):
    __tablename__ = 'result_rating09'
    result_rating_id = Column(Integer, primary_key=True)
    result_id = Column(Integer, ForeignKey('result09.result_id'))
    commenter_id = Column(Integer, ForeignKey('commenter09.commenter_id'))
    expertise_level_id = Column(Integer, ForeignKey('expertise_level09.expertise_level_id'))
    rating_id = Column(Integer, ForeignKey('rating09.rating_id'))
    comment_datetime = Column(DateTime, nullable=False)
    comment = Column(Text, nullable=True)
    result = relationship(Result)
    commenter = relationship(Commenter)
    expertise_level = relationship(Expertise_level)
    rating = relationship(Rating)


#### The main RTXFeedback class
class RTXFeedback:

    #### Constructor
    def __init__(self):
        self.databaseName = "RTXFeedback"
        self.connect()

    #### Destructor
    def __del__(self):
        self.disconnect()


    #### Define attribute session
    @property
    def session(self) -> str:
        return self._session

    @session.setter
    def session(self, session: str):
        self._session = session


    #### Define attribute engine
    @property
    def engine(self) -> str:
        return self._engine

    @engine.setter
    def engine(self, engine: str):
        self._engine = engine


    #### Define attribute databaseName
    @property
    def databaseName(self) -> str:
        return self._databaseName

    @databaseName.setter
    def databaseName(self, databaseName: str):
        self._databaseName = databaseName


    #### Delete and create the RTXFeedback SQLite database. Careful!
    def createDatabase(self):
        print("Creating database")
        #if os.path.exists(self.databaseName):
        #  os.remove(self.databaseName)
        #engine = create_engine("sqlite:///"+self.databaseName)
        rtxConfig = RTXConfiguration()
        engine = create_engine("mysql+pymysql://" + rtxConfig.mysql_feedback_username + ":" + rtxConfig.mysql_feedback_password + "@" + rtxConfig.mysql_feedback_host + "/" + self.databaseName)
        Base.metadata.drop_all(engine)
        Base.metadata.create_all(engine)
        self.connect()

    #### Create and store a database connection
    def connect(self):
        #engine = create_engine("sqlite:///"+self.databaseName)
        rtxConfig = RTXConfiguration()
        engine = create_engine("mysql+pymysql://" + rtxConfig.mysql_feedback_username + ":" + rtxConfig.mysql_feedback_password + "@" + rtxConfig.mysql_feedback_host + "/" + self.databaseName)
        DBSession = sessionmaker(bind=engine)
        session = DBSession()
        self.session = session
        self.engine = engine

    #### Create and store a database connection
    def disconnect(self):
        session = self.session
        engine = self.engine
        session.close()
        try:
            engine.dispose()
        except:
            pass


    #### Pre-populate the database with reference data
    def prepopulateDatabase(self):
        session = self.session
        rating = Rating(score=100,tag='Excellent',name='Excellent result',description='This result provides a correct and complete answer to the asked question.')
        session.add(rating)
        rating = Rating(score=90,tag='Very good',name='Very good result',description='This result provides a correct but slightly incomplete answer to the asked question.')
        session.add(rating)
        rating = Rating(score=80,tag='Intriguing',name='Intriguing result',description='This result may or may not be correct but provides a very intriguing thought process that bears further scrutiny.')
        session.add(rating)
        rating = Rating(score=50,tag='Good',name='Good result',description='This result provides a mostly correct but substantially incomplete answer to the asked question.')
        session.add(rating)
        rating = Rating(score=30,tag='Okay',name='Okay result',description='This result is not incorrect but is a vast oversimplification compared with a complete answer.')
        session.add(rating)
        rating = Rating(score=20,tag='Poor',name='Poor result',description='This result is some merit but includes thought paths that are incorrect.')
        session.add(rating)
        rating = Rating(score=10,tag='Irrelevant',name='Irrelevant result',description='This result does not address the question that was asked.')
        session.add(rating)
        rating = Rating(score=0,tag='Wrong',name='Wrong result',description='This result makes assertions that are wrong.')
        session.add(rating)
        session.commit()

        expertise_level = Expertise_level(score=100,tag='High',name='High expertise',description='Reviewer has a high degree of expertise related to this question and result. He or she writes papers on this question or treats patients with conditions related to this question.')
        session.add(expertise_level)
        expertise_level = Expertise_level(score=70,tag='Medium',name='Medium expertise',description='Reviewer has a medium degree of expertise related to this question and result. He or she is engaged in research or treats patients in a related field.')
        session.add(expertise_level)
        expertise_level = Expertise_level(score=50,tag='Some',name='Some expertise',description='Reviewer has some familiarity related with this question and result, but not enough to be authoritative.')
        session.add(expertise_level)
        expertise_level = Expertise_level(score=30,tag='Low',name='Low expertise',description='Reviewer has low familiarity with this question and result, but has basic understanding of biology and disease.')
        session.add(expertise_level)
        expertise_level = Expertise_level(score=10,tag='None',name='No expertise',description='Reviewer might best keep his thoughts on this to himself, but cannot prevent himself from amusing his colleagues with irrelevant nonsense. Coder.')
        session.add(expertise_level)
        session.commit()

        commenter = Commenter(full_name='Test User',email_address='testuser@systemsbioloy.org',password='None')
        session.add(commenter)
        session.commit()


    #### Store a new Message into the database
    def addNewMessage(self,message,query):
        session = self.session

        #### Update the n_results information
        n_results = 0
        if message.results is not None:
            n_results = len(message.results)
        if message.code_description is None:
            plural = "s"
            if n_results == 1: plural = ""
            message.code_description = "Query returned %i result%s" % (n_results,plural)

        #### Add result metadata
        if message.results is not None:
            for result in message.results:
                if result.reasoner_id is None:
                    result.reasoner_id = "ARAX"

        #### Update the message with current information
        rtxConfig = RTXConfiguration()
        if message.tool_version is None:
            message.tool_version = rtxConfig.version
        if message.schema_version is None:
            message.schema_version = "0.9.3"
        if message.reasoner_id is None:
            message.reasoner_id = "ARAX"
        message.n_results = n_results
        message.type = "translator_reasoner_message"
        message.context = "https://raw.githubusercontent.com/biolink/biolink-model/master/context.jsonld"
        message.datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        if message.restated_question is None:
            message.restated_question = ""
        if message.original_question is None:
            message.original_question = ""

        termsString = "{}"
        query_type_id = 0
        if query is not None and "query_type_id" in query:
            query_type_id = query["query_type_id"]
        if query_type_id == 0 and query is not None and "message" in query and "query_type_id" in query["message"]:
            query_type_id = query["message"]["query_type_id"]

        if query is not None and "message" in query:
            if "terms" in query["message"]:
                termsString = stringifyDict(query["message"]["terms"])
            elif "query_graph" in query["message"]:
                termsString = stringifyDict(query["message"]["query_graph"])

        storedMessage = Message(message_datetime=datetime.now(),restated_question=message.restated_question,query_type=query_type_id,
            terms=termsString,tool_version=rtxConfig.version,result_code=message.message_code,message=message.code_description,n_results=n_results,message_object=b'')
        session.add(storedMessage)
        session.flush()
        session.commit()
        message.id = "https://arax.ncats.io/api/rtx/v1/message/"+str(storedMessage.message_id)

        #### Instead of storing the message in the MySQL database as a message_object (the old way)
        #### Instead now store it as a JSON file on the filesystem
        message_dir = os.path.dirname(os.path.abspath(__file__)) + '/../../../data/responses'
        if not os.path.exists(message_dir):
            try:
                os.mkdir(message_dir)
            except:
                eprint(f"ERROR: Unable to create dir {message_dir}")

        if os.path.exists(message_dir):
            message_filename = f"{storedMessage.message_id}.json"
            message_path = f"{message_dir}/{message_filename}"
            try:
                with open(message_path, 'w') as outfile:
                    json.dump(message.to_dict(), outfile, sort_keys=True)
            except:
                eprint(f"ERROR: Unable to write message to file {message_path}")

        #### This has been mostly castrated but it still puts ids in there, and may be resurrected someday
        self.addNewResults(storedMessage.message_id,message)

        #### After updating all the ids, store an updated object
        #### No longer needed because we're writing to a file after the INSERT
        #storedMessage.message_object=pickle.dumps(ast.literal_eval(repr(message)))
        #session.commit()

        return storedMessage.message_id


    #### We used to store all the result as separate entities in the database, mostly to allow feedback on each one.
    #### But this was not used and was slow, so this is being purged from the codebase on 2020-09-22. Can always bring
    #### it back if desired. But set everything to a plain integer count for now. Not correct, but
    #### not used anyway.
    def addNewResults(self,message_id,message):
        if message.results is None:
            return

        id_counter = 1
        for result in message.results:

            if result.result_type is None:
                result.result_type = "individual query answer"
            if result.confidence is None:
                result.confidence = 0
            try:
                result.confidence = float(result.confidence)
            except:
                eprint(f"WARNING: Confidence value '{result.confidence}' cannot be converted to float")
                result.confidence = -999.0

            result.id = f"https://arax.ncats.io/api/rtx/v1/result/{id_counter}"
            id_counter += 1


    #### Get a previously stored message for this query from the database
    def getCachedMessage(self,query):
        if "bypass_cache" in query and query["bypass_cache"] == "true":
            return
        session = self.session
        rtxConfig = RTXConfiguration()
        tool_version = rtxConfig.version
        termsString = stringifyDict(query["message"]["terms"])

        #### Look for previous messages we could use
        storedMessage = session.query(Message).filter(Message.query_type==query["message"]["query_type_id"]).filter(Message.tool_version==tool_version).filter(Message.terms==termsString).order_by(desc(Message.message_datetime)).first()
        if ( storedMessage is not None ):
            return pickle.loads(storedMessage.message_object)
        return


    #### Get the list of ratings
    def getRatings(self):
        session = self.session
        message = { "ratings": [] }
        count = 0
        for rating in session.query(Rating).all():
            message["ratings"].append(object_as_dict(rating))
            count += 1
        message["n_ratings"] = count
        return(message)


    #### Get the list of expertise levels
    def getExpertiseLevels(self):
        session = self.session
        message = { "expertise_levels": [] }
        count = 0
        for level in session.query(Expertise_level).all():
            message["expertise_levels"].append(object_as_dict(level))
            count += 1
        message["n_expertise_levels"] = count
        return(message)


    #### Fetch a cached message
    def getMessage(self, message_id):
        session = self.session

        if message_id is None:
            return( { "status": 400, "title": "message_id missing", "detail": "Required attribute message_id is missing from URL", "type": "about:blank" }, 400)

        #### Find the message
        storedMessage = session.query(Message).filter(Message.message_id==message_id).first()
        if storedMessage is not None:
            if len(storedMessage.message_object) < 5:
                message_dir = os.path.dirname(os.path.abspath(__file__)) + '/../../../data/responses'
                message_filename = f"{storedMessage.message_id}.json"
                message_path = f"{message_dir}/{message_filename}"
                try:
                    with open(message_path) as infile:
                        return json.load(infile)
                except:
                    eprint(f"ERROR: Unable to read message from file '{message_path}'")

            else:
                return pickle.loads(storedMessage.message_object)
        else:
            return( { "status": 404, "title": "Message not found", "detail": "There is no message corresponding to message_id="+str(message_id), "type": "about:blank" }, 404)


    #### Get a previously stored message for this query from the database
    def processExternalPreviousMessageProcessingPlan(self,inputEnvelope):
        debug = 1
        if debug: eprint("DEBUG: Entering processExternalPreviousMessageProcessingPlan")
        messages = []
        finalMessage = None
        finalMessage_id = None
        query = None

        #### Pull out the main processing plan envelope
        envelope = PreviousMessageProcessingPlan.from_dict(inputEnvelope["previous_message_processing_plan"])

        #### If there are URIs provided, try to load them
        if envelope.previous_message_uris is not None:
            if debug: eprint("DEBUG: Got previous_message_uris")
            for uri in envelope.previous_message_uris:
                if debug: eprint("DEBUG:   messageURI="+uri)
                matchResult = re.match( r'http[s]://arax.ncats.io/.*api/rtx/.+/message/(\d+)',uri,re.M|re.I )
                if matchResult:
                    message_id = matchResult.group(1)
                    if debug: eprint("DEBUG: Found local ARAX identifier corresponding to message_id "+message_id)
                    if debug: eprint("DEBUG: Loading message_id "+message_id)
                    message = self.getMessage(message_id)
                    #eprint(type(message))
                    if not isinstance(message,tuple):
                        if debug: eprint("DEBUG: Original question was: "+message["original_question"])
                        messages.append(message)
                        finalMessage_id = message_id
                        query = { "query_type_id": message["query_type_id"], "restated_question": message["restated_question"], "terms": message["terms"] }
                    else:
                        eprint("ERROR: Unable to load message_id "+message_id)
                        return( { "status": 404, "title": "Message not found", "detail": "There is no local message corresponding to message_id="+str(message_id), "type": "about:blank" }, 404)

        #### If there are one or more previous_messages embedded in the POST, process them
        if envelope.previous_messages is not None:
            if debug: eprint("DEBUG: Got previous_messages")
            for uploadedMessage in envelope.previous_messages:
                if debug: eprint("DEBUG: uploadedMessage is a "+str(uploadedMessage.__class__))
                if str(uploadedMessage.__class__) == "<class 'swagger_server.models.message.Message'>":
                    if uploadedMessage.results:
                        message = ast.literal_eval(repr(uploadedMessage))
                        messages.append(message)

                        if message["terms"] is None:
                            message["terms"] = { "dummyTerm": "giraffe" }
                        if message["query_type_id"] is None:
                            message["query_type_id"] = "UnknownQ"
                        if message["restated_question"] is None:
                            message["restated_question"] = "What is life?"
                        if message["original_question"] is None:
                            message["original_question"] = "what is life"

                        query = { "query_type_id": message["query_type_id"], "restated_question": message["restated_question"], "original_question": message["original_question"], "terms": message["terms"] }
                    else:
                        eprint("Uploaded message does not contain a results. May be the wrong format")
                        return( { "status": 404, "title": "Bad uploaded Message", "detail": "There is no results in the uploaded Message object=", "type": "about:blank" }, 404)
                else:
                    eprint("Uploaded message is not of type Message. It is of type"+str(uploadedMessage.__class__))
                    return( { "status": 404, "title": "Bad uploaded Message", "detail": "Uploaded message is not of type Message. It is of type"+str(uploadedMessage.__class__), "type": "about:blank" }, 404)

        #### Take different actions based on the number of messages we now have in hand
        n_messages = len(messages)
        if n_messages == 0:
            return( { "status": 499, "title": "No Messages", "detail": "Did not get any useful Message objects", "type": "about:blank" }, 499)
        elif n_messages == 1:
            finalMessage = messages[0]
        else:
            finalMessage = TxMessage.from_dict(messages[0])
            counter = 1
            while counter < n_messages:
                messageToMerge = TxMessage.from_dict(messages[counter])
                if messageToMerge.reasoner_id is None:
                    messageToMerge.reasoner_id = "Unknown"
                if messageToMerge.reasoner_id != "ARAX":
                    messageToMerge = self.fix_message(query,messageToMerge,messageToMerge.reasoner_id)

                finalMessage = self.merge_message(finalMessage,messageToMerge)
                counter += 1
            finalMessage = ast.literal_eval(repr(finalMessage))
            #return( { "status": 498, "title": "Multiple Messages", "detail": "I have multiple messages. Merging code awaits!", "type": "about:blank" }, 498)

        #### Examine the options that were provided and act accordingly
        optionsDict = {}
        if envelope.options:
            if debug: eprint("DEBUG: Got options")
            for option in envelope.options:
                if debug: eprint("DEBUG:   option="+option)
                optionsDict[option] = 1

        #### If there are processing_actions, then fulfill those
        processing_actions = []
        if envelope.processing_actions:
            if debug: eprint("DEBUG: Found processing_actions")
            actions_parser = ActionsParser()
            result = actions_parser.parse(envelope.processing_actions)
            if result.error_code != 'OK':
                eprint(result)
                raise()

            #### Message suffers from a dual life as a dict and an object. above we seem to treat it as a dict. Fix that. FIXME
            #### Below we start treating it as and object. This should be the way forward.
            #### This is not a good place to do this, but may need to convert here
            from ARAX_messenger import ARAXMessenger
            finalMessage = ARAXMessenger().from_dict(finalMessage)

            #### Process each action in order
            action_stats = { }
            actions = result.data['actions']
            for action in actions:
                if debug: eprint(f"DEBUG: Considering action '{action['command']}' with parameters {action['parameters']}")
                #### If we encounter a return, then this is the end of the line
                if action['command'] == 'return':
                    action_stats['return_action'] = action
                    break
                if action['command'] == 'filter':
                    filter = ARAXFilter()
                    result = filter.apply(finalMessage,action['parameters'])
                if result.error_code != 'OK':
                    response = result
                    break
                else:
                    if debug: eprint(f"DEBUG: Action '{action['command']}' is not known")

            #### At the end, process the explicit return() action, or implicitly perform one
            return_action = { 'command': 'return', 'parameters': { 'message': 'false', 'store': 'false' } }
            if action is not None and action['command'] == 'return':
                return_action = action
                #### If an explicit one left out some parameters, set the defaults
                if 'store' not in return_action['parameters']:
                    return_action['parameters']['store'] == 'false'
                if 'message' not in return_action['parameters']:
                    return_action['parameters']['message'] == 'false'

        #if "AnnotateDrugs" in optionsDict:
        #  if debug: eprint("DEBUG: Annotating drugs")
        #  annotate_std_results(finalMessage)

        if return_action['parameters']['store'] == 'true':
            if debug: eprint("DEBUG: Storing resulting Message")
            finalMessage_id = self.addNewMessage(TxMessage.from_dict(finalMessage),query)

        #### If requesting a full redirect to the resulting message display. This doesn't really work I don't think
        #if "RedirectToMessage" in optionsDict:
        #  #redirect("https://arax.ncats.io/api/rtx/v1/message/"+str(finalMessage_id), code=302)
        #  #return( { "status": 302, "redirect": "https://arax.ncats.io/api/rtx/v1/message/"+str(finalMessage_id) }, 302)
        #  return( "Location: https://arax.ncats.io/api/rtx/v1/message/"+str(finalMessage_id), 302)

        #### If asking for the full message back
        if return_action['parameters']['message'] == 'true':
            return(finalMessage)

        #### Else just the id is returned
        else:
            #return( { "status": 200, "message_id": str(finalMessage_id), "n_results": finalMessage['n_results'], "url": "https://arax.ncats.io/api/rtx/v1/message/"+str(finalMessage_id) }, 200)
            return( { "status": 200, "message_id": str(finalMessage_id), "n_results": finalMessage.n_results, "url": "https://arax.ncats.io/api/rtx/v1/message/"+str(finalMessage_id) }, 200)


    ##########################################################################################################################
    def fix_message(self,query,message,reasoner_id):

        if reasoner_id == "ARAX":
            base_url = "https://arax.ncats.io/api/rtx/v1"
        elif reasoner_id == "Robokop":
            base_url = "http://robokop.renci.org:6011/api"
        elif reasoner_id == "Indigo":
            base_url = "https://indigo.ncats.io/reasoner/api/v0"
        else:
            base_url = "https://unknown.url.org/"
            eprint("ERROR: Unrecognized reasoner_id '"+reasoner_id+"'")

        if message.context is None:
            message.context = "https://raw.githubusercontent.com/biolink/biolink-model/master/context.jsonld"
        if message.id is None or message.id == "":
            message.id = base_url + "/message/1234"
        message.original_question = query["original_question"]
        message.restated_question = query["restated_question"]
        message.reasoner_id = reasoner_id
        if message.message_code is None or message.message_code == "":
            message.message_code = "OK"
        if message.n_results is None:
            if message.results is not None:
                message.n_results = len(message.results)
            else:
                message.n_results = 0
        if message.code_description is None or message.code_description == "":
            message.code_description = str(message.n_results) + " results returned"

        if message.results is not None:
            result_id = 2345
            for result in message.results:
                if result.id is None or result.id == "":
                    result.id = base_url + "/result/" + str(result_id)
                    result_id += 1
                if result.reasoner_id is None or result.reasoner_id == "":
                    result.reasoner_id = reasoner_id
                if result.confidence is None:
                    result.confidence = 0

        return(message)


    def merge_message(self,final_message,message_to_merge):
        for result in message_to_merge.results:
            final_message.results.append(result)
        final_message.n_results = len(final_message.results)
        final_message.code_description = str(final_message.n_results) + " merged reults"
        return(final_message)




############################################ General functions ###############################################
#### Turn a row into a dict
def object_as_dict(obj):
    return {c.key: getattr(obj, c.key)
        for c in inspect(obj).mapper.column_attrs}

#### convert a dict into a string in guaranteed repeatable order i.e. sorted
def stringifyDict(inputDict):
    outString = "{"
    for key,value in sorted(inputDict.items(), key=lambda t: t[0]):
        if outString != "{":
            outString += ","
        outString += "'"+str(key)+"':'"+str(value)+"'"
    outString += "}"
    return(outString)



# This is from Kevin Xin from team orange.
# It performs MOD1 and MOD2 (annotation and scoring modules) of workflow 1
# input std API message format
# output std API message format

def annotate_drug(drug_id, id_type):
    """
    Provide annotation for drug
    """
    if id_type == 'chembl':
        query_template = 'http://mychem.info/v1/query?q=drugcentral.xrefs.chembl_id:{{drug_id}}&fields=drugcentral'
    elif id_type == 'chebi':
        query_template = 'http://mychem.info/v1/query?q=drugcentral.xrefs.chebi:"{{drug_id}}"&fields=drugcentral'
    query_url = query_template.replace('{{drug_id}}', drug_id)
    results = {'annotate': {'common_side_effects': None, 'approval': None, 'indication': None, 'EPC': None}}
    api_message = requests.get(query_url).json()

    # get drug approval information from mychem
    approval = DictQuery(api_message).get("hits/drugcentral/approval")
    if approval:
        results['annotate']['approval'] = 'Yes'
    # get drug approved indication information
    indication = DictQuery(api_message).get("hits/drugcentral/drug_use/indication")
    if len(indication) > 0 and indication[0] and not isinstance(indication[0], list):
        results['annotate']['indication'] = [_doc['snomed_full_name'] for _doc in indication if
                                             'snomed_full_name' in _doc]
    elif len(indication) > 0 and indication[0]:
        results['annotate']['indication'] = [_doc['snomed_full_name'] for _doc in indication[0] if
                                             'snomed_full_name' in _doc]
        # get drug established pharm class information
    epc = DictQuery(api_message).get("hits/drugcentral/pharmacology_class/fda_epc")
    if len(epc) > 0 and epc[0] and not isinstance(epc[0], list):
        results['annotate']['EPC'] = [_doc['description'] for _doc in epc if 'description' in _doc]
    elif len(epc) > 0 and epc[0]:
        results['annotate']['EPC'] = [_doc['description'] for _doc in epc[0] if 'description' in _doc]
        # get drug common side effects
    side_effects = DictQuery(api_message).get("hits/drugcentral/fda_adverse_event")
    if len(side_effects) > 0 and side_effects[0]:
        if isinstance(side_effects[0], list):
            # only keep side effects with likelihood higher than the threshold
            results['annotate']['common_side_effects'] = [_doc['meddra_term'] for _doc in side_effects[0] if
                                                          _doc['llr'] > _doc['llr_threshold']]
            if len(results['annotate']['common_side_effects']) > 10:
                results['annotate']['common_side_effects'] = results['annotate']['common_side_effects'][:10]
        elif isinstance(side_effects[0], dict) and 'meddra_term' in side_effects[0]:
          results['annotate']['common_side_effects'] = side_effects[0]['meddra_term']

    #### EWD: Now transform this into the schema!
    results = unlist(results)
    node_attributes = []
    for key,value in results["annotate"].items():
      if isinstance(value,list):
        counter = 0
        for item in value:
          node_attributes.append( { "name": key, "value": item } )
          counter += 1
          if counter > 11: break
      else:
        node_attributes.append( { "name": key, "value": value } )
    return node_attributes

    #return unlist(results)
    

"""
Helper functions
"""
def unlist(d):
    """
    If the list contain only one element, unlist it
    """
    for key, val in d.items():
        if isinstance(val, list):
            if len(val) == 1:
                d[key] = val[0]
        elif isinstance(val, dict):
            unlist(val)
    return d

class DictQuery(dict):
    """
    Helper function to fetch value from a python dictionary
    """
    def get(self, path, default = None):
        keys = path.split("/")
        val = None

        for key in keys:
            if val:
                if isinstance(val, list):
                    val = [ v.get(key, default) if v else None for v in val]
                else:
                    val = val.get(key, default)
            else:
                val = dict.get(self, key, default)

            if not val:
                break;

        return val


def annotate_std_results(input_json_doc):
    """
    Annotate results from reasoner's standard output
    """
    for _doc in input_json_doc['results']:
        if 'result_graph' in _doc and _doc['result_graph'] is not None:
            eprint("Found result_graph and it is")
            eprint(_doc['result_graph'])
            for _node in _doc['result_graph']['nodes']:
                if _node['id'].startswith('CHEMBL'):
                    _drug = _node['id'].split(':')[-1]
                    _node['node_attributes'] = annotate_drug(_drug, 'chembl')
                elif _node['id'].startswith("CHEBI:"):
                    _node['node_attributes'] = annotate_drug(_node['id'], 'chebi')
    if 'knowledge_graph' in input_json_doc:
        for _node in input_json_doc['knowledge_graph']['nodes']:
            if _node['id'].startswith('CHEMBL'):
                _drug = _node['id'].split(':')[-1]
                _node['node_attributes'] = annotate_drug(_drug, 'chembl')
            elif _node['id'].startswith("CHEBI:"):
                _node['node_attributes'] = annotate_drug(_node['id'], 'chebi')
    return input_json_doc






#### If this class is run from the command line, perform a short little test to see if it is working correctly
def main():

    #### Create a new RTXFeedback object
    rtxFeedback = RTXFeedback()
    envelope = PreviousMessageProcessingPlan()
    #envelope.options = [ "Store", "RedirectToMessage" ]
    envelope.options = [ "AnnotateDrugs", "Store", "ReturnMessageId" ]
    #envelope.options = [ "ReturnMessage" ]
    #envelope.options = [ "AnnotateDrugs", "ReturnMessage" ]
    envelope.message_ur_is = [ "https://arax.ncats.io/api/rtx/v1/message/300" ]

    #result = rtxFeedback.processExternalPreviousMessageProcessingPlan(envelope)
    #print(result)



    #### Careful, don't destroy an important database!!!
    ##rtxFeedback.createDatabase()
    ##rtxFeedback.prepopulateDatabase()
    #sys.exit()

    #### Connect to the database
    session = rtxFeedback.session

    #### Query and print some rows from the reference tables
    print("Querying database")
    for rating in session.query(Rating).all():
        print(rating)
        print("rating_id="+str(rating.rating_id)+"  name="+rating.name+"\n")

    for expertise_level in session.query(Expertise_level).all():
        print(expertise_level)
        print("expertise_level_id="+str(expertise_level.expertise_level_id)+"  name="+expertise_level.name+"\n")

    print(rtxFeedback.getRatings())


if __name__ == "__main__": main()


# code/UI/Feedback/RTXFeedback_Test1.py

#!/usr/bin/python3
# Some example test code for the RTX feedback system

import os
import sys
import json
import ast

from RTXFeedback import RTXFeedback
import requests
sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../OpenAPI/python-flask-server")
from swagger_server.models.response import Response

url = "http://arax.ncats.io/api/rtx/v1/query"

query = {
  "known_query_type_id": "Q3",
  "original_question": "what proteins does acetaminophen target",
  "restated_question": "Which proteins are the target of acetaminophen?",
  "terms": {
    "chemical_substance": "CHEMBL112",
    "rel_type": "directly_interacts_with",
    "target_label": "protein"
    }
  }

query = {
  "known_query_type_id": "Q0",
  "original_question": "what is malaria",
  "restated_question": "What is malaria",
  "terms": {
    "term": "malaria"
  }
}


#### Create an RTX Feedback management object
rtxFeedback = RTXFeedback()

#### Purge and re-create the database if desired
#rtxFeedback.createDatabase()
#rtxFeedback.prepopulateDatabase()

#### Connect to the database
rtxFeedback.connect()

#### Fetch a cached response based on this query if there is one
cachedResponse = rtxFeedback.getCachedResponse(query)
#cachedResponse = None

#### If there was one, then return it
if ( cachedResponse is not None ):
  apiResponse = Response().from_dict(cachedResponse)

#### Otherwise, send the query to the web service (which creates an entry in the cache)  
else:
  httpResponse = requests.post(url,json=query)
  assert(httpResponse.status_code == 200)
  apiResponse = Response.from_dict(httpResponse.json())
  rtxFeedback.addNewResponse(apiResponse,query)

#### Print out the result as JSON
dumpString = json.dumps(ast.literal_eval(repr(apiResponse)),sort_keys=True,indent=2)
print(dumpString[0:1000]+"\n...")


# code/UI/Feedback/reinitiatizeDatabase.py

#!/bin/env python3
# Reinitialize the Message/Feedback MySQL database

import os
import sys
import json
import ast

from RTXFeedback import RTXFeedback

if len(sys.argv) < 2:
    print("Run reinitializeDatabase.pl with the parameter 'yes' if you really want to delete and re-create the feedback/Message database")
    sys.exit(0)

if sys.argv[1] != 'yes':
    print("If you really want to delete and re-create the feedback/Message database, use:")
    print("  python reinitializeDatabase.py yes")
    sys.exit(0)

#### Create an RTX Feedback management object
rtxFeedback = RTXFeedback()

#### Purge and re-create the database
rtxFeedback.createDatabase()
rtxFeedback.prepopulateDatabase()

#### Connect to the database
rtxFeedback.connect()



# code/UI/Feedback/sqlalchemy_ericTest1.py

#!/usr/bin/python3
# Trivial sample program fiddling with SQLAlchemy

import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
 
class Person(Base):
    __tablename__ = 'person'
    # Here we define columns for the table person
    # Notice that each column is also a normal Python instance attribute.
    id = Column(Integer, primary_key=True)
    name = Column(String(250), nullable=False)
 
class Address(Base):
    __tablename__ = 'address'
    # Here we define columns for the table address.
    # Notice that each column is also a normal Python instance attribute.
    id = Column(Integer, primary_key=True)
    street_name = Column(String(250))
    street_number = Column(String(250))
    post_code = Column(String(250), nullable=False)
    person_id = Column(Integer, ForeignKey('person.id'))
    person = relationship(Person)


def main():
  print("Creating database")
  engine = create_engine('sqlite:///sqlalchemy_example.db')
  Base.metadata.create_all(engine)

  print("Writing to database")
  DBSession = sessionmaker(bind=engine)
  session = DBSession()
  new_person = Person(name='Eric')
  session.add(new_person)
  session.commit()
  new_address = Address(post_code='98008', person=new_person)
  session.add(new_address)
  session.commit()

  print("Querying database")
  for person in session.query(Person).all():
    print(person)
    print("id="+str(person.id)+"  name="+person.name+"\n")
    

if __name__ == "__main__": main()


# code/UI/autocomplete/GenerateQuestionTerms.py

import sys, os
import json

question_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
                            'reasoningtool/QuestionAnswering')
sys.path.append(question_dir)
from Question import Question

neo4j_helper_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
                                'reasoningtool/kg-construction')
sys.path.append(neo4j_helper_dir)
from Neo4jConnection import Neo4jConnection

sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../../")  # code directory
from RTXConfiguration import RTXConfiguration


class GenerateQuestionTerms:
    @staticmethod
    def __get_question_templates():
        question_templates = []
        with open(os.path.join(question_dir, 'Questions.tsv'), 'r') as fid:
            for line in fid.readlines():
                if line[0] == "#":
                    pass
                else:
                    question = Question(line)
                    question_templates.append(question)
        return question_templates

    @staticmethod
    def __get_node_names(type):
        # # connect to Neo4j
        # f = open(os.path.join(neo4j_helper_dir, 'config.json'), 'r')
        # config_data = f.read()
        # f.close()
        # config = json.loads(config_data)

        # create the RTXConfiguration object
        rtxConfig = RTXConfiguration()

        conn = Neo4jConnection(rtxConfig.neo4j_bolt, rtxConfig.neo4j_username, rtxConfig.neo4j_password)
        names = conn.get_node_names(type)
        conn.close()

        return names

    @staticmethod
    def generateQuetionsToTXT():
        question_templates = GenerateQuestionTerms.__get_question_templates()

        have_writen = False
        for i, question in enumerate(question_templates):

            # retrieve the type and template from question_template
            if len(question.parameter_names) == 0:
                continue
            type = (question.parameter_names)[0]
            question_template = question.restated_question_template

            names = GenerateQuestionTerms.__get_node_names(type)

            if len(names) != 0:
                question_content = ''
                for name in names:
                    question_phase = question_template.safe_substitute({type: name})
                    question_content = question_content + question_phase + '\n'

                # write content to file
                if have_writen:
                    with open('question_terms.txt', 'a') as w_f:
                        w_f.write(question_content)
                else:
                    with open('question_terms.txt', 'w') as w_f:
                        w_f.write(question_content)
                        have_writen = True


if __name__ == '__main__':
    GenerateQuestionTerms.generateQuetionsToTXT()


# code/UI/interactive/dsl2json.py

import datetime
import importlib
import json
import os
import re
import sys

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../ARAX/ARAXQuery")
modules = {
    "ARAX_messenger"      : {"class": "ARAXMessenger"     , "command": "create_envelope()"}, #"create_message()"},
    "ARAX_expander"       : {"class": "ARAXExpander"      , "command": "expand()"},
    "ARAX_overlay"        : {"class": "ARAXOverlay"       , "command": "overlay()"},
    "ARAX_filter_kg"      : {"class": "ARAXFilterKG"      , "command": "filter_kg()"},
    "ARAX_filter_results" : {"class": "ARAXFilterResults" , "command": "filter_results()"},
    "ARAX_resultify"      : {"class": "ARAXResultify"     , "command": "resultify()"},
    "ARAX_ranker"         : {"class": "ARAXRanker"        , "command": "rank_results()"},
    "ARAX_connect"        : {"class": "ARAXConnect"       , "command": "connect()"},
    "ARAX_infer"          : {"class": "ARAXInfer"         , "command": "infer()"}
}

araxi_json = {}

for module in modules.keys():
    m = importlib.import_module(module)
    dsl_name = modules[module]["command"]

    ## for dic in getattr(m, modules[module]["class"])().describe_me():
    for dic in sorted(getattr(m, modules[module]["class"])().describe_me(), key=lambda i: i['dsl_command']):
        #print(dic)

        if 'action' in dic:  # for classes that use the `action=` paradigm
            action = dic['action'].pop()
            del dic['action']
            dsl_name = re.sub('\(.*\)',f'(action={action})', dsl_name)
        elif 'dsl_command' in dic:  # for classes like ARAX_messenger that have different DSL commands with different top level names as methods to the main class
            dsl_command = dic['dsl_command']
            del dic['dsl_command']
            dsl_name = dsl_command

        dsl_name = re.sub('\`','', dsl_name)
        print(dsl_name, end='')
        print('.' * (60-len(dsl_name)), end='')
        
        araxi_json[dsl_name] = {}
        araxi_json[dsl_name]['parameters'] = {}

        if 'brief_description' in dic:
            araxi_json[dsl_name]['description'] = dic['brief_description']
            del dic['brief_description']
        elif 'description' in dic:
            araxi_json[dsl_name]['description'] = dic['description']
            del dic['description']

        if 'parameters' in dic:
            araxi_json[dsl_name]['parameters'] = dic['parameters']
            print('OK')
        else:
            print('found NO parameters!')


ahora = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

file_name = 'araxi.js.new'
fid = open(file_name, 'w')
fid.write("// WARNING:\n")
fid.write("// This file was auto-generated by "+__file__+" on: "+ahora+"\n")
fid.write("//\n")
fid.write("var araxi_commands = "+json.dumps(araxi_json,indent=2)+"\n")
fid.close()

print('\nOutput written to: '+file_name)


# code/UI/interactive/wf2js.py

import datetime
import json
import os
import sys
import urllib.request

jsdefs = {}

# schema at:  https://github.com/NCATSTranslator/OperationsAndWorkflows/blob/main/schema/operation.json
raw_schema = 'https://raw.githubusercontent.com/NCATSTranslator/OperationsAndWorkflows/main/schema/operation.json'

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../ARAX/ARAXQuery")
from operation_to_ARAXi import WorkflowToARAXi
wf = WorkflowToARAXi()
in_arax = wf.implemented

# for local testing:
#with open('wfschema.json') as file:
#    schema = json.load(file)

with urllib.request.urlopen(raw_schema) as file:
    schema = json.loads(file.read().decode())

    for operation in schema["$defs"]:
        id = schema["$defs"][operation]['properties']['id']['enum'][0]
        print("In: "+operation+" ("+id+")")

        jsdefs[id] = {}
        jsdefs[id]['in_arax'] = id in in_arax

        all_props = []
        if 'properties' in schema["$defs"][operation]['properties']['parameters']:
            all_props.append(schema["$defs"][operation]['properties']['parameters']['properties'])
        if 'oneOf' in schema["$defs"][operation]['properties']['parameters']:
            jsdefs[id]['warning'] = 'Only one of the following sets of parameters can be used: '
            ortxt = ''
            for p in schema["$defs"][operation]['properties']['parameters']['oneOf']:
                all_props.append(p['properties'])
                jsdefs[id]['warning'] += ortxt + '['
                comma = ''
                for o in p['properties']:
                    jsdefs[id]['warning'] += comma + o
                    comma = ', '
                jsdefs[id]['warning'] += ']'
                ortxt = ' OR '

        params = {}
        for p in all_props:
            for par in p:
                if par in params:
                    del params[par]
                params[par] = {}
                ref = p[par]
                if 'required' in schema["$defs"][operation]['properties']['parameters']:
                    params[par]['is_required'] = par in schema["$defs"][operation]['properties']['parameters']['required']
                else:
                    params[par]['is_required'] = False;

                # rename and discard some fields
                if 'minimum' in ref:
                    ref['min'] = ref.pop('minimum')
                if 'maximum' in ref:
                    ref['max'] = ref.pop('maximum')
                if 'example' in ref:
                    ref['examples'] = ref.pop('example')
                if 'items' in ref:
                    del ref['items']

                # capture what we care about
                for v in ['type', 'minLength', 'enum', 'default','min', 'max', 'examples', 'description']:
                    if v in ref:
                        params[par][v] = ref.pop(v)

                for a in ref:
                    print("--------------- missed: ["+par+"]: "+a)

        jsdefs[id]['parameters'] = params


ahora = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

file_name = 'wfoperations.js.new'
fid = open(file_name, 'w')
fid.write("// WARNING:\n")
fid.write("// This file was auto-generated by "+__file__+" on: "+ahora+"\n")
fid.write("//\n")
fid.write("var wf_operations = "+json.dumps(jsdefs,indent=2)+"\n")
fid.close()

print('\nOutput written to: '+file_name)


# code/RTXConfiguration.py

#!/usr/bin/python3

import os
import datetime
import json
import time
import re
from typing import Optional

import yaml
from pygit2 import Repository, discover_repository


class RTXConfiguration:

    _GET_FILE_CMD = "scp araxconfig@araxconfig.rtx.ai:config_secrets.json "

    # ### Constructor
    def __init__(self):
        # Determine current ARAX and TRAPI versions
        file_dir = os.path.dirname(os.path.abspath(__file__))
        openapi_yaml_path = f"{file_dir}/UI/OpenAPI/python-flask-server/openapi_server/openapi/openapi.yaml"
        with open(openapi_yaml_path) as api_file:
            openapi_yaml = yaml.safe_load(api_file)
        self.arax_version = openapi_yaml["info"]["version"]
        self.trapi_version = openapi_yaml["info"]["x-trapi"]["version"]
        first_two_trapi_version_nums = self.trapi_version.split(".")[:2]
        self.trapi_major_version = ".".join(first_two_trapi_version_nums)
        self.version = f"ARAX {self.arax_version}"  # Not sure exactly what this is used for; legacy?

        # Grab instance/domain name info, if available
        self.instance_name = '??'
        match = re.match(r'/mnt/data/orangeboard/(.+)/RTX/code', file_dir)
        if match:
            self.instance_name = match.group(1)
        if self.instance_name == 'production':
            self.instance_name = 'ARAX'

        try:
            with open(f"{file_dir}/config.domain") as infile:
                for line in infile:
                    self.domain = line.strip()
        except:
            self.domain = '??'

        # Determine the branch we're running in
        repo_path = discover_repository(file_dir)
        try:
            repo = Repository(repo_path)
            self.current_branch_name = repo.head.name.split("/")[-1]
        except Exception:
            # TODO: Figure out why Docker container doesn't like this Git branch determination method
            # Ok to skip branch name here for now since domain name can be used instead in such cases
            self.current_branch_name = None

        # Determine our maturity
        maturity_override_value = self._read_override_file(f"{file_dir}/maturity_override.txt")
        if maturity_override_value:
            self.maturity = maturity_override_value
        else:
            # Otherwise we'll dynamically determine our maturity based on instance/domain name and/or branch
            if self.domain in ["arax.ci.transltr.io", "kg2.ci.transltr.io"]:
                self.maturity = "staging"
            elif self.domain in ["arax.test.transltr.io", "kg2.test.transltr.io"] or self.current_branch_name == "itrb-test":
                self.maturity = "testing"
            elif self.domain in ["arax.transltr.io", "kg2.transltr.io"] or self.current_branch_name == "production":
                self.maturity = "production"
            elif self.domain == "arax.ncats.io":
                if self.instance_name in ["ARAX", "kg2"] or self.current_branch_name == "production":
                    self.maturity = "production"
                else:
                    self.maturity = "development"
            else:
                self.maturity = "development"

        # Determine if this is an ITRB instance or our CICD instance
        self.is_itrb_instance = "transltr.io" in self.domain  # Hacky, but works

        config_secrets_file_path = os.path.dirname(os.path.abspath(__file__)) + '/config_secrets.json'
        config_dbs_file_path = os.path.dirname(os.path.abspath(__file__)) + '/config_dbs.json'
        config_secrets_local_file_path = os.path.dirname(os.path.abspath(__file__)) + '/config_secrets_local.json'

        # Download the latest copy of config_secrets.json as appropriate (or override by local file, if present)
        if os.path.exists(config_secrets_local_file_path):
            config_secrets_file_path = config_secrets_local_file_path
        elif not os.path.exists(config_secrets_file_path):
            # scp the file
            os.system(RTXConfiguration._GET_FILE_CMD + config_secrets_file_path)
        else:
            now_time = datetime.datetime.now()
            modified_time = time.localtime(os.stat(config_secrets_file_path).st_mtime)
            modified_time = datetime.datetime(*modified_time[:6])
            if (now_time - modified_time).days > 0:
                # scp the file
                os.system(RTXConfiguration._GET_FILE_CMD + config_secrets_file_path)

        # Load the contents of the two config files (config_dbs.json lives in the repo; no need to download)
        with open(config_secrets_file_path, 'r') as config_secrets_file:
            self.config_secrets = json.load(config_secrets_file)
        with open(config_dbs_file_path, 'r') as config_dbs_file:
            self.config_dbs = json.load(config_dbs_file)

        # AG: Not sure exactly what this is doing?
        self.is_production_server = False
        if ( ( 'HOSTNAME' in os.environ and os.environ['HOSTNAME'] == '6d6766e08a31' ) or
            ( 'PWD' in os.environ and 'mnt/data/orangeboard' in os.environ['PWD'] ) ):
            self.is_production_server = True

        # Set database file paths
        self.db_host = "arax-databases.rtx.ai"
        self.db_username = "rtxconfig"
        database_downloads = self.config_dbs["database_downloads"]
        self.cohd_database_path = database_downloads["cohd_database"]
        self.cohd_database_version = self.cohd_database_path.split('/')[-1].split('_v')[-1].replace('.db', '')
        self.graph_database_path = database_downloads["graph_database"]
        self.graph_database_version = self.graph_database_path.split('/')[-1].split('_v')[-1].replace('.sqlite', '')
        self.log_model_path = database_downloads["log_model"]
        self.log_model_version = self.log_model_path.split('/')[-1].split('_v')[-1].replace('.pkl', '')
        self.curie_to_pmids_path = database_downloads["curie_to_pmids"]
        self.curie_to_pmids_version = self.curie_to_pmids_path.split('/')[-1].split('_v')[-1].replace('.sqlite', '')
        self.node_synonymizer_path = database_downloads["node_synonymizer"]
        self.node_synonymizer_version = self.node_synonymizer_path.split('/')[-1].split('_v')[-1].replace('.sqlite', '')
        self.dtd_prob_path = database_downloads["dtd_prob"]
        self.dtd_prob_version = self.dtd_prob_path.split('/')[-1].split('_v')[-1].replace('.db', '')
        self.kg2c_sqlite_path = database_downloads["kg2c_sqlite"]
        self.kg2c_sqlite_version = self.kg2c_sqlite_path.split('/')[-1].split('_v')[-1].replace('.sqlite', '')
        self.kg2c_meta_kg_path = database_downloads["kg2c_meta_kg"]
        self.kg2c_meta_kg_version = self.kg2c_meta_kg_path.split('/')[-1].split('_v')[-1].replace('.json', '')
        self.fda_approved_drugs_path = database_downloads["fda_approved_drugs"]
        self.fda_approved_drugs_version = self.fda_approved_drugs_path.split('/')[-1].split('_v')[-1].replace('.pickle', '')
        self.autocomplete_path = database_downloads["autocomplete"]
        self.autocomplete_version = self.autocomplete_path.split('/')[-1].split('_v')[-1].replace('.sqlite', '')
        self.explainable_dtd_db_path = database_downloads["explainable_dtd_db"]
        self.explainable_dtd_db_version = self.explainable_dtd_db_path.split('/')[-1].split('_v')[-1].replace('.db', '')
        self.xcrg_embeddings_path = database_downloads["xcrg_embeddings"]
        self.xcrg_embeddings_version = self.xcrg_embeddings_path.split('/')[-1].split('_v')[-1].replace('.npz', '')
        self.xcrg_increase_model_path = database_downloads["xcrg_increase_model"]
        self.xcrg_increase_model_version = self.xcrg_embeddings_path.split('/')[-1].split('_v')[-1].replace('.pt', '')
        self.xcrg_decrease_model_path = database_downloads["xcrg_decrease_model"]
        self.xcrg_decrease_model_version = self.xcrg_embeddings_path.split('/')[-1].split('_v')[-1].replace('.pt', '')         

        # Set up mysql feedback
        self.mysql_feedback_host = self.config_secrets["mysql_feedback"]["host"]
        self.mysql_feedback_port = self.config_secrets["mysql_feedback"]["port"]
        self.mysql_feedback_username = self.config_secrets["mysql_feedback"]["username"]
        self.mysql_feedback_password = self.config_secrets["mysql_feedback"]["password"]

        # Set up correct Plover URL (since it's not registered in SmartAPI)
        plover_url_override_value = self._read_override_file(f"{file_dir}/plover_url_override.txt")
        if plover_url_override_value:
            self.plover_url = plover_url_override_value
        elif self.maturity in {"production", "prod"}:
            self.plover_url = self.config_dbs["plover"]["prod"]
        elif self.maturity in {"testing", "test"}:
            self.plover_url = self.config_dbs["plover"]["test"]
        else:  # Includes staging, development
            self.plover_url = self.config_dbs["plover"]["dev"]

        # Set KG2 url if an override was provided
        kg2_url_override_value = self._read_override_file(f"{file_dir}/kg2_url_override.txt")
        if kg2_url_override_value:
            self.rtx_kg2_url = kg2_url_override_value
        else:
            self.rtx_kg2_url = None

        # Default to KG2c neo4j
        self.neo4j_kg2 = "KG2c"

    @staticmethod
    def _read_override_file(file_path: str) -> Optional[str]:
        if os.path.exists(file_path):
            with open(file_path, 'r') as override_file:
                lines = override_file.readlines()
                if not lines or not lines[0]:
                    raise ValueError(f"{file_path} exists but does not contain anything! "
                                     f"It should be a single-line file containing the override value.")
                else:
                    return lines[0].strip()
        else:
            return None

    # ### Define attribute version
    @property
    def version(self) -> str:
        return self._version

    @version.setter
    def version(self, version: str):
        self._version = version

    @property
    def neo4j_kg2(self) -> str:
        return self._neo4j_kg2

    @neo4j_kg2.setter
    def neo4j_kg2(self, neo4j_kg2: str):
        self._neo4j_kg2 = neo4j_kg2

        if self.neo4j_kg2 not in self.config_dbs["neo4j"].keys():
            self.neo4j_bolt = None
            self.neo4j_database = None
            self.neo4j_username = None
            self.neo4j_password = None
        else:
            neo4j_instance = self.config_dbs["neo4j"][self.neo4j_kg2]
            self.neo4j_bolt = f"bolt://{neo4j_instance}:7687"
            self.neo4j_database = f"{neo4j_instance}:7474/db/data"
            self.neo4j_username = self.config_secrets["neo4j"][self.neo4j_kg2]["username"]
            self.neo4j_password = self.config_secrets["neo4j"][self.neo4j_kg2]["password"]

    @property
    def neo4j_bolt(self) -> str:
        return self._neo4j_bolt

    @neo4j_bolt.setter
    def neo4j_bolt(self, bolt: str):
        self._neo4j_bolt = bolt

    @property
    def neo4j_database(self) -> str:
        return self._neo4j_database

    @neo4j_database.setter
    def neo4j_database(self, database: str):
        self._neo4j_database = database

    @property
    def neo4j_username(self) -> str:
        return self._neo4j_username

    @neo4j_username.setter
    def neo4j_username(self, username: str):
        self._neo4j_username = username

    @property
    def neo4j_password(self) -> str:
        return self._neo4j_password

    @neo4j_password.setter
    def neo4j_password(self, password: str):
        self._neo4j_password = password

    @property
    def plover_url(self) -> str:
        return self._plover_url

    @plover_url.setter
    def plover_url(self, url: str):
        self._plover_url = url

    @property
    def mysql_feedback_host(self) -> str:
        return self._mysql_feedback_host

    @mysql_feedback_host.setter
    def mysql_feedback_host(self, host: str):
        self._mysql_feedback_host = host

    @property
    def mysql_feedback_port(self) -> str:
        return self._mysql_feedback_port

    @mysql_feedback_port.setter
    def mysql_feedback_port(self, port: str):
        self._mysql_feedback_port = port

    @property
    def mysql_feedback_username(self) -> str:
        return self._mysql_feedback_username

    @mysql_feedback_username.setter
    def mysql_feedback_username(self, username: str):
        self._mysql_feedback_username = username

    @property
    def mysql_feedback_password(self) -> str:
        return self._mysql_feedback_password

    @mysql_feedback_password.setter
    def mysql_feedback_password(self, password: str):
        self._mysql_feedback_password = password


def main():
    rtxConfig = RTXConfiguration()
    print("RTX Version string: " + rtxConfig.version)
    print("neo4j KG2 version: %s" % rtxConfig.neo4j_kg2)
    print("neo4j bolt: %s" % rtxConfig.neo4j_bolt)
    print("neo4j database: %s" % rtxConfig.neo4j_database)
    print("neo4j username: %s" % rtxConfig.neo4j_username)
    print("neo4j password: %s" % rtxConfig.neo4j_password)
    print("plover url: %s" % rtxConfig.plover_url)
    print("rtx-kg2 url: %s" % rtxConfig.rtx_kg2_url)
    print("mysql feedback host: %s" % rtxConfig.mysql_feedback_host)
    print("mysql feedback port: %s" % rtxConfig.mysql_feedback_port)
    print("mysql feedback username: %s" % rtxConfig.mysql_feedback_username)
    print("mysql feedback password: %s" % rtxConfig.mysql_feedback_password)
    print(f"maturity: {rtxConfig.maturity}")
    print(f"current branch: {rtxConfig.current_branch_name}")
    print(f"is_itrb_instance: {rtxConfig.is_itrb_instance}")


if __name__ == "__main__":
    main()


# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/asyncquery_controller.py

import connexion
import six

import os
import sys

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../../../../ARAX/ARAXQuery")
from ARAX_query import ARAXQuery


def asyncquery(request_body):  # noqa: E501
    """Initiate a query with a callback to receive the response

     # noqa: E501

    :param request_body: Query information to be submitted
    :type request_body: Dict[str, ]

    :rtype: AsyncQueryResponse
    """

    # Note that we never even get here if the request_body is not schema-valid JSON

    query = connexion.request.get_json()

    #### Record the remote IP address in the query for now so it is available downstream
    try:
        query['remote_address'] = connexion.request.headers['x-forwarded-for']
    except:
        query['remote_address'] = '???'

    araxq = ARAXQuery()

    envelope = araxq.query_return_message(query, mode='asynchronous')
    http_status = 200
    if hasattr(envelope, 'http_status'):
        http_status = envelope.http_status
    return(envelope,http_status)




# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/asyncquery_status_controller.py

import connexion
import six

from openapi_server.models.async_query_status_response import AsyncQueryStatusResponse  # noqa: E501
from openapi_server import util


def asyncquery_status(job_id):  # noqa: E501
    """Retrieve the current status of a previously submitted asyncquery given its job_id

     # noqa: E501

    :param job_id: Identifier of the job for status request
    :type job_id: str

    :rtype: AsyncQueryStatusResponse
    """
    return 'do some magic!'


# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/entity_controller.py

import connexion
import six

from openapi_server.models.entity_query import EntityQuery  # noqa: E501
from openapi_server import util

import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../../../../ARAX/NodeSynonymizer")
from node_synonymizer import NodeSynonymizer


def get_entity(q):  # noqa: E501
    """Obtain CURIE and synonym information about a search term

     # noqa: E501

    :param q: A string to search by (name, abbreviation, CURIE, etc.). The parameter may be repeated for multiple search strings.
    :type q: List[str]

    :rtype: object
    """
    synonymizer = NodeSynonymizer()
    response = synonymizer.get_normalizer_results(q)

    return response


def post_entity(body):  # noqa: E501
    """Obtain CURIE and synonym information about search terms

     # noqa: E501

    :param body: List of terms to get information about
    :type body: 

    :rtype: EntityQuery
    """

    synonymizer = NodeSynonymizer()
    response = synonymizer.get_normalizer_results(body)

    return response



# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/example_questions_controller.py

import connexion
import six

from openapi_server import util

from QuestionExamples import QuestionExamples


def example_questions():  # noqa: E501
    """Request a list of example questions that ARAX can answer

     # noqa: E501


    :rtype: List[object]
    """
    exampleQuestions = QuestionExamples()
    return(exampleQuestions.questions)


# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/meta_knowledge_graph_controller.py

import connexion
import six
import os
import sys

from openapi_server.models.meta_knowledge_graph import MetaKnowledgeGraph  # noqa: E501
from openapi_server import util

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../../../../ARAX/KnowledgeSources")
from knowledge_source_metadata import KnowledgeSourceMetadata


def meta_knowledge_graph(format_=None):  # noqa: E501
    """Meta knowledge graph representation of this TRAPI web service.

     # noqa: E501

    :param format: Provide meta_knowledge_graph information in a format other than the default. Default value is &#39;full&#39;. Also permitted is &#39;simple&#39;
    :type format: str

    :rtype: MetaKnowledgeGraph
    """
    ksm = KnowledgeSourceMetadata()
    return(ksm.get_meta_knowledge_graph(format_=format_))


# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/pubmed_mesh_ngd_controller.py

import connexion
import six

from openapi_server.models.mesh_ngd_response import MeshNgdResponse  # noqa: E501
from openapi_server import util

import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../../../../reasoningtool/kg-construction/")
from NormGoogleDistance import NormGoogleDistance


def pubmed_mesh_ngd(term1, term2):  # noqa: E501
    """Query to get the Normalized Google Distance between two MeSH terms based on co-occurrence in all PubMed article annotations

     # noqa: E501

    :param term1: First of two terms. Order not important.
    :type term1: str
    :param term2: Second of two terms. Order not important.
    :type term2: str

    :rtype: MeshNgdResponse
    """
    cwd = os.getcwd()
    os.chdir(os.path.dirname(os.path.abspath(__file__))+"/../../../../../reasoningtool/kg-construction")
    ngd = NormGoogleDistance()
    response = ngd.api_ngd(term1,term2)
    os.chdir(cwd)
    return(response)



# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/query_controller.py

import connexion, flask
import json
import os, sys, signal
import resource
import logging
import traceback
from typing import Iterable, Callable

rlimit_child_process_bytes = 34359738368  # 32 GiB

sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../../../../../ARAX/ARAXQuery")
import ARAX_query

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)) + "/../models")
import response


def child_receive_sigpipe(signal_number, frame):
    if signal_number == signal.SIGPIPE:
        logging.info("[query_controller]: child process detected a SIGPIPE; exiting python")
        os._exit(0)

def run_query_dict_in_child_process(query_dict: dict,
                                    query_runner: Callable) -> Iterable[str]:
    logging.debug("[query_controller]: Creating pipe and forking a child to handle the query")
    read_fd, write_fd = os.pipe()

    # always flush stdout and stderr before calling fork(); someone could have turned off auto-flushing and we don't want double-output
    sys.stderr.flush()
    sys.stdout.flush()

    pid = os.fork()

    if pid == 0: # I am the child process
        sys.stdout = open('/dev/null', 'w')         # parent and child process should not share the same stdout stream object
        sys.stdin = open('/dev/null', 'r')          # parent and child process should not share the same stdin stream object
        os.close(read_fd)                   # child doesn't read from the pipe, it writes to it 
        resource.setrlimit(resource.RLIMIT_AS, (rlimit_child_process_bytes, rlimit_child_process_bytes))  # set a virtual memory limit for the child process
        signal.signal(signal.SIGPIPE, child_receive_sigpipe) # get rid of signal handler so we don't double-print to the log on SIGPIPE error
        signal.signal(signal.SIGCHLD, signal.SIG_IGN) # disregard any SIGCHLD signal in the child process
        try:
            with os.fdopen(write_fd, "w") as write_fo:  # child process needs to get a stream object for the file descriptor `write_fd`
                json_string_generator = query_runner(query_dict)
                for json_string in json_string_generator:
                    write_fo.write(json_string)
                    write_fo.flush()
        except BaseException as e:
            print(f"Exception in query_controller.run_query_dict_in_child_process: {type(e)}\n{traceback.print_exc()}", file=sys.stderr)
            os._exit(1)
        os._exit(0)
    elif pid > 0: # I am the parent process
        os.close(write_fd)  # the parent does not write to the pipe, it reads from it
        logging.debug(f"[query_controller]: child process pid={pid}")
        read_fo = os.fdopen(read_fd, "r")
    else:
        logging.error("[query_controller]: fork() unsuccessful")
        assert False, "********** fork() unsuccessful; something went very wrong *********"
    return read_fo


def _run_query_and_return_json_generator_nonstream(query_dict: dict) -> Iterable[str]:
    envelope = ARAX_query.ARAXQuery().query_return_message(query_dict)
    envelope_dict = envelope.to_dict()
    if hasattr(envelope, 'http_status'):
        envelope_dict['http_status'] = envelope.http_status
    else:
        envelope_dict['http_status'] = 200
    return (json.dumps(envelope_dict), )


def _run_query_and_return_json_generator_stream(query_dict: dict) -> Iterable[str]:
    return ARAX_query.ARAXQuery().query_return_stream(query_dict)


def query(request_body):  # noqa: E501
    """Initiate a query and wait to receive a Response

     # noqa: E501

    :param request_body: Query information to be submitted
    :type request_body: Dict[str, ]

    :rtype: Response
    """

    # Note that we never even get here if the request_body is not schema-valid JSON

    query = connexion.request.get_json()  # :QUESTION: why don't we use `request_body`?

    #### Record the remote IP address in the query for now so it is available downstream
    try:
        query['remote_address'] = connexion.request.headers['x-forwarded-for']
    except:
        query['remote_address'] = '???'

    mime_type = 'application/json'

    if query.get('stream_progress', False):  # if stream_progress is specified and if it is True:

        fork_mode = True # :DEBUG: can turn this to False to disable fork-mode
        http_status = None
        mime_type = 'text/event-stream'

        if not fork_mode:
            json_generator = _run_query_and_return_json_generator_stream(query)
        else:
            json_generator = run_query_dict_in_child_process(query,
                                                             _run_query_and_return_json_generator_stream)

        resp_obj = flask.Response(json_generator, mimetype=mime_type)
    # Else perform the query and return the result
        http_status = None

    else:
        json_generator = run_query_dict_in_child_process(query,
                                                         _run_query_and_return_json_generator_nonstream)
        the_dict = json.loads(next(json_generator))
        http_status = the_dict.get('http_status', 200)
        resp_obj = response.Response.from_dict(the_dict)
        resp_obj.http_status = http_status

    return (resp_obj, http_status)


# :TESTING: vvvvvvvvvvvvvvvvvv
# if __name__ == "__main__":
#     signal.signal(signal.SIGPIPE, signal.SIG_IGN)
#     query_dict = {
#         "operations": {
#             "actions": [
#                 "add_qnode(ids=[CHEMBL.COMPOUND:CHEMBL112], key=n00)",
#                 "add_qnode(ids=[UniProtKB:P55000], key=n01)",
#                 "add_qedge(subject=n00, object=n01, key=e00)",
#                 "expand(edge_key=e00,kp=RTX-KG2)",
#                 "resultify()",
#                 "return(message=true, store=false)",
#             ]
#         }
#     }
#     for json_str in run_query_dict_in_child_process(query_dict, _run_query_and_return_json_generator_stream):
#         print(json_str)
# :TESTING: ^^^^^^^^^^^^^^^^^^


# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/response_controller.py

import connexion
import six

from openapi_server.models.response import Response  # noqa: E501
from openapi_server import util

import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../../../../ARAX/ResponseCache")
from response_cache import ResponseCache


def get_response(response_id):  # noqa: E501
    """Request a previously stored response from the server

     # noqa: E501

    :param response_id: Identifier of the response to return
    :type response_id: str

    :rtype: Response
    """

    response_cache = ResponseCache()
    envelope = response_cache.get_response(response_id)
    return envelope


def post_response(body):  # noqa: E501
    """Annotate a response

     # noqa: E501

    :param body: Object that provides annotation information
    :type body: 

    :rtype: object
    """
    response_cache = ResponseCache()
    response_cache.store_callback(body)
    return 'received!'



# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/security_controller_.py

from typing import List




# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/status_controller.py

import six
import os
import sys

from openapi_server import util
from ARAX_query_tracker import ARAXQueryTracker
from Expand.smartapi import SmartAPI


def get_status(last_n_hours=None, id_=None, terminate_pid=None, authorization=None):  # noqa: E501
    """Obtain status information about the endpoint

     # noqa: E501

    :param last_n_hours: Limit results to the past N hours
    :type last_n_hours: int
    :param id: Identifier of the log entry
    :type id: int
    :param terminate_pid: PID of an ongoing query to terminate
    :type terminate_pid: int
    :param authorization: Authorization string required for certain calls to status
    :type authorization: str

    :rtype: object
    """

    if authorization is not None and authorization == 'smartapi':
        smartapi = SmartAPI()
        return smartapi.get_trapi_endpoints()

    query_tracker = ARAXQueryTracker()
    if terminate_pid is not None:
        status = query_tracker.terminate_job(terminate_pid, authorization)
    else:
        status = query_tracker.get_status(last_n_hours=last_n_hours, id_=id_)
    return status


def get_logs(mode=None):  # noqa: E501
    """Get log information from the server

     # noqa: E501

    :param mode: Specify the log sending mode
    :type mode: string

    :rtype: string
    """

    query_tracker = ARAXQueryTracker()
    status = query_tracker.get_logs(mode=mode)
    return status


# code/UI/OpenAPI/python-flask-server/openapi_server/controllers/translate_controller.py

import connexion
import six

from openapi_server.models.query import Query  # noqa: E501
from openapi_server import util

#from ParseQuestion import ParseQuestion


def translate(request_body=None):  # noqa: E501
    """Translate natural language question into a standardized query

     # noqa: E501

    :param request_body: Question information to be translated
    :type request_body: Dict[str, ]

    :rtype: List[Query]
    """
    if connexion.request.is_json:
        question = connexion.request.get_json()
        #questionParser = ParseQuestion()
        #query = questionParser.format_response(question)
        #return(query)
        return( { "status": 501, "title": "Translate not implemented", "detail": "The Translate function used to work, but have been disabled", "type": "about:blank" }, 501 )
    else:
        return( { "status": 400, "title": "body content not JSON", "detail": "Required body content is not JSON", "type": "about:blank" }, 400 )


# code/UI/OpenAPI/python-flask-server/openapi_server/models/__init__.py

# coding: utf-8

# flake8: noqa
from __future__ import absolute_import
# import models into model package
from openapi_server.models.async_query import AsyncQuery
from openapi_server.models.attribute import Attribute
from openapi_server.models.attribute_constraint import AttributeConstraint
from openapi_server.models.edge import Edge
from openapi_server.models.edge_binding import EdgeBinding
from openapi_server.models.entity_query import EntityQuery
from openapi_server.models.knowledge_graph import KnowledgeGraph
from openapi_server.models.log_entry import LogEntry
from openapi_server.models.log_level import LogLevel
from openapi_server.models.mesh_ngd_response import MeshNgdResponse
from openapi_server.models.message import Message
from openapi_server.models.meta_attribute import MetaAttribute
from openapi_server.models.meta_edge import MetaEdge
from openapi_server.models.meta_knowledge_graph import MetaKnowledgeGraph
from openapi_server.models.meta_node import MetaNode
from openapi_server.models.node import Node
from openapi_server.models.node_binding import NodeBinding
from openapi_server.models.operation_annotate import OperationAnnotate
from openapi_server.models.operation_annotate_edges import OperationAnnotateEdges
from openapi_server.models.operation_annotate_edges_parameters import OperationAnnotateEdgesParameters
from openapi_server.models.operation_annotate_nodes import OperationAnnotateNodes
from openapi_server.models.operation_annotate_nodes_parameters import OperationAnnotateNodesParameters
from openapi_server.models.operation_bind import OperationBind
from openapi_server.models.operation_complete_results import OperationCompleteResults
from openapi_server.models.operation_enrich_results import OperationEnrichResults
from openapi_server.models.operation_enrich_results_parameters import OperationEnrichResultsParameters
from openapi_server.models.operation_fill import OperationFill
from openapi_server.models.operation_filter_kgraph import OperationFilterKgraph
from openapi_server.models.operation_filter_kgraph_continuous_kedge_attribute import OperationFilterKgraphContinuousKedgeAttribute
from openapi_server.models.operation_filter_kgraph_continuous_kedge_attribute_parameters import OperationFilterKgraphContinuousKedgeAttributeParameters
from openapi_server.models.operation_filter_kgraph_discrete_kedge_attribute import OperationFilterKgraphDiscreteKedgeAttribute
from openapi_server.models.operation_filter_kgraph_discrete_kedge_attribute_parameters import OperationFilterKgraphDiscreteKedgeAttributeParameters
from openapi_server.models.operation_filter_kgraph_discrete_knode_attribute import OperationFilterKgraphDiscreteKnodeAttribute
from openapi_server.models.operation_filter_kgraph_discrete_knode_attribute_parameters import OperationFilterKgraphDiscreteKnodeAttributeParameters
from openapi_server.models.operation_filter_kgraph_orphans import OperationFilterKgraphOrphans
from openapi_server.models.operation_filter_kgraph_percentile import OperationFilterKgraphPercentile
from openapi_server.models.operation_filter_kgraph_percentile_parameters import OperationFilterKgraphPercentileParameters
from openapi_server.models.operation_filter_kgraph_std_dev import OperationFilterKgraphStdDev
from openapi_server.models.operation_filter_kgraph_std_dev_parameters import OperationFilterKgraphStdDevParameters
from openapi_server.models.operation_filter_kgraph_top_n import OperationFilterKgraphTopN
from openapi_server.models.operation_filter_kgraph_top_n_parameters import OperationFilterKgraphTopNParameters
from openapi_server.models.operation_filter_results import OperationFilterResults
from openapi_server.models.operation_filter_results_top_n import OperationFilterResultsTopN
from openapi_server.models.operation_filter_results_top_n_parameters import OperationFilterResultsTopNParameters
from openapi_server.models.operation_lookup import OperationLookup
from openapi_server.models.operation_overlay import OperationOverlay
from openapi_server.models.operation_overlay_compute_jaccard import OperationOverlayComputeJaccard
from openapi_server.models.operation_overlay_compute_jaccard_parameters import OperationOverlayComputeJaccardParameters
from openapi_server.models.operation_overlay_compute_ngd import OperationOverlayComputeNgd
from openapi_server.models.operation_overlay_compute_ngd_parameters import OperationOverlayComputeNgdParameters
from openapi_server.models.operation_overlay_connect_knodes import OperationOverlayConnectKnodes
from openapi_server.models.operation_overlay_fisher_exact_test import OperationOverlayFisherExactTest
from openapi_server.models.operation_overlay_fisher_exact_test_parameters import OperationOverlayFisherExactTestParameters
from openapi_server.models.operation_restate import OperationRestate
from openapi_server.models.operation_score import OperationScore
from openapi_server.models.operation_sort_results import OperationSortResults
from openapi_server.models.operation_sort_results_edge_attribute import OperationSortResultsEdgeAttribute
from openapi_server.models.operation_sort_results_edge_attribute_parameters import OperationSortResultsEdgeAttributeParameters
from openapi_server.models.operation_sort_results_node_attribute import OperationSortResultsNodeAttribute
from openapi_server.models.operation_sort_results_node_attribute_parameters import OperationSortResultsNodeAttributeParameters
from openapi_server.models.operation_sort_results_score import OperationSortResultsScore
from openapi_server.models.operation_sort_results_score_parameters import OperationSortResultsScoreParameters
from openapi_server.models.operations import Operations
from openapi_server.models.q_edge import QEdge
from openapi_server.models.q_node import QNode
from openapi_server.models.qualifier import Qualifier
from openapi_server.models.qualifier_constraint import QualifierConstraint
from openapi_server.models.query import Query
from openapi_server.models.query_graph import QueryGraph
from openapi_server.models.question import Question
from openapi_server.models.response import Response
from openapi_server.models.result import Result


# code/UI/OpenAPI/python-flask-server/openapi_server/models/analysis.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server.models.edge_binding import EdgeBinding
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501
from openapi_server.models.edge_binding import EdgeBinding  # noqa: E501

class Analysis(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, reasoner_id=None, score=None, edge_bindings=None, support_graphs=None, scoring_method=None, attributes=None):  # noqa: E501
        """Analysis - a model defined in OpenAPI

        :param reasoner_id: The reasoner_id of this Analysis.  # noqa: E501
        :type reasoner_id: str
        :param score: The score of this Analysis.  # noqa: E501
        :type score: float
        :param edge_bindings: The edge_bindings of this Analysis.  # noqa: E501
        :type edge_bindings: Dict[str, List[EdgeBinding]]
        :param support_graphs: The support_graphs of this Analysis.  # noqa: E501
        :type support_graphs: List[str]
        :param scoring_method: The scoring_method of this Analysis.  # noqa: E501
        :type scoring_method: str
        :param attributes: The attributes of this Analysis.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'reasoner_id': str,
            'score': float,
            'edge_bindings': Dict[str, List[EdgeBinding]],
            'support_graphs': List[str],
            'scoring_method': str,
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'reasoner_id': 'reasoner_id',
            'score': 'score',
            'edge_bindings': 'edge_bindings',
            'support_graphs': 'support_graphs',
            'scoring_method': 'scoring_method',
            'attributes': 'attributes'
        }

        self._reasoner_id = reasoner_id
        self._score = score
        self._edge_bindings = edge_bindings
        self._support_graphs = support_graphs
        self._scoring_method = scoring_method
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'Analysis':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Analysis of this Analysis.  # noqa: E501
        :rtype: Analysis
        """
        return util.deserialize_model(dikt, cls)

    @property
    def reasoner_id(self):
        """Gets the reasoner_id of this Analysis.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The reasoner_id of this Analysis.
        :rtype: str
        """
        return self._reasoner_id

    @reasoner_id.setter
    def reasoner_id(self, reasoner_id):
        """Sets the reasoner_id of this Analysis.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param reasoner_id: The reasoner_id of this Analysis.
        :type reasoner_id: str
        """
        if reasoner_id is None:
            raise ValueError("Invalid value for `reasoner_id`, must not be `None`")  # noqa: E501

        self._reasoner_id = reasoner_id

    @property
    def score(self):
        """Gets the score of this Analysis.

        A numerical score associated with this result indicating the relevance or confidence of this result relative to others in the returned set. Higher MUST be better.  # noqa: E501

        :return: The score of this Analysis.
        :rtype: float
        """
        return self._score

    @score.setter
    def score(self, score):
        """Sets the score of this Analysis.

        A numerical score associated with this result indicating the relevance or confidence of this result relative to others in the returned set. Higher MUST be better.  # noqa: E501

        :param score: The score of this Analysis.
        :type score: float
        """

        self._score = score

    @property
    def edge_bindings(self):
        """Gets the edge_bindings of this Analysis.

        The dictionary of input Query Graph to Knowledge Graph edge bindings where the dictionary keys are the key identifiers of the Query Graph edges and the associated values of those keys are instances of EdgeBinding schema type (see below). This value is an array of EdgeBindings since a given query edge may resolve to multiple Knowledge Graph Edges.  # noqa: E501

        :return: The edge_bindings of this Analysis.
        :rtype: Dict[str, List[EdgeBinding]]
        """
        return self._edge_bindings

    @edge_bindings.setter
    def edge_bindings(self, edge_bindings):
        """Sets the edge_bindings of this Analysis.

        The dictionary of input Query Graph to Knowledge Graph edge bindings where the dictionary keys are the key identifiers of the Query Graph edges and the associated values of those keys are instances of EdgeBinding schema type (see below). This value is an array of EdgeBindings since a given query edge may resolve to multiple Knowledge Graph Edges.  # noqa: E501

        :param edge_bindings: The edge_bindings of this Analysis.
        :type edge_bindings: Dict[str, List[EdgeBinding]]
        """
        if edge_bindings is None:
            raise ValueError("Invalid value for `edge_bindings`, must not be `None`")  # noqa: E501

        self._edge_bindings = edge_bindings

    @property
    def support_graphs(self):
        """Gets the support_graphs of this Analysis.

        This is a list of references to Auxiliary Graph instances that supported the analysis of a Result as performed by the reasoning service. Each item in the list is the key of a single Auxiliary Graph.  # noqa: E501

        :return: The support_graphs of this Analysis.
        :rtype: List[str]
        """
        return self._support_graphs

    @support_graphs.setter
    def support_graphs(self, support_graphs):
        """Sets the support_graphs of this Analysis.

        This is a list of references to Auxiliary Graph instances that supported the analysis of a Result as performed by the reasoning service. Each item in the list is the key of a single Auxiliary Graph.  # noqa: E501

        :param support_graphs: The support_graphs of this Analysis.
        :type support_graphs: List[str]
        """

        self._support_graphs = support_graphs

    @property
    def scoring_method(self):
        """Gets the scoring_method of this Analysis.

        An identifier and link to an explanation for the method used to generate the score  # noqa: E501

        :return: The scoring_method of this Analysis.
        :rtype: str
        """
        return self._scoring_method

    @scoring_method.setter
    def scoring_method(self, scoring_method):
        """Sets the scoring_method of this Analysis.

        An identifier and link to an explanation for the method used to generate the score  # noqa: E501

        :param scoring_method: The scoring_method of this Analysis.
        :type scoring_method: str
        """

        self._scoring_method = scoring_method

    @property
    def attributes(self):
        """Gets the attributes of this Analysis.

        The attributes of this particular Analysis.  # noqa: E501

        :return: The attributes of this Analysis.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this Analysis.

        The attributes of this particular Analysis.  # noqa: E501

        :param attributes: The attributes of this Analysis.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/openapi_server/models/any_type.py

class AnyType():
    """NOTE: This class should be auto generated by OpenAPI Generator (https://openapi-generator.tech).
        But it is mysteriously not, so it was hand-coded by Eric Deutsch
        It is a stub type that stands in for any OpenAPI type
    """

    def __init__(self):

        # If openapi_types is None, then util.deserialize_model just returns the data as is,
        # which is what we want for AnyType
        self.openapi_types = None



# code/UI/OpenAPI/python-flask-server/openapi_server/models/async_query.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_level import LogLevel
from openapi_server.models.message import Message
from openapi_server.models.any_type import AnyType
import re
from openapi_server import util

from openapi_server.models.log_level import LogLevel  # noqa: E501
from openapi_server.models.message import Message  # noqa: E501
from openapi_server.models.any_type import AnyType  # noqa: E501
import re  # noqa: E501

class AsyncQuery(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, callback=None, message=None, log_level=None, workflow=None, submitter=None):  # noqa: E501
        """AsyncQuery - a model defined in OpenAPI

        :param callback: The callback of this AsyncQuery.  # noqa: E501
        :type callback: str
        :param message: The message of this AsyncQuery.  # noqa: E501
        :type message: Message
        :param log_level: The log_level of this AsyncQuery.  # noqa: E501
        :type log_level: LogLevel
        :param workflow: The workflow of this AsyncQuery.  # noqa: E501
        :type workflow: List[AnyType]
        :param submitter: The submitter of this AsyncQuery.  # noqa: E501
        :type submitter: str
        """
        self.openapi_types = {
            'callback': str,
            'message': Message,
            'log_level': LogLevel,
            'workflow': List[AnyType],
            'submitter': str
        }

        self.attribute_map = {
            'callback': 'callback',
            'message': 'message',
            'log_level': 'log_level',
            'workflow': 'workflow',
            'submitter': 'submitter'
        }

        self._callback = callback
        self._message = message
        self._log_level = log_level
        self._workflow = workflow
        self._submitter = submitter

    @classmethod
    def from_dict(cls, dikt) -> 'AsyncQuery':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The AsyncQuery of this AsyncQuery.  # noqa: E501
        :rtype: AsyncQuery
        """
        return util.deserialize_model(dikt, cls)

    @property
    def callback(self):
        """Gets the callback of this AsyncQuery.

        Upon completion, this server will send a POST request to the callback URL with `Content-Type: application/json` header and request body containing a JSON-encoded `Response` object. The server MAY POST `Response` objects before work is fully complete to provide interim results with a Response.status value of 'Running'. If a POST operation to the callback URL does not succeed, the server SHOULD retry the POST at least once.  # noqa: E501

        :return: The callback of this AsyncQuery.
        :rtype: str
        """
        return self._callback

    @callback.setter
    def callback(self, callback):
        """Sets the callback of this AsyncQuery.

        Upon completion, this server will send a POST request to the callback URL with `Content-Type: application/json` header and request body containing a JSON-encoded `Response` object. The server MAY POST `Response` objects before work is fully complete to provide interim results with a Response.status value of 'Running'. If a POST operation to the callback URL does not succeed, the server SHOULD retry the POST at least once.  # noqa: E501

        :param callback: The callback of this AsyncQuery.
        :type callback: str
        """
        if callback is None:
            raise ValueError("Invalid value for `callback`, must not be `None`")  # noqa: E501
        if callback is not None and not re.search(r'^https?:\/\/', callback):  # noqa: E501
            raise ValueError("Invalid value for `callback`, must be a follow pattern or equal to `/^https?:\/\//`")  # noqa: E501

        self._callback = callback

    @property
    def message(self):
        """Gets the message of this AsyncQuery.


        :return: The message of this AsyncQuery.
        :rtype: Message
        """
        return self._message

    @message.setter
    def message(self, message):
        """Sets the message of this AsyncQuery.


        :param message: The message of this AsyncQuery.
        :type message: Message
        """
        if message is None:
            raise ValueError("Invalid value for `message`, must not be `None`")  # noqa: E501

        self._message = message

    @property
    def log_level(self):
        """Gets the log_level of this AsyncQuery.

        The least critical level of logs to return  # noqa: E501

        :return: The log_level of this AsyncQuery.
        :rtype: LogLevel
        """
        return self._log_level

    @log_level.setter
    def log_level(self, log_level):
        """Sets the log_level of this AsyncQuery.

        The least critical level of logs to return  # noqa: E501

        :param log_level: The log_level of this AsyncQuery.
        :type log_level: LogLevel
        """

        self._log_level = log_level

    @property
    def workflow(self):
        """Gets the workflow of this AsyncQuery.

        List of workflow steps to be executed.  # noqa: E501

        :return: The workflow of this AsyncQuery.
        :rtype: List[AnyType]
        """
        return self._workflow

    @workflow.setter
    def workflow(self, workflow):
        """Sets the workflow of this AsyncQuery.

        List of workflow steps to be executed.  # noqa: E501

        :param workflow: The workflow of this AsyncQuery.
        :type workflow: List[AnyType]
        """

        self._workflow = workflow

    @property
    def submitter(self):
        """Gets the submitter of this AsyncQuery.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :return: The submitter of this AsyncQuery.
        :rtype: str
        """
        return self._submitter

    @submitter.setter
    def submitter(self, submitter):
        """Sets the submitter of this AsyncQuery.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :param submitter: The submitter of this AsyncQuery.
        :type submitter: str
        """

        self._submitter = submitter


# code/UI/OpenAPI/python-flask-server/openapi_server/models/async_query_response.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class AsyncQueryResponse(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, status=None, description=None, job_id=None):  # noqa: E501
        """AsyncQueryResponse - a model defined in OpenAPI

        :param status: The status of this AsyncQueryResponse.  # noqa: E501
        :type status: str
        :param description: The description of this AsyncQueryResponse.  # noqa: E501
        :type description: str
        :param job_id: The job_id of this AsyncQueryResponse.  # noqa: E501
        :type job_id: str
        """
        self.openapi_types = {
            'status': str,
            'description': str,
            'job_id': str
        }

        self.attribute_map = {
            'status': 'status',
            'description': 'description',
            'job_id': 'job_id'
        }

        self._status = status
        self._description = description
        self._job_id = job_id

    @classmethod
    def from_dict(cls, dikt) -> 'AsyncQueryResponse':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The AsyncQueryResponse of this AsyncQueryResponse.  # noqa: E501
        :rtype: AsyncQueryResponse
        """
        return util.deserialize_model(dikt, cls)

    @property
    def status(self):
        """Gets the status of this AsyncQueryResponse.

        One of a standardized set of short codes: e.g. Accepted, QueryNotTraversable, KPsNotAvailable  # noqa: E501

        :return: The status of this AsyncQueryResponse.
        :rtype: str
        """
        return self._status

    @status.setter
    def status(self, status):
        """Sets the status of this AsyncQueryResponse.

        One of a standardized set of short codes: e.g. Accepted, QueryNotTraversable, KPsNotAvailable  # noqa: E501

        :param status: The status of this AsyncQueryResponse.
        :type status: str
        """

        self._status = status

    @property
    def description(self):
        """Gets the description of this AsyncQueryResponse.

        A brief human-readable description of the result of the async_query submission.  # noqa: E501

        :return: The description of this AsyncQueryResponse.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this AsyncQueryResponse.

        A brief human-readable description of the result of the async_query submission.  # noqa: E501

        :param description: The description of this AsyncQueryResponse.
        :type description: str
        """

        self._description = description

    @property
    def job_id(self):
        """Gets the job_id of this AsyncQueryResponse.

        An identifier for the submitted job that can be used with /async_query_status to receive an update on the status of the job.  # noqa: E501

        :return: The job_id of this AsyncQueryResponse.
        :rtype: str
        """
        return self._job_id

    @job_id.setter
    def job_id(self, job_id):
        """Sets the job_id of this AsyncQueryResponse.

        An identifier for the submitted job that can be used with /async_query_status to receive an update on the status of the job.  # noqa: E501

        :param job_id: The job_id of this AsyncQueryResponse.
        :type job_id: str
        """
        if job_id is None:
            raise ValueError("Invalid value for `job_id`, must not be `None`")  # noqa: E501

        self._job_id = job_id


# code/UI/OpenAPI/python-flask-server/openapi_server/models/async_query_status_response.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_entry import LogEntry
from openapi_server import util

from openapi_server.models.log_entry import LogEntry  # noqa: E501

class AsyncQueryStatusResponse(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, status=None, description=None, logs=None, response_url=None):  # noqa: E501
        """AsyncQueryStatusResponse - a model defined in OpenAPI

        :param status: The status of this AsyncQueryStatusResponse.  # noqa: E501
        :type status: str
        :param description: The description of this AsyncQueryStatusResponse.  # noqa: E501
        :type description: str
        :param logs: The logs of this AsyncQueryStatusResponse.  # noqa: E501
        :type logs: List[LogEntry]
        :param response_url: The response_url of this AsyncQueryStatusResponse.  # noqa: E501
        :type response_url: str
        """
        self.openapi_types = {
            'status': str,
            'description': str,
            'logs': List[LogEntry],
            'response_url': str
        }

        self.attribute_map = {
            'status': 'status',
            'description': 'description',
            'logs': 'logs',
            'response_url': 'response_url'
        }

        self._status = status
        self._description = description
        self._logs = logs
        self._response_url = response_url

    @classmethod
    def from_dict(cls, dikt) -> 'AsyncQueryStatusResponse':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The AsyncQueryStatusResponse of this AsyncQueryStatusResponse.  # noqa: E501
        :rtype: AsyncQueryStatusResponse
        """
        return util.deserialize_model(dikt, cls)

    @property
    def status(self):
        """Gets the status of this AsyncQueryStatusResponse.

        One of a standardized set of short codes: Queued, Running, Completed, Failed  # noqa: E501

        :return: The status of this AsyncQueryStatusResponse.
        :rtype: str
        """
        return self._status

    @status.setter
    def status(self, status):
        """Sets the status of this AsyncQueryStatusResponse.

        One of a standardized set of short codes: Queued, Running, Completed, Failed  # noqa: E501

        :param status: The status of this AsyncQueryStatusResponse.
        :type status: str
        """
        if status is None:
            raise ValueError("Invalid value for `status`, must not be `None`")  # noqa: E501

        self._status = status

    @property
    def description(self):
        """Gets the description of this AsyncQueryStatusResponse.

        A brief human-readable description of the current state or summary of the problem if the status is Failed.  # noqa: E501

        :return: The description of this AsyncQueryStatusResponse.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this AsyncQueryStatusResponse.

        A brief human-readable description of the current state or summary of the problem if the status is Failed.  # noqa: E501

        :param description: The description of this AsyncQueryStatusResponse.
        :type description: str
        """
        if description is None:
            raise ValueError("Invalid value for `description`, must not be `None`")  # noqa: E501

        self._description = description

    @property
    def logs(self):
        """Gets the logs of this AsyncQueryStatusResponse.

        A list of LogEntry items, containing errors, warnings, debugging information, etc. List items MUST be in chronological order with earliest first. The most recent entry should be last. Its timestamp will be compared against the current time to see if there is still activity.  # noqa: E501

        :return: The logs of this AsyncQueryStatusResponse.
        :rtype: List[LogEntry]
        """
        return self._logs

    @logs.setter
    def logs(self, logs):
        """Sets the logs of this AsyncQueryStatusResponse.

        A list of LogEntry items, containing errors, warnings, debugging information, etc. List items MUST be in chronological order with earliest first. The most recent entry should be last. Its timestamp will be compared against the current time to see if there is still activity.  # noqa: E501

        :param logs: The logs of this AsyncQueryStatusResponse.
        :type logs: List[LogEntry]
        """
        if logs is None:
            raise ValueError("Invalid value for `logs`, must not be `None`")  # noqa: E501

        self._logs = logs

    @property
    def response_url(self):
        """Gets the response_url of this AsyncQueryStatusResponse.

        Optional URL that can be queried to restrieve the full TRAPI Response.  # noqa: E501

        :return: The response_url of this AsyncQueryStatusResponse.
        :rtype: str
        """
        return self._response_url

    @response_url.setter
    def response_url(self, response_url):
        """Sets the response_url of this AsyncQueryStatusResponse.

        Optional URL that can be queried to restrieve the full TRAPI Response.  # noqa: E501

        :param response_url: The response_url of this AsyncQueryStatusResponse.
        :type response_url: str
        """

        self._response_url = response_url


# code/UI/OpenAPI/python-flask-server/openapi_server/models/attribute.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class Attribute(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, attribute_type_id=None, original_attribute_name=None, value=None, value_type_id=None, attribute_source=None, value_url=None, description=None, attributes=None):  # noqa: E501
        """Attribute - a model defined in OpenAPI

        :param attribute_type_id: The attribute_type_id of this Attribute.  # noqa: E501
        :type attribute_type_id: str
        :param original_attribute_name: The original_attribute_name of this Attribute.  # noqa: E501
        :type original_attribute_name: str
        :param value: The value of this Attribute.  # noqa: E501
        :type value: object
        :param value_type_id: The value_type_id of this Attribute.  # noqa: E501
        :type value_type_id: str
        :param attribute_source: The attribute_source of this Attribute.  # noqa: E501
        :type attribute_source: str
        :param value_url: The value_url of this Attribute.  # noqa: E501
        :type value_url: str
        :param description: The description of this Attribute.  # noqa: E501
        :type description: str
        :param attributes: The attributes of this Attribute.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'attribute_type_id': str,
            'original_attribute_name': str,
            'value': object,
            'value_type_id': str,
            'attribute_source': str,
            'value_url': str,
            'description': str,
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'attribute_type_id': 'attribute_type_id',
            'original_attribute_name': 'original_attribute_name',
            'value': 'value',
            'value_type_id': 'value_type_id',
            'attribute_source': 'attribute_source',
            'value_url': 'value_url',
            'description': 'description',
            'attributes': 'attributes'
        }

        self._attribute_type_id = attribute_type_id
        self._original_attribute_name = original_attribute_name
        self._value = value
        self._value_type_id = value_type_id
        self._attribute_source = attribute_source
        self._value_url = value_url
        self._description = description
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'Attribute':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Attribute of this Attribute.  # noqa: E501
        :rtype: Attribute
        """
        return util.deserialize_model(dikt, cls)

    @property
    def attribute_type_id(self):
        """Gets the attribute_type_id of this Attribute.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The attribute_type_id of this Attribute.
        :rtype: str
        """
        return self._attribute_type_id

    @attribute_type_id.setter
    def attribute_type_id(self, attribute_type_id):
        """Sets the attribute_type_id of this Attribute.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param attribute_type_id: The attribute_type_id of this Attribute.
        :type attribute_type_id: str
        """
        if attribute_type_id is None:
            raise ValueError("Invalid value for `attribute_type_id`, must not be `None`")  # noqa: E501

        self._attribute_type_id = attribute_type_id

    @property
    def original_attribute_name(self):
        """Gets the original_attribute_name of this Attribute.

        The term used by the original source of an attribute to describe the meaning or significance of the value it captures. This may be a column name in a source tsv file, or a key in a source json document for the field in the data that held the attribute's value. Capturing this information  where possible lets us preserve what the original source said. Note that the data type is string' but the contents of the field could also be a CURIE of a third party ontology term.  # noqa: E501

        :return: The original_attribute_name of this Attribute.
        :rtype: str
        """
        return self._original_attribute_name

    @original_attribute_name.setter
    def original_attribute_name(self, original_attribute_name):
        """Sets the original_attribute_name of this Attribute.

        The term used by the original source of an attribute to describe the meaning or significance of the value it captures. This may be a column name in a source tsv file, or a key in a source json document for the field in the data that held the attribute's value. Capturing this information  where possible lets us preserve what the original source said. Note that the data type is string' but the contents of the field could also be a CURIE of a third party ontology term.  # noqa: E501

        :param original_attribute_name: The original_attribute_name of this Attribute.
        :type original_attribute_name: str
        """

        self._original_attribute_name = original_attribute_name

    @property
    def value(self):
        """Gets the value of this Attribute.

        Value of the attribute. May be any data type, including a list.  # noqa: E501

        :return: The value of this Attribute.
        :rtype: object
        """
        return self._value

    @value.setter
    def value(self, value):
        """Sets the value of this Attribute.

        Value of the attribute. May be any data type, including a list.  # noqa: E501

        :param value: The value of this Attribute.
        :type value: object
        """
        if value is None:
            raise ValueError("Invalid value for `value`, must not be `None`")  # noqa: E501

        self._value = value

    @property
    def value_type_id(self):
        """Gets the value_type_id of this Attribute.

        CURIE describing the semantic type of an  attribute's value. Use a Biolink class if possible, otherwise a term from an external ontology. If a suitable CURIE/identifier does not exist, enter a descriptive phrase here and submit the new type for consideration by the appropriate authority.  # noqa: E501

        :return: The value_type_id of this Attribute.
        :rtype: str
        """
        return self._value_type_id

    @value_type_id.setter
    def value_type_id(self, value_type_id):
        """Sets the value_type_id of this Attribute.

        CURIE describing the semantic type of an  attribute's value. Use a Biolink class if possible, otherwise a term from an external ontology. If a suitable CURIE/identifier does not exist, enter a descriptive phrase here and submit the new type for consideration by the appropriate authority.  # noqa: E501

        :param value_type_id: The value_type_id of this Attribute.
        :type value_type_id: str
        """

        self._value_type_id = value_type_id

    @property
    def attribute_source(self):
        """Gets the attribute_source of this Attribute.

        The source of the core assertion made by the key-value pair of an attribute object. Use a CURIE or namespace designator for this resource where possible.  # noqa: E501

        :return: The attribute_source of this Attribute.
        :rtype: str
        """
        return self._attribute_source

    @attribute_source.setter
    def attribute_source(self, attribute_source):
        """Sets the attribute_source of this Attribute.

        The source of the core assertion made by the key-value pair of an attribute object. Use a CURIE or namespace designator for this resource where possible.  # noqa: E501

        :param attribute_source: The attribute_source of this Attribute.
        :type attribute_source: str
        """

        self._attribute_source = attribute_source

    @property
    def value_url(self):
        """Gets the value_url of this Attribute.

        Human-consumable URL linking to a web document that provides additional information about an  attribute's value (not the node or the edge fom which it hangs).  # noqa: E501

        :return: The value_url of this Attribute.
        :rtype: str
        """
        return self._value_url

    @value_url.setter
    def value_url(self, value_url):
        """Sets the value_url of this Attribute.

        Human-consumable URL linking to a web document that provides additional information about an  attribute's value (not the node or the edge fom which it hangs).  # noqa: E501

        :param value_url: The value_url of this Attribute.
        :type value_url: str
        """

        self._value_url = value_url

    @property
    def description(self):
        """Gets the description of this Attribute.

        Human-readable description for the attribute and its value.  # noqa: E501

        :return: The description of this Attribute.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this Attribute.

        Human-readable description for the attribute and its value.  # noqa: E501

        :param description: The description of this Attribute.
        :type description: str
        """

        self._description = description

    @property
    def attributes(self):
        """Gets the attributes of this Attribute.

        A list of attributes providing further information about the parent attribute (for example to provide provenance information about the parent attribute).  # noqa: E501

        :return: The attributes of this Attribute.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this Attribute.

        A list of attributes providing further information about the parent attribute (for example to provide provenance information about the parent attribute).  # noqa: E501

        :param attributes: The attributes of this Attribute.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/openapi_server/models/auxiliary_graph.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501

class AuxiliaryGraph(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, edges=None, attributes=None):  # noqa: E501
        """AuxiliaryGraph - a model defined in OpenAPI

        :param edges: The edges of this AuxiliaryGraph.  # noqa: E501
        :type edges: List[str]
        :param attributes: The attributes of this AuxiliaryGraph.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'edges': List[str],
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'edges': 'edges',
            'attributes': 'attributes'
        }

        self._edges = edges
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'AuxiliaryGraph':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The AuxiliaryGraph of this AuxiliaryGraph.  # noqa: E501
        :rtype: AuxiliaryGraph
        """
        return util.deserialize_model(dikt, cls)

    @property
    def edges(self):
        """Gets the edges of this AuxiliaryGraph.

        List of edges that form the Auxiliary Graph. Each item is a reference to a single Knowledge Graph edge  # noqa: E501

        :return: The edges of this AuxiliaryGraph.
        :rtype: List[str]
        """
        return self._edges

    @edges.setter
    def edges(self, edges):
        """Sets the edges of this AuxiliaryGraph.

        List of edges that form the Auxiliary Graph. Each item is a reference to a single Knowledge Graph edge  # noqa: E501

        :param edges: The edges of this AuxiliaryGraph.
        :type edges: List[str]
        """
        if edges is None:
            raise ValueError("Invalid value for `edges`, must not be `None`")  # noqa: E501
        if edges is not None and len(edges) < 1:
            raise ValueError("Invalid value for `edges`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._edges = edges

    @property
    def attributes(self):
        """Gets the attributes of this AuxiliaryGraph.

        Attributes of the Auxiliary Graph  # noqa: E501

        :return: The attributes of this AuxiliaryGraph.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this AuxiliaryGraph.

        Attributes of the Auxiliary Graph  # noqa: E501

        :param attributes: The attributes of this AuxiliaryGraph.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/openapi_server/models/base_model_.py

import pprint

import six
import typing

from openapi_server import util

T = typing.TypeVar('T')


class Model(object):
    # openapiTypes: The key is attribute name and the
    # value is attribute type.
    openapi_types = {}

    # attributeMap: The key is attribute name and the
    # value is json key in definition.
    attribute_map = {}

    @classmethod
    def from_dict(cls: typing.Type[T], dikt) -> T:
        """Returns the dict as a model"""
        return util.deserialize_model(dikt, cls)

    def to_dict(self):
        """Returns the model properties as a dict

        :rtype: dict
        """
        result = {}

        for attr, _ in six.iteritems(self.openapi_types):
            value = getattr(self, attr)
            if isinstance(value, list):
                result[attr] = list(map(
                    lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
                    value
                ))
            elif hasattr(value, "to_dict"):
                result[attr] = value.to_dict()
            elif isinstance(value, dict):

                #### This only can handle one level of lists or dicts between objects
                #result[attr] = dict(map(
                #    lambda item: (item[0], item[1].to_dict())
                #    if hasattr(item[1], "to_dict") else item,
                #    value.items()
                #))

                #### This is a little fancier in that it can handle two levels, a dict and then
                #### another dict or list between objects. Not the ultimate solution but
                #### perhaps adequate for now?
                result_dict = {}
                for dict_key, dict_value in value.items():
                    if isinstance(dict_value, list):
                        result_dict[dict_key] = list(map(
                            lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
                            dict_value
                        ))
                    elif isinstance(dict_value, dict):
                        result_dict[dict_key] = dict(map(
                            lambda dict_value_item: (dict_value_item[0], dict_value_item[1].to_dict())
                            if hasattr(dict_value_item[1], "to_dict") else dict_value_item,
                            dict_value.items()
                        ))
                    elif hasattr(dict_value, "to_dict"):
                        result_dict[dict_key] = dict_value.to_dict()
                    else:
                        result_dict[dict_key] = dict_value
                result[attr] = result_dict

            else:
                if attr == '_not':
                    attr = 'not'
                result[attr] = value

        return result

    def to_str(self):
        """Returns the string representation of the model

        :rtype: str
        """
        return pprint.pformat(self.to_dict())

    def __repr__(self):
        """For `print` and `pprint`"""
        return self.to_str()

    def __eq__(self, other):
        """Returns true if both objects are equal"""
        return self.__dict__ == other.__dict__

    def __ne__(self, other):
        """Returns true if both objects are not equal"""
        return not self == other


# code/UI/OpenAPI/python-flask-server/openapi_server/models/edge.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server.models.qualifier import Qualifier
from openapi_server.models.retrieval_source import RetrievalSource
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501
from openapi_server.models.qualifier import Qualifier  # noqa: E501
from openapi_server.models.retrieval_source import RetrievalSource  # noqa: E501

class Edge(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, predicate=None, subject=None, object=None, attributes=None, qualifiers=None, sources=None):  # noqa: E501
        """Edge - a model defined in OpenAPI

        :param predicate: The predicate of this Edge.  # noqa: E501
        :type predicate: str
        :param subject: The subject of this Edge.  # noqa: E501
        :type subject: str
        :param object: The object of this Edge.  # noqa: E501
        :type object: str
        :param attributes: The attributes of this Edge.  # noqa: E501
        :type attributes: List[Attribute]
        :param qualifiers: The qualifiers of this Edge.  # noqa: E501
        :type qualifiers: List[Qualifier]
        :param sources: The sources of this Edge.  # noqa: E501
        :type sources: List[RetrievalSource]
        """
        self.openapi_types = {
            'predicate': str,
            'subject': str,
            'object': str,
            'attributes': List[Attribute],
            'qualifiers': List[Qualifier],
            'sources': List[RetrievalSource]
        }

        self.attribute_map = {
            'predicate': 'predicate',
            'subject': 'subject',
            'object': 'object',
            'attributes': 'attributes',
            'qualifiers': 'qualifiers',
            'sources': 'sources'
        }

        self._predicate = predicate
        self._subject = subject
        self._object = object
        self._attributes = attributes
        self._qualifiers = qualifiers
        self._sources = sources

    @classmethod
    def from_dict(cls, dikt) -> 'Edge':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Edge of this Edge.  # noqa: E501
        :rtype: Edge
        """
        return util.deserialize_model(dikt, cls)

    @property
    def predicate(self):
        """Gets the predicate of this Edge.


        :return: The predicate of this Edge.
        :rtype: str
        """
        return self._predicate

    @predicate.setter
    def predicate(self, predicate):
        """Sets the predicate of this Edge.


        :param predicate: The predicate of this Edge.
        :type predicate: str
        """
        if predicate is None:
            raise ValueError("Invalid value for `predicate`, must not be `None`")  # noqa: E501

        self._predicate = predicate

    @property
    def subject(self):
        """Gets the subject of this Edge.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The subject of this Edge.
        :rtype: str
        """
        return self._subject

    @subject.setter
    def subject(self, subject):
        """Sets the subject of this Edge.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param subject: The subject of this Edge.
        :type subject: str
        """
        if subject is None:
            raise ValueError("Invalid value for `subject`, must not be `None`")  # noqa: E501

        self._subject = subject

    @property
    def object(self):
        """Gets the object of this Edge.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The object of this Edge.
        :rtype: str
        """
        return self._object

    @object.setter
    def object(self, object):
        """Sets the object of this Edge.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param object: The object of this Edge.
        :type object: str
        """
        if object is None:
            raise ValueError("Invalid value for `object`, must not be `None`")  # noqa: E501

        self._object = object

    @property
    def attributes(self):
        """Gets the attributes of this Edge.

        A list of additional attributes for this edge  # noqa: E501

        :return: The attributes of this Edge.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this Edge.

        A list of additional attributes for this edge  # noqa: E501

        :param attributes: The attributes of this Edge.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes

    @property
    def qualifiers(self):
        """Gets the qualifiers of this Edge.

        A set of Qualifiers that act together to add nuance or detail to the statement expressed in an Edge.  # noqa: E501

        :return: The qualifiers of this Edge.
        :rtype: List[Qualifier]
        """
        return self._qualifiers

    @qualifiers.setter
    def qualifiers(self, qualifiers):
        """Sets the qualifiers of this Edge.

        A set of Qualifiers that act together to add nuance or detail to the statement expressed in an Edge.  # noqa: E501

        :param qualifiers: The qualifiers of this Edge.
        :type qualifiers: List[Qualifier]
        """

        self._qualifiers = qualifiers

    @property
    def sources(self):
        """Gets the sources of this Edge.

        A list of RetrievalSource objects that provide information about how a particular Information Resource served as a source from which the knowledge expressed in an Edge, or data used to generate this knowledge, was retrieved.  # noqa: E501

        :return: The sources of this Edge.
        :rtype: List[RetrievalSource]
        """
        return self._sources

    @sources.setter
    def sources(self, sources):
        """Sets the sources of this Edge.

        A list of RetrievalSource objects that provide information about how a particular Information Resource served as a source from which the knowledge expressed in an Edge, or data used to generate this knowledge, was retrieved.  # noqa: E501

        :param sources: The sources of this Edge.
        :type sources: List[RetrievalSource]
        """
        if sources is None:
            raise ValueError("Invalid value for `sources`, must not be `None`")  # noqa: E501
        if sources is not None and len(sources) < 1:
            raise ValueError("Invalid value for `sources`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._sources = sources



# code/UI/OpenAPI/python-flask-server/openapi_server/models/edge_binding.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501

class EdgeBinding(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, attributes=None):  # noqa: E501
        """EdgeBinding - a model defined in OpenAPI

        :param id: The id of this EdgeBinding.  # noqa: E501
        :type id: str
        :param attributes: The attributes of this EdgeBinding.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'id': str,
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'id': 'id',
            'attributes': 'attributes'
        }

        self._id = id
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'EdgeBinding':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The EdgeBinding of this EdgeBinding.  # noqa: E501
        :rtype: EdgeBinding
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this EdgeBinding.

        The key identifier of a specific KnowledgeGraph Edge.  # noqa: E501

        :return: The id of this EdgeBinding.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this EdgeBinding.

        The key identifier of a specific KnowledgeGraph Edge.  # noqa: E501

        :param id: The id of this EdgeBinding.
        :type id: str
        """
        if id is None:
            raise ValueError("Invalid value for `id`, must not be `None`")  # noqa: E501

        self._id = id

    @property
    def attributes(self):
        """Gets the attributes of this EdgeBinding.

        A list of attributes providing further information about the edge binding. This is not intended for capturing edge attributes and should only be used for properties that vary from result to result.  # noqa: E501

        :return: The attributes of this EdgeBinding.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this EdgeBinding.

        A list of attributes providing further information about the edge binding. This is not intended for capturing edge attributes and should only be used for properties that vary from result to result.  # noqa: E501

        :param attributes: The attributes of this EdgeBinding.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/openapi_server/models/entity_query.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class EntityQuery(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, terms=None, format=None):  # noqa: E501
        """EntityQuery - a model defined in OpenAPI

        :param terms: The terms of this EntityQuery.  # noqa: E501
        :type terms: List[str]
        :param format: The format of this EntityQuery.  # noqa: E501
        :type format: str
        """
        self.openapi_types = {
            'terms': List[str],
            'format': str
        }

        self.attribute_map = {
            'terms': 'terms',
            'format': 'format'
        }

        self._terms = terms
        self._format = format

    @classmethod
    def from_dict(cls, dikt) -> 'EntityQuery':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The EntityQuery of this EntityQuery.  # noqa: E501
        :rtype: EntityQuery
        """
        return util.deserialize_model(dikt, cls)

    @property
    def terms(self):
        """Gets the terms of this EntityQuery.

        List of term strings to search for  # noqa: E501

        :return: The terms of this EntityQuery.
        :rtype: List[str]
        """
        return self._terms

    @terms.setter
    def terms(self, terms):
        """Sets the terms of this EntityQuery.

        List of term strings to search for  # noqa: E501

        :param terms: The terms of this EntityQuery.
        :type terms: List[str]
        """

        self._terms = terms

    @property
    def format(self):
        """Gets the format of this EntityQuery.

        Format of the response (full or compact)  # noqa: E501

        :return: The format of this EntityQuery.
        :rtype: str
        """
        return self._format

    @format.setter
    def format(self, format):
        """Sets the format of this EntityQuery.

        Format of the response (full or compact)  # noqa: E501

        :param format: The format of this EntityQuery.
        :type format: str
        """

        self._format = format


# code/UI/OpenAPI/python-flask-server/openapi_server/models/knowledge_graph.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.edge import Edge
from openapi_server.models.node import Node
from openapi_server import util

from openapi_server.models.edge import Edge  # noqa: E501
from openapi_server.models.node import Node  # noqa: E501

class KnowledgeGraph(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, nodes=None, edges=None):  # noqa: E501
        """KnowledgeGraph - a model defined in OpenAPI

        :param nodes: The nodes of this KnowledgeGraph.  # noqa: E501
        :type nodes: Dict[str, Node]
        :param edges: The edges of this KnowledgeGraph.  # noqa: E501
        :type edges: Dict[str, Edge]
        """
        self.openapi_types = {
            'nodes': Dict[str, Node],
            'edges': Dict[str, Edge]
        }

        self.attribute_map = {
            'nodes': 'nodes',
            'edges': 'edges'
        }

        self._nodes = nodes
        self._edges = edges

    @classmethod
    def from_dict(cls, dikt) -> 'KnowledgeGraph':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The KnowledgeGraph of this KnowledgeGraph.  # noqa: E501
        :rtype: KnowledgeGraph
        """
        return util.deserialize_model(dikt, cls)

    @property
    def nodes(self):
        """Gets the nodes of this KnowledgeGraph.

        Dictionary of Node instances used in the KnowledgeGraph, referenced elsewhere in the TRAPI output by the dictionary key.  # noqa: E501

        :return: The nodes of this KnowledgeGraph.
        :rtype: Dict[str, Node]
        """
        return self._nodes

    @nodes.setter
    def nodes(self, nodes):
        """Sets the nodes of this KnowledgeGraph.

        Dictionary of Node instances used in the KnowledgeGraph, referenced elsewhere in the TRAPI output by the dictionary key.  # noqa: E501

        :param nodes: The nodes of this KnowledgeGraph.
        :type nodes: Dict[str, Node]
        """
        if nodes is None:
            raise ValueError("Invalid value for `nodes`, must not be `None`")  # noqa: E501

        self._nodes = nodes

    @property
    def edges(self):
        """Gets the edges of this KnowledgeGraph.

        Dictionary of Edge instances used in the KnowledgeGraph, referenced elsewhere in the TRAPI output by the dictionary key.  # noqa: E501

        :return: The edges of this KnowledgeGraph.
        :rtype: Dict[str, Edge]
        """
        return self._edges

    @edges.setter
    def edges(self, edges):
        """Sets the edges of this KnowledgeGraph.

        Dictionary of Edge instances used in the KnowledgeGraph, referenced elsewhere in the TRAPI output by the dictionary key.  # noqa: E501

        :param edges: The edges of this KnowledgeGraph.
        :type edges: Dict[str, Edge]
        """
        if edges is None:
            raise ValueError("Invalid value for `edges`, must not be `None`")  # noqa: E501

        self._edges = edges


# code/UI/OpenAPI/python-flask-server/openapi_server/models/log_entry.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_level import LogLevel
from openapi_server import util

from openapi_server.models.log_level import LogLevel  # noqa: E501

class LogEntry(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, timestamp=None, level=None, code=None, message=None):  # noqa: E501
        """LogEntry - a model defined in OpenAPI

        :param timestamp: The timestamp of this LogEntry.  # noqa: E501
        :type timestamp: datetime
        :param level: The level of this LogEntry.  # noqa: E501
        :type level: LogLevel
        :param code: The code of this LogEntry.  # noqa: E501
        :type code: str
        :param message: The message of this LogEntry.  # noqa: E501
        :type message: str
        """
        self.openapi_types = {
            'timestamp': datetime,
            'level': LogLevel,
            'code': str,
            'message': str
        }

        self.attribute_map = {
            'timestamp': 'timestamp',
            'level': 'level',
            'code': 'code',
            'message': 'message'
        }

        self._timestamp = timestamp
        self._level = level
        self._code = code
        self._message = message

    @classmethod
    def from_dict(cls, dikt) -> 'LogEntry':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The LogEntry of this LogEntry.  # noqa: E501
        :rtype: LogEntry
        """
        return util.deserialize_model(dikt, cls)

    @property
    def timestamp(self):
        """Gets the timestamp of this LogEntry.

        Timestamp in ISO 8601 format  # noqa: E501

        :return: The timestamp of this LogEntry.
        :rtype: datetime
        """
        return self._timestamp

    @timestamp.setter
    def timestamp(self, timestamp):
        """Sets the timestamp of this LogEntry.

        Timestamp in ISO 8601 format  # noqa: E501

        :param timestamp: The timestamp of this LogEntry.
        :type timestamp: datetime
        """

        self._timestamp = timestamp

    @property
    def level(self):
        """Gets the level of this LogEntry.


        :return: The level of this LogEntry.
        :rtype: LogLevel
        """
        return self._level

    @level.setter
    def level(self, level):
        """Sets the level of this LogEntry.


        :param level: The level of this LogEntry.
        :type level: LogLevel
        """

        self._level = level

    @property
    def code(self):
        """Gets the code of this LogEntry.

        One of a standardized set of short codes e.g. QueryNotTraversable, KPNotAvailable, KPResponseMalformed  # noqa: E501

        :return: The code of this LogEntry.
        :rtype: str
        """
        return self._code

    @code.setter
    def code(self, code):
        """Sets the code of this LogEntry.

        One of a standardized set of short codes e.g. QueryNotTraversable, KPNotAvailable, KPResponseMalformed  # noqa: E501

        :param code: The code of this LogEntry.
        :type code: str
        """

        self._code = code

    @property
    def message(self):
        """Gets the message of this LogEntry.

        A human-readable log message  # noqa: E501

        :return: The message of this LogEntry.
        :rtype: str
        """
        return self._message

    @message.setter
    def message(self, message):
        """Sets the message of this LogEntry.

        A human-readable log message  # noqa: E501

        :param message: The message of this LogEntry.
        :type message: str
        """

        self._message = message


# code/UI/OpenAPI/python-flask-server/openapi_server/models/log_level.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class LogLevel(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    """
    allowed enum values
    """
    ERROR = "ERROR"
    WARNING = "WARNING"
    INFO = "INFO"
    DEBUG = "DEBUG"
    def __init__(self):  # noqa: E501
        """LogLevel - a model defined in OpenAPI

        """
        self.openapi_types = {
        }

        self.attribute_map = {
        }

    @classmethod
    def from_dict(cls, dikt) -> 'LogLevel':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The LogLevel of this LogLevel.  # noqa: E501
        :rtype: LogLevel
        """
        return util.deserialize_model(dikt, cls)


# code/UI/OpenAPI/python-flask-server/openapi_server/models/mesh_ngd_response.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class MeshNgdResponse(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, result_code=None, message=None, value=None):  # noqa: E501
        """MeshNgdResponse - a model defined in OpenAPI

        :param result_code: The result_code of this MeshNgdResponse.  # noqa: E501
        :type result_code: str
        :param message: The message of this MeshNgdResponse.  # noqa: E501
        :type message: str
        :param value: The value of this MeshNgdResponse.  # noqa: E501
        :type value: float
        """
        self.openapi_types = {
            'result_code': str,
            'message': str,
            'value': float
        }

        self.attribute_map = {
            'result_code': 'result_code',
            'message': 'message',
            'value': 'value'
        }

        self._result_code = result_code
        self._message = message
        self._value = value

    @classmethod
    def from_dict(cls, dikt) -> 'MeshNgdResponse':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MeshNgdResponse of this MeshNgdResponse.  # noqa: E501
        :rtype: MeshNgdResponse
        """
        return util.deserialize_model(dikt, cls)

    @property
    def result_code(self):
        """Gets the result_code of this MeshNgdResponse.

        Set to OK for success, or some other short string to indicate and error (e.g., TermNotFound, etc.)  # noqa: E501

        :return: The result_code of this MeshNgdResponse.
        :rtype: str
        """
        return self._result_code

    @result_code.setter
    def result_code(self, result_code):
        """Sets the result_code of this MeshNgdResponse.

        Set to OK for success, or some other short string to indicate and error (e.g., TermNotFound, etc.)  # noqa: E501

        :param result_code: The result_code of this MeshNgdResponse.
        :type result_code: str
        """

        self._result_code = result_code

    @property
    def message(self):
        """Gets the message of this MeshNgdResponse.

        Extended message denoting the success or mode of failure for request  # noqa: E501

        :return: The message of this MeshNgdResponse.
        :rtype: str
        """
        return self._message

    @message.setter
    def message(self, message):
        """Sets the message of this MeshNgdResponse.

        Extended message denoting the success or mode of failure for request  # noqa: E501

        :param message: The message of this MeshNgdResponse.
        :type message: str
        """

        self._message = message

    @property
    def value(self):
        """Gets the value of this MeshNgdResponse.

        Calculated Normalized Google Distance for the two supplied terms  # noqa: E501

        :return: The value of this MeshNgdResponse.
        :rtype: float
        """
        return self._value

    @value.setter
    def value(self, value):
        """Sets the value of this MeshNgdResponse.

        Calculated Normalized Google Distance for the two supplied terms  # noqa: E501

        :param value: The value of this MeshNgdResponse.
        :type value: float
        """

        self._value = value


# code/UI/OpenAPI/python-flask-server/openapi_server/models/message.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.knowledge_graph import KnowledgeGraph
from openapi_server.models.query_graph import QueryGraph
from openapi_server.models.result import Result
from openapi_server import util

from openapi_server.models.knowledge_graph import KnowledgeGraph  # noqa: E501
from openapi_server.models.query_graph import QueryGraph  # noqa: E501
from openapi_server.models.result import Result  # noqa: E501

class Message(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, results=None, query_graph=None, knowledge_graph=None, auxiliary_graphs=None):  # noqa: E501
        """Message - a model defined in OpenAPI

        :param results: The results of this Message.  # noqa: E501
        :type results: List[Result]
        :param query_graph: The query_graph of this Message.  # noqa: E501
        :type query_graph: QueryGraph
        :param knowledge_graph: The knowledge_graph of this Message.  # noqa: E501
        :type knowledge_graph: KnowledgeGraph
        :param auxiliary_graphs: The auxiliary_graphs of this Message.  # noqa: E501
        :type auxiliary_graphs: Dict[str, object]
        """
        self.openapi_types = {
            'results': List[Result],
            'query_graph': QueryGraph,
            'knowledge_graph': KnowledgeGraph,
            'auxiliary_graphs': Dict[str, object]
        }

        self.attribute_map = {
            'results': 'results',
            'query_graph': 'query_graph',
            'knowledge_graph': 'knowledge_graph',
            'auxiliary_graphs': 'auxiliary_graphs'
        }

        self._results = results
        self._query_graph = query_graph
        self._knowledge_graph = knowledge_graph
        self._auxiliary_graphs = auxiliary_graphs

    @classmethod
    def from_dict(cls, dikt) -> 'Message':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Message of this Message.  # noqa: E501
        :rtype: Message
        """
        return util.deserialize_model(dikt, cls)

    @property
    def results(self):
        """Gets the results of this Message.

        List of all returned Result objects for the query posed  # noqa: E501

        :return: The results of this Message.
        :rtype: List[Result]
        """
        return self._results

    @results.setter
    def results(self, results):
        """Sets the results of this Message.

        List of all returned Result objects for the query posed  # noqa: E501

        :param results: The results of this Message.
        :type results: List[Result]
        """

        self._results = results

    @property
    def query_graph(self):
        """Gets the query_graph of this Message.

        QueryGraph object that contains a serialization of a query in the form of a graph  # noqa: E501

        :return: The query_graph of this Message.
        :rtype: QueryGraph
        """
        return self._query_graph

    @query_graph.setter
    def query_graph(self, query_graph):
        """Sets the query_graph of this Message.

        QueryGraph object that contains a serialization of a query in the form of a graph  # noqa: E501

        :param query_graph: The query_graph of this Message.
        :type query_graph: QueryGraph
        """

        self._query_graph = query_graph

    @property
    def knowledge_graph(self):
        """Gets the knowledge_graph of this Message.

        KnowledgeGraph object that contains lists of nodes and edges in the thought graph corresponding to the message  # noqa: E501

        :return: The knowledge_graph of this Message.
        :rtype: KnowledgeGraph
        """
        return self._knowledge_graph

    @knowledge_graph.setter
    def knowledge_graph(self, knowledge_graph):
        """Sets the knowledge_graph of this Message.

        KnowledgeGraph object that contains lists of nodes and edges in the thought graph corresponding to the message  # noqa: E501

        :param knowledge_graph: The knowledge_graph of this Message.
        :type knowledge_graph: KnowledgeGraph
        """

        self._knowledge_graph = knowledge_graph

    @property
    def auxiliary_graphs(self):
        """Gets the auxiliary_graphs of this Message.

        Dictionary of AuxiliaryGraph instances that are used by Knowledge Graph Edges and Result Analyses. These are referenced elsewhere by the dictionary key.  # noqa: E501

        :return: The auxiliary_graphs of this Message.
        :rtype: Dict[str, object]
        """
        return self._auxiliary_graphs

    @auxiliary_graphs.setter
    def auxiliary_graphs(self, auxiliary_graphs):
        """Sets the auxiliary_graphs of this Message.

        Dictionary of AuxiliaryGraph instances that are used by Knowledge Graph Edges and Result Analyses. These are referenced elsewhere by the dictionary key.  # noqa: E501

        :param auxiliary_graphs: The auxiliary_graphs of this Message.
        :type auxiliary_graphs: Dict[str, object]
        """

        self._auxiliary_graphs = auxiliary_graphs



# code/UI/OpenAPI/python-flask-server/openapi_server/models/meta_attribute.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class MetaAttribute(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, attribute_type_id=None, attribute_source=None, original_attribute_names=None, constraint_use=False, constraint_name=None):  # noqa: E501
        """MetaAttribute - a model defined in OpenAPI

        :param attribute_type_id: The attribute_type_id of this MetaAttribute.  # noqa: E501
        :type attribute_type_id: str
        :param attribute_source: The attribute_source of this MetaAttribute.  # noqa: E501
        :type attribute_source: str
        :param original_attribute_names: The original_attribute_names of this MetaAttribute.  # noqa: E501
        :type original_attribute_names: List[str]
        :param constraint_use: The constraint_use of this MetaAttribute.  # noqa: E501
        :type constraint_use: bool
        :param constraint_name: The constraint_name of this MetaAttribute.  # noqa: E501
        :type constraint_name: str
        """
        self.openapi_types = {
            'attribute_type_id': str,
            'attribute_source': str,
            'original_attribute_names': List[str],
            'constraint_use': bool,
            'constraint_name': str
        }

        self.attribute_map = {
            'attribute_type_id': 'attribute_type_id',
            'attribute_source': 'attribute_source',
            'original_attribute_names': 'original_attribute_names',
            'constraint_use': 'constraint_use',
            'constraint_name': 'constraint_name'
        }

        self._attribute_type_id = attribute_type_id
        self._attribute_source = attribute_source
        self._original_attribute_names = original_attribute_names
        self._constraint_use = constraint_use
        self._constraint_name = constraint_name

    @classmethod
    def from_dict(cls, dikt) -> 'MetaAttribute':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MetaAttribute of this MetaAttribute.  # noqa: E501
        :rtype: MetaAttribute
        """
        return util.deserialize_model(dikt, cls)

    @property
    def attribute_type_id(self):
        """Gets the attribute_type_id of this MetaAttribute.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The attribute_type_id of this MetaAttribute.
        :rtype: str
        """
        return self._attribute_type_id

    @attribute_type_id.setter
    def attribute_type_id(self, attribute_type_id):
        """Sets the attribute_type_id of this MetaAttribute.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param attribute_type_id: The attribute_type_id of this MetaAttribute.
        :type attribute_type_id: str
        """
        if attribute_type_id is None:
            raise ValueError("Invalid value for `attribute_type_id`, must not be `None`")  # noqa: E501

        self._attribute_type_id = attribute_type_id

    @property
    def attribute_source(self):
        """Gets the attribute_source of this MetaAttribute.

        Source of an attribute provided by this TRAPI web service.  # noqa: E501

        :return: The attribute_source of this MetaAttribute.
        :rtype: str
        """
        return self._attribute_source

    @attribute_source.setter
    def attribute_source(self, attribute_source):
        """Sets the attribute_source of this MetaAttribute.

        Source of an attribute provided by this TRAPI web service.  # noqa: E501

        :param attribute_source: The attribute_source of this MetaAttribute.
        :type attribute_source: str
        """

        self._attribute_source = attribute_source

    @property
    def original_attribute_names(self):
        """Gets the original_attribute_names of this MetaAttribute.

        Names of an the attribute as provided by the source.  # noqa: E501

        :return: The original_attribute_names of this MetaAttribute.
        :rtype: List[str]
        """
        return self._original_attribute_names

    @original_attribute_names.setter
    def original_attribute_names(self, original_attribute_names):
        """Sets the original_attribute_names of this MetaAttribute.

        Names of an the attribute as provided by the source.  # noqa: E501

        :param original_attribute_names: The original_attribute_names of this MetaAttribute.
        :type original_attribute_names: List[str]
        """
        if original_attribute_names is not None and len(original_attribute_names) < 1:
            raise ValueError("Invalid value for `original_attribute_names`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._original_attribute_names = original_attribute_names

    @property
    def constraint_use(self):
        """Gets the constraint_use of this MetaAttribute.

        Indicates whether this attribute can be used as a query constraint.  # noqa: E501

        :return: The constraint_use of this MetaAttribute.
        :rtype: bool
        """
        return self._constraint_use

    @constraint_use.setter
    def constraint_use(self, constraint_use):
        """Sets the constraint_use of this MetaAttribute.

        Indicates whether this attribute can be used as a query constraint.  # noqa: E501

        :param constraint_use: The constraint_use of this MetaAttribute.
        :type constraint_use: bool
        """

        self._constraint_use = constraint_use

    @property
    def constraint_name(self):
        """Gets the constraint_name of this MetaAttribute.

        Human-readable name or label for the constraint concept. Required whenever constraint_use is true.  # noqa: E501

        :return: The constraint_name of this MetaAttribute.
        :rtype: str
        """
        return self._constraint_name

    @constraint_name.setter
    def constraint_name(self, constraint_name):
        """Sets the constraint_name of this MetaAttribute.

        Human-readable name or label for the constraint concept. Required whenever constraint_use is true.  # noqa: E501

        :param constraint_name: The constraint_name of this MetaAttribute.
        :type constraint_name: str
        """

        self._constraint_name = constraint_name


# code/UI/OpenAPI/python-flask-server/openapi_server/models/meta_edge.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.meta_attribute import MetaAttribute
from openapi_server.models.meta_qualifier import MetaQualifier
import re
from openapi_server import util

from openapi_server.models.meta_attribute import MetaAttribute  # noqa: E501
from openapi_server.models.meta_qualifier import MetaQualifier  # noqa: E501
import re  # noqa: E501

class MetaEdge(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, subject=None, predicate=None, object=None, knowledge_types=None, attributes=None, qualifiers=None, association=None):  # noqa: E501
        """MetaEdge - a model defined in OpenAPI

        :param subject: The subject of this MetaEdge.  # noqa: E501
        :type subject: str
        :param predicate: The predicate of this MetaEdge.  # noqa: E501
        :type predicate: str
        :param object: The object of this MetaEdge.  # noqa: E501
        :type object: str
        :param knowledge_types: The knowledge_types of this MetaEdge.  # noqa: E501
        :type knowledge_types: List[str]
        :param attributes: The attributes of this MetaEdge.  # noqa: E501
        :type attributes: List[MetaAttribute]
        :param qualifiers: The qualifiers of this MetaEdge.  # noqa: E501
        :type qualifiers: List[MetaQualifier]
        :param association: The association of this MetaEdge.  # noqa: E501
        :type association: str
        """
        self.openapi_types = {
            'subject': str,
            'predicate': str,
            'object': str,
            'knowledge_types': List[str],
            'attributes': List[MetaAttribute],
            'qualifiers': List[MetaQualifier],
            'association': str
        }

        self.attribute_map = {
            'subject': 'subject',
            'predicate': 'predicate',
            'object': 'object',
            'knowledge_types': 'knowledge_types',
            'attributes': 'attributes',
            'qualifiers': 'qualifiers',
            'association': 'association'
        }

        self._subject = subject
        self._predicate = predicate
        self._object = object
        self._knowledge_types = knowledge_types
        self._attributes = attributes
        self._qualifiers = qualifiers
        self._association = association

    @classmethod
    def from_dict(cls, dikt) -> 'MetaEdge':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MetaEdge of this MetaEdge.  # noqa: E501
        :rtype: MetaEdge
        """
        return util.deserialize_model(dikt, cls)

    @property
    def subject(self):
        """Gets the subject of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :return: The subject of this MetaEdge.
        :rtype: str
        """
        return self._subject

    @subject.setter
    def subject(self, subject):
        """Sets the subject of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :param subject: The subject of this MetaEdge.
        :type subject: str
        """
        if subject is None:
            raise ValueError("Invalid value for `subject`, must not be `None`")  # noqa: E501
        if subject is not None and not re.search(r'^biolink:[A-Z][a-zA-Z]*$', subject):  # noqa: E501
            raise ValueError("Invalid value for `subject`, must be a follow pattern or equal to `/^biolink:[A-Z][a-zA-Z]*$/`")  # noqa: E501

        self._subject = subject

    @property
    def predicate(self):
        """Gets the predicate of this MetaEdge.

        CURIE for a Biolink 'predicate' slot, taken from the Biolink slot ('is_a') hierarchy rooted in biolink:related_to (snake_case). This predicate defines the Biolink relationship between the subject and object nodes of a biolink:Association defining a knowledge graph edge.  # noqa: E501

        :return: The predicate of this MetaEdge.
        :rtype: str
        """
        return self._predicate

    @predicate.setter
    def predicate(self, predicate):
        """Sets the predicate of this MetaEdge.

        CURIE for a Biolink 'predicate' slot, taken from the Biolink slot ('is_a') hierarchy rooted in biolink:related_to (snake_case). This predicate defines the Biolink relationship between the subject and object nodes of a biolink:Association defining a knowledge graph edge.  # noqa: E501

        :param predicate: The predicate of this MetaEdge.
        :type predicate: str
        """
        if predicate is None:
            raise ValueError("Invalid value for `predicate`, must not be `None`")  # noqa: E501
        if predicate is not None and not re.search(r'^biolink:[a-z][a-z_]*$', predicate):  # noqa: E501
            raise ValueError("Invalid value for `predicate`, must be a follow pattern or equal to `/^biolink:[a-z][a-z_]*$/`")  # noqa: E501

        self._predicate = predicate

    @property
    def object(self):
        """Gets the object of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :return: The object of this MetaEdge.
        :rtype: str
        """
        return self._object

    @object.setter
    def object(self, object):
        """Sets the object of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :param object: The object of this MetaEdge.
        :type object: str
        """
        if object is None:
            raise ValueError("Invalid value for `object`, must not be `None`")  # noqa: E501
        if object is not None and not re.search(r'^biolink:[A-Z][a-zA-Z]*$', object):  # noqa: E501
            raise ValueError("Invalid value for `object`, must be a follow pattern or equal to `/^biolink:[A-Z][a-zA-Z]*$/`")  # noqa: E501

        self._object = object

    @property
    def knowledge_types(self):
        """Gets the knowledge_types of this MetaEdge.

        A list of knowledge_types that are supported by the service. If the knowledge_types is null, this means that only 'lookup' is supported. Currently allowed values are 'lookup' or 'inferred'.  # noqa: E501

        :return: The knowledge_types of this MetaEdge.
        :rtype: List[str]
        """
        return self._knowledge_types

    @knowledge_types.setter
    def knowledge_types(self, knowledge_types):
        """Sets the knowledge_types of this MetaEdge.

        A list of knowledge_types that are supported by the service. If the knowledge_types is null, this means that only 'lookup' is supported. Currently allowed values are 'lookup' or 'inferred'.  # noqa: E501

        :param knowledge_types: The knowledge_types of this MetaEdge.
        :type knowledge_types: List[str]
        """
        if knowledge_types is not None and len(knowledge_types) < 1:
            raise ValueError("Invalid value for `knowledge_types`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._knowledge_types = knowledge_types

    @property
    def attributes(self):
        """Gets the attributes of this MetaEdge.

        Edge attributes provided by this TRAPI web service.  # noqa: E501

        :return: The attributes of this MetaEdge.
        :rtype: List[MetaAttribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this MetaEdge.

        Edge attributes provided by this TRAPI web service.  # noqa: E501

        :param attributes: The attributes of this MetaEdge.
        :type attributes: List[MetaAttribute]
        """

        self._attributes = attributes

    @property
    def qualifiers(self):
        """Gets the qualifiers of this MetaEdge.

        Qualifiers that are possible to be found on this edge type.  # noqa: E501

        :return: The qualifiers of this MetaEdge.
        :rtype: List[MetaQualifier]
        """
        return self._qualifiers

    @qualifiers.setter
    def qualifiers(self, qualifiers):
        """Sets the qualifiers of this MetaEdge.

        Qualifiers that are possible to be found on this edge type.  # noqa: E501

        :param qualifiers: The qualifiers of this MetaEdge.
        :type qualifiers: List[MetaQualifier]
        """

        self._qualifiers = qualifiers

    @property
    def association(self):
        """Gets the association of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :return: The association of this MetaEdge.
        :rtype: str
        """
        return self._association

    @association.setter
    def association(self, association):
        """Sets the association of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :param association: The association of this MetaEdge.
        :type association: str
        """
        if association is not None and not re.search(r'^biolink:[A-Z][a-zA-Z]*$', association):  # noqa: E501
            raise ValueError("Invalid value for `association`, must be a follow pattern or equal to `/^biolink:[A-Z][a-zA-Z]*$/`")  # noqa: E501

        self._association = association


# code/UI/OpenAPI/python-flask-server/openapi_server/models/meta_knowledge_graph.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.meta_edge import MetaEdge
from openapi_server.models.meta_node import MetaNode
from openapi_server import util

from openapi_server.models.meta_edge import MetaEdge  # noqa: E501
from openapi_server.models.meta_node import MetaNode  # noqa: E501

class MetaKnowledgeGraph(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, nodes=None, edges=None):  # noqa: E501
        """MetaKnowledgeGraph - a model defined in OpenAPI

        :param nodes: The nodes of this MetaKnowledgeGraph.  # noqa: E501
        :type nodes: Dict[str, MetaNode]
        :param edges: The edges of this MetaKnowledgeGraph.  # noqa: E501
        :type edges: List[MetaEdge]
        """
        self.openapi_types = {
            'nodes': Dict[str, MetaNode],
            'edges': List[MetaEdge]
        }

        self.attribute_map = {
            'nodes': 'nodes',
            'edges': 'edges'
        }

        self._nodes = nodes
        self._edges = edges

    @classmethod
    def from_dict(cls, dikt) -> 'MetaKnowledgeGraph':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MetaKnowledgeGraph of this MetaKnowledgeGraph.  # noqa: E501
        :rtype: MetaKnowledgeGraph
        """
        return util.deserialize_model(dikt, cls)

    @property
    def nodes(self):
        """Gets the nodes of this MetaKnowledgeGraph.

        Collection of the most specific node categories provided by this TRAPI web service, indexed by Biolink class CURIEs. A node category is only exposed here if there is node for which that is the most specific category available.  # noqa: E501

        :return: The nodes of this MetaKnowledgeGraph.
        :rtype: Dict[str, MetaNode]
        """
        return self._nodes

    @nodes.setter
    def nodes(self, nodes):
        """Sets the nodes of this MetaKnowledgeGraph.

        Collection of the most specific node categories provided by this TRAPI web service, indexed by Biolink class CURIEs. A node category is only exposed here if there is node for which that is the most specific category available.  # noqa: E501

        :param nodes: The nodes of this MetaKnowledgeGraph.
        :type nodes: Dict[str, MetaNode]
        """
        if nodes is None:
            raise ValueError("Invalid value for `nodes`, must not be `None`")  # noqa: E501

        self._nodes = nodes

    @property
    def edges(self):
        """Gets the edges of this MetaKnowledgeGraph.

        List of the most specific edges/predicates provided by this TRAPI web service. A predicate is only exposed here if there is an edge for which the predicate is the most specific available.  # noqa: E501

        :return: The edges of this MetaKnowledgeGraph.
        :rtype: List[MetaEdge]
        """
        return self._edges

    @edges.setter
    def edges(self, edges):
        """Sets the edges of this MetaKnowledgeGraph.

        List of the most specific edges/predicates provided by this TRAPI web service. A predicate is only exposed here if there is an edge for which the predicate is the most specific available.  # noqa: E501

        :param edges: The edges of this MetaKnowledgeGraph.
        :type edges: List[MetaEdge]
        """
        if edges is None:
            raise ValueError("Invalid value for `edges`, must not be `None`")  # noqa: E501

        self._edges = edges


# code/UI/OpenAPI/python-flask-server/openapi_server/models/meta_node.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.meta_attribute import MetaAttribute
from openapi_server import util

from openapi_server.models.meta_attribute import MetaAttribute  # noqa: E501

class MetaNode(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id_prefixes=None, attributes=None):  # noqa: E501
        """MetaNode - a model defined in OpenAPI

        :param id_prefixes: The id_prefixes of this MetaNode.  # noqa: E501
        :type id_prefixes: List[str]
        :param attributes: The attributes of this MetaNode.  # noqa: E501
        :type attributes: List[MetaAttribute]
        """
        self.openapi_types = {
            'id_prefixes': List[str],
            'attributes': List[MetaAttribute]
        }

        self.attribute_map = {
            'id_prefixes': 'id_prefixes',
            'attributes': 'attributes'
        }

        self._id_prefixes = id_prefixes
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'MetaNode':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MetaNode of this MetaNode.  # noqa: E501
        :rtype: MetaNode
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id_prefixes(self):
        """Gets the id_prefixes of this MetaNode.

        List of CURIE prefixes for the node category that this TRAPI web service understands and accepts on the input.  # noqa: E501

        :return: The id_prefixes of this MetaNode.
        :rtype: List[str]
        """
        return self._id_prefixes

    @id_prefixes.setter
    def id_prefixes(self, id_prefixes):
        """Sets the id_prefixes of this MetaNode.

        List of CURIE prefixes for the node category that this TRAPI web service understands and accepts on the input.  # noqa: E501

        :param id_prefixes: The id_prefixes of this MetaNode.
        :type id_prefixes: List[str]
        """
        if id_prefixes is None:
            raise ValueError("Invalid value for `id_prefixes`, must not be `None`")  # noqa: E501
        if id_prefixes is not None and len(id_prefixes) < 1:
            raise ValueError("Invalid value for `id_prefixes`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._id_prefixes = id_prefixes

    @property
    def attributes(self):
        """Gets the attributes of this MetaNode.

        Node attributes provided by this TRAPI web service.  # noqa: E501

        :return: The attributes of this MetaNode.
        :rtype: List[MetaAttribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this MetaNode.

        Node attributes provided by this TRAPI web service.  # noqa: E501

        :param attributes: The attributes of this MetaNode.
        :type attributes: List[MetaAttribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/openapi_server/models/meta_qualifier.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class MetaQualifier(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, qualifier_type_id=None, applicable_values=None):  # noqa: E501
        """MetaQualifier - a model defined in OpenAPI

        :param qualifier_type_id: The qualifier_type_id of this MetaQualifier.  # noqa: E501
        :type qualifier_type_id: str
        :param applicable_values: The applicable_values of this MetaQualifier.  # noqa: E501
        :type applicable_values: List[str]
        """
        self.openapi_types = {
            'qualifier_type_id': str,
            'applicable_values': List[str]
        }

        self.attribute_map = {
            'qualifier_type_id': 'qualifier_type_id',
            'applicable_values': 'applicable_values'
        }

        self._qualifier_type_id = qualifier_type_id
        self._applicable_values = applicable_values

    @classmethod
    def from_dict(cls, dikt) -> 'MetaQualifier':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MetaQualifier of this MetaQualifier.  # noqa: E501
        :rtype: MetaQualifier
        """
        return util.deserialize_model(dikt, cls)

    @property
    def qualifier_type_id(self):
        """Gets the qualifier_type_id of this MetaQualifier.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The qualifier_type_id of this MetaQualifier.
        :rtype: str
        """
        return self._qualifier_type_id

    @qualifier_type_id.setter
    def qualifier_type_id(self, qualifier_type_id):
        """Sets the qualifier_type_id of this MetaQualifier.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param qualifier_type_id: The qualifier_type_id of this MetaQualifier.
        :type qualifier_type_id: str
        """
        if qualifier_type_id is None:
            raise ValueError("Invalid value for `qualifier_type_id`, must not be `None`")  # noqa: E501

        self._qualifier_type_id = qualifier_type_id

    @property
    def applicable_values(self):
        """Gets the applicable_values of this MetaQualifier.

        The list of values that are possible for this qualifier.  # noqa: E501

        :return: The applicable_values of this MetaQualifier.
        :rtype: List[str]
        """
        return self._applicable_values

    @applicable_values.setter
    def applicable_values(self, applicable_values):
        """Sets the applicable_values of this MetaQualifier.

        The list of values that are possible for this qualifier.  # noqa: E501

        :param applicable_values: The applicable_values of this MetaQualifier.
        :type applicable_values: List[str]
        """

        self._applicable_values = applicable_values


# code/UI/OpenAPI/python-flask-server/openapi_server/models/node.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501

class Node(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, name=None, categories=None, attributes=None):  # noqa: E501
        """Node - a model defined in OpenAPI

        :param name: The name of this Node.  # noqa: E501
        :type name: str
        :param categories: The categories of this Node.  # noqa: E501
        :type categories: List[str]
        :param attributes: The attributes of this Node.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'name': str,
            'categories': List[str],
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'name': 'name',
            'categories': 'categories',
            'attributes': 'attributes'
        }

        self._name = name
        self._categories = categories
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'Node':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Node of this Node.  # noqa: E501
        :rtype: Node
        """
        return util.deserialize_model(dikt, cls)

    @property
    def name(self):
        """Gets the name of this Node.

        Formal name of the entity  # noqa: E501

        :return: The name of this Node.
        :rtype: str
        """
        return self._name

    @name.setter
    def name(self, name):
        """Sets the name of this Node.

        Formal name of the entity  # noqa: E501

        :param name: The name of this Node.
        :type name: str
        """

        self._name = name

    @property
    def categories(self):
        """Gets the categories of this Node.


        :return: The categories of this Node.
        :rtype: List[str]
        """
        return self._categories

    @categories.setter
    def categories(self, categories):
        """Sets the categories of this Node.


        :param categories: The categories of this Node.
        :type categories: List[str]
        """

        self._categories = categories

    @property
    def attributes(self):
        """Gets the attributes of this Node.

        A list of attributes describing the node  # noqa: E501

        :return: The attributes of this Node.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this Node.

        A list of attributes describing the node  # noqa: E501

        :param attributes: The attributes of this Node.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/openapi_server/models/node_binding.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501

class NodeBinding(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, query_id=None, attributes=None):  # noqa: E501
        """NodeBinding - a model defined in OpenAPI

        :param id: The id of this NodeBinding.  # noqa: E501
        :type id: str
        :param query_id: The query_id of this NodeBinding.  # noqa: E501
        :type query_id: str
        :param attributes: The attributes of this NodeBinding.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'id': str,
            'query_id': str,
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'id': 'id',
            'query_id': 'query_id',
            'attributes': 'attributes'
        }

        self._id = id
        self._query_id = query_id
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'NodeBinding':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The NodeBinding of this NodeBinding.  # noqa: E501
        :rtype: NodeBinding
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this NodeBinding.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The id of this NodeBinding.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this NodeBinding.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param id: The id of this NodeBinding.
        :type id: str
        """
        if id is None:
            raise ValueError("Invalid value for `id`, must not be `None`")  # noqa: E501

        self._id = id

    @property
    def query_id(self):
        """Gets the query_id of this NodeBinding.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The query_id of this NodeBinding.
        :rtype: str
        """
        return self._query_id

    @query_id.setter
    def query_id(self, query_id):
        """Sets the query_id of this NodeBinding.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param query_id: The query_id of this NodeBinding.
        :type query_id: str
        """

        self._query_id = query_id

    @property
    def attributes(self):
        """Gets the attributes of this NodeBinding.

        A list of attributes providing further information about the node binding. This is not intended for capturing node attributes and should only be used for properties that vary from result to result.  # noqa: E501

        :return: The attributes of this NodeBinding.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this NodeBinding.

        A list of attributes providing further information about the node binding. This is not intended for capturing node attributes and should only be used for properties that vary from result to result.  # noqa: E501

        :param attributes: The attributes of this NodeBinding.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_annotate.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationAnnotate(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationAnnotate - a model defined in OpenAPI

        :param id: The id of this OperationAnnotate.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationAnnotate.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationAnnotate':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationAnnotate of this OperationAnnotate.  # noqa: E501
        :rtype: OperationAnnotate
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationAnnotate.


        :return: The id of this OperationAnnotate.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationAnnotate.


        :param id: The id of this OperationAnnotate.
        :type id: str
        """
        allowed_values = ["annotate"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationAnnotate.


        :return: The parameters of this OperationAnnotate.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationAnnotate.


        :param parameters: The parameters of this OperationAnnotate.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_annotate_edges.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_annotate_edges_parameters import OperationAnnotateEdgesParameters
from openapi_server import util

from openapi_server.models.operation_annotate_edges_parameters import OperationAnnotateEdgesParameters  # noqa: E501

class OperationAnnotateEdges(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationAnnotateEdges - a model defined in OpenAPI

        :param id: The id of this OperationAnnotateEdges.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationAnnotateEdges.  # noqa: E501
        :type parameters: OperationAnnotateEdgesParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationAnnotateEdgesParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationAnnotateEdges':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationAnnotateEdges of this OperationAnnotateEdges.  # noqa: E501
        :rtype: OperationAnnotateEdges
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationAnnotateEdges.


        :return: The id of this OperationAnnotateEdges.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationAnnotateEdges.


        :param id: The id of this OperationAnnotateEdges.
        :type id: str
        """
        allowed_values = ["annotate_edges"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationAnnotateEdges.


        :return: The parameters of this OperationAnnotateEdges.
        :rtype: OperationAnnotateEdgesParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationAnnotateEdges.


        :param parameters: The parameters of this OperationAnnotateEdges.
        :type parameters: OperationAnnotateEdgesParameters
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_annotate_edges_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationAnnotateEdgesParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, attributes=None):  # noqa: E501
        """OperationAnnotateEdgesParameters - a model defined in OpenAPI

        :param attributes: The attributes of this OperationAnnotateEdgesParameters.  # noqa: E501
        :type attributes: List[str]
        """
        self.openapi_types = {
            'attributes': List[str]
        }

        self.attribute_map = {
            'attributes': 'attributes'
        }

        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'OperationAnnotateEdgesParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationAnnotateEdges_parameters of this OperationAnnotateEdgesParameters.  # noqa: E501
        :rtype: OperationAnnotateEdgesParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def attributes(self):
        """Gets the attributes of this OperationAnnotateEdgesParameters.

        A list of attributes to annotate the edges with. If not included then all available data will be annotated.  # noqa: E501

        :return: The attributes of this OperationAnnotateEdgesParameters.
        :rtype: List[str]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this OperationAnnotateEdgesParameters.

        A list of attributes to annotate the edges with. If not included then all available data will be annotated.  # noqa: E501

        :param attributes: The attributes of this OperationAnnotateEdgesParameters.
        :type attributes: List[str]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_annotate_nodes.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_annotate_nodes_parameters import OperationAnnotateNodesParameters
from openapi_server import util

from openapi_server.models.operation_annotate_nodes_parameters import OperationAnnotateNodesParameters  # noqa: E501

class OperationAnnotateNodes(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationAnnotateNodes - a model defined in OpenAPI

        :param id: The id of this OperationAnnotateNodes.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationAnnotateNodes.  # noqa: E501
        :type parameters: OperationAnnotateNodesParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationAnnotateNodesParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationAnnotateNodes':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationAnnotateNodes of this OperationAnnotateNodes.  # noqa: E501
        :rtype: OperationAnnotateNodes
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationAnnotateNodes.


        :return: The id of this OperationAnnotateNodes.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationAnnotateNodes.


        :param id: The id of this OperationAnnotateNodes.
        :type id: str
        """
        allowed_values = ["annotate_nodes"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationAnnotateNodes.


        :return: The parameters of this OperationAnnotateNodes.
        :rtype: OperationAnnotateNodesParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationAnnotateNodes.


        :param parameters: The parameters of this OperationAnnotateNodes.
        :type parameters: OperationAnnotateNodesParameters
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_annotate_nodes_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationAnnotateNodesParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, attributes=None):  # noqa: E501
        """OperationAnnotateNodesParameters - a model defined in OpenAPI

        :param attributes: The attributes of this OperationAnnotateNodesParameters.  # noqa: E501
        :type attributes: List[str]
        """
        self.openapi_types = {
            'attributes': List[str]
        }

        self.attribute_map = {
            'attributes': 'attributes'
        }

        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'OperationAnnotateNodesParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationAnnotateNodes_parameters of this OperationAnnotateNodesParameters.  # noqa: E501
        :rtype: OperationAnnotateNodesParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def attributes(self):
        """Gets the attributes of this OperationAnnotateNodesParameters.

        A list of attributes to annotate the nodes with. If not included then all available data will be annotated.  # noqa: E501

        :return: The attributes of this OperationAnnotateNodesParameters.
        :rtype: List[str]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this OperationAnnotateNodesParameters.

        A list of attributes to annotate the nodes with. If not included then all available data will be annotated.  # noqa: E501

        :param attributes: The attributes of this OperationAnnotateNodesParameters.
        :type attributes: List[str]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_bind.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationBind(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationBind - a model defined in OpenAPI

        :param id: The id of this OperationBind.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationBind.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationBind':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationBind of this OperationBind.  # noqa: E501
        :rtype: OperationBind
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationBind.


        :return: The id of this OperationBind.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationBind.


        :param id: The id of this OperationBind.
        :type id: str
        """
        allowed_values = ["bind"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationBind.


        :return: The parameters of this OperationBind.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationBind.


        :param parameters: The parameters of this OperationBind.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_complete_results.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.any_type import AnyType
from openapi_server import util

from openapi_server.models.any_type import AnyType  # noqa: E501

class OperationCompleteResults(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationCompleteResults - a model defined in OpenAPI

        :param id: The id of this OperationCompleteResults.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationCompleteResults.  # noqa: E501
        :type parameters: AnyType
        """
        self.openapi_types = {
            'id': str,
            'parameters': AnyType
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationCompleteResults':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationCompleteResults of this OperationCompleteResults.  # noqa: E501
        :rtype: OperationCompleteResults
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationCompleteResults.


        :return: The id of this OperationCompleteResults.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationCompleteResults.


        :param id: The id of this OperationCompleteResults.
        :type id: str
        """
        allowed_values = ["complete_results"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationCompleteResults.


        :return: The parameters of this OperationCompleteResults.
        :rtype: AnyType
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationCompleteResults.


        :param parameters: The parameters of this OperationCompleteResults.
        :type parameters: AnyType
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_enrich_results.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_enrich_results_parameters import OperationEnrichResultsParameters
from openapi_server import util

from openapi_server.models.operation_enrich_results_parameters import OperationEnrichResultsParameters  # noqa: E501

class OperationEnrichResults(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationEnrichResults - a model defined in OpenAPI

        :param id: The id of this OperationEnrichResults.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationEnrichResults.  # noqa: E501
        :type parameters: OperationEnrichResultsParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationEnrichResultsParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationEnrichResults':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationEnrichResults of this OperationEnrichResults.  # noqa: E501
        :rtype: OperationEnrichResults
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationEnrichResults.


        :return: The id of this OperationEnrichResults.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationEnrichResults.


        :param id: The id of this OperationEnrichResults.
        :type id: str
        """
        allowed_values = ["enrich_results"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationEnrichResults.


        :return: The parameters of this OperationEnrichResults.
        :rtype: OperationEnrichResultsParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationEnrichResults.


        :param parameters: The parameters of this OperationEnrichResults.
        :type parameters: OperationEnrichResultsParameters
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_enrich_results_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationEnrichResultsParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, pvalue_threshold=0.0000010, qnode_keys=None):  # noqa: E501
        """OperationEnrichResultsParameters - a model defined in OpenAPI

        :param pvalue_threshold: The pvalue_threshold of this OperationEnrichResultsParameters.  # noqa: E501
        :type pvalue_threshold: float
        :param qnode_keys: The qnode_keys of this OperationEnrichResultsParameters.  # noqa: E501
        :type qnode_keys: List[str]
        """
        self.openapi_types = {
            'pvalue_threshold': float,
            'qnode_keys': List[str]
        }

        self.attribute_map = {
            'pvalue_threshold': 'pvalue_threshold',
            'qnode_keys': 'qnode_keys'
        }

        self._pvalue_threshold = pvalue_threshold
        self._qnode_keys = qnode_keys

    @classmethod
    def from_dict(cls, dikt) -> 'OperationEnrichResultsParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationEnrichResults_parameters of this OperationEnrichResultsParameters.  # noqa: E501
        :rtype: OperationEnrichResultsParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def pvalue_threshold(self):
        """Gets the pvalue_threshold of this OperationEnrichResultsParameters.

        The cutoff p-value for enrichment.  # noqa: E501

        :return: The pvalue_threshold of this OperationEnrichResultsParameters.
        :rtype: float
        """
        return self._pvalue_threshold

    @pvalue_threshold.setter
    def pvalue_threshold(self, pvalue_threshold):
        """Sets the pvalue_threshold of this OperationEnrichResultsParameters.

        The cutoff p-value for enrichment.  # noqa: E501

        :param pvalue_threshold: The pvalue_threshold of this OperationEnrichResultsParameters.
        :type pvalue_threshold: float
        """
        if pvalue_threshold is not None and pvalue_threshold > 1:  # noqa: E501
            raise ValueError("Invalid value for `pvalue_threshold`, must be a value less than or equal to `1`")  # noqa: E501
        if pvalue_threshold is not None and pvalue_threshold < 0:  # noqa: E501
            raise ValueError("Invalid value for `pvalue_threshold`, must be a value greater than or equal to `0`")  # noqa: E501

        self._pvalue_threshold = pvalue_threshold

    @property
    def qnode_keys(self):
        """Gets the qnode_keys of this OperationEnrichResultsParameters.

        If specified, then only knodes bound to these qnodes will be examined for enrichment and combination.  # noqa: E501

        :return: The qnode_keys of this OperationEnrichResultsParameters.
        :rtype: List[str]
        """
        return self._qnode_keys

    @qnode_keys.setter
    def qnode_keys(self, qnode_keys):
        """Sets the qnode_keys of this OperationEnrichResultsParameters.

        If specified, then only knodes bound to these qnodes will be examined for enrichment and combination.  # noqa: E501

        :param qnode_keys: The qnode_keys of this OperationEnrichResultsParameters.
        :type qnode_keys: List[str]
        """

        self._qnode_keys = qnode_keys


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_fill.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.any_type import AnyType
from openapi_server import util


class OperationFill(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFill - a model defined in OpenAPI

        :param id: The id of this OperationFill.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFill.  # noqa: E501
        :type parameters: OneOfobjectobject
        """
        self.openapi_types = {
            'id': str,
            'parameters': AnyType
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFill':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFill of this OperationFill.  # noqa: E501
        :rtype: OperationFill
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFill.


        :return: The id of this OperationFill.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFill.


        :param id: The id of this OperationFill.
        :type id: str
        """
        allowed_values = ["fill"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFill.


        :return: The parameters of this OperationFill.
        :rtype: OneOfobjectobject
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFill.


        :param parameters: The parameters of this OperationFill.
        :type parameters: OneOfobjectobject
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterKgraph(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterKgraph - a model defined in OpenAPI

        :param id: The id of this OperationFilterKgraph.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterKgraph.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraph':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraph of this OperationFilterKgraph.  # noqa: E501
        :rtype: OperationFilterKgraph
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterKgraph.


        :return: The id of this OperationFilterKgraph.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterKgraph.


        :param id: The id of this OperationFilterKgraph.
        :type id: str
        """
        allowed_values = ["filter_kgraph"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterKgraph.


        :return: The parameters of this OperationFilterKgraph.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterKgraph.


        :param parameters: The parameters of this OperationFilterKgraph.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_continuous_kedge_attribute.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_filter_kgraph_continuous_kedge_attribute_parameters import OperationFilterKgraphContinuousKedgeAttributeParameters
from openapi_server import util

from openapi_server.models.operation_filter_kgraph_continuous_kedge_attribute_parameters import OperationFilterKgraphContinuousKedgeAttributeParameters  # noqa: E501

class OperationFilterKgraphContinuousKedgeAttribute(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterKgraphContinuousKedgeAttribute - a model defined in OpenAPI

        :param id: The id of this OperationFilterKgraphContinuousKedgeAttribute.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterKgraphContinuousKedgeAttribute.  # noqa: E501
        :type parameters: OperationFilterKgraphContinuousKedgeAttributeParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationFilterKgraphContinuousKedgeAttributeParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphContinuousKedgeAttribute':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphContinuousKedgeAttribute of this OperationFilterKgraphContinuousKedgeAttribute.  # noqa: E501
        :rtype: OperationFilterKgraphContinuousKedgeAttribute
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterKgraphContinuousKedgeAttribute.


        :return: The id of this OperationFilterKgraphContinuousKedgeAttribute.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterKgraphContinuousKedgeAttribute.


        :param id: The id of this OperationFilterKgraphContinuousKedgeAttribute.
        :type id: str
        """
        allowed_values = ["filter_kgraph_continuous_kedge_attribute"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterKgraphContinuousKedgeAttribute.


        :return: The parameters of this OperationFilterKgraphContinuousKedgeAttribute.
        :rtype: OperationFilterKgraphContinuousKedgeAttributeParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterKgraphContinuousKedgeAttribute.


        :param parameters: The parameters of this OperationFilterKgraphContinuousKedgeAttribute.
        :type parameters: OperationFilterKgraphContinuousKedgeAttributeParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_continuous_kedge_attribute_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterKgraphContinuousKedgeAttributeParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, edge_attribute=None, qedge_keys=None, qnode_keys=[], remove_above_or_below=None, threshold=None):  # noqa: E501
        """OperationFilterKgraphContinuousKedgeAttributeParameters - a model defined in OpenAPI

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphContinuousKedgeAttributeParameters.  # noqa: E501
        :type edge_attribute: str
        :param qedge_keys: The qedge_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.  # noqa: E501
        :type qedge_keys: List[str]
        :param qnode_keys: The qnode_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.  # noqa: E501
        :type qnode_keys: List[str]
        :param remove_above_or_below: The remove_above_or_below of this OperationFilterKgraphContinuousKedgeAttributeParameters.  # noqa: E501
        :type remove_above_or_below: str
        :param threshold: The threshold of this OperationFilterKgraphContinuousKedgeAttributeParameters.  # noqa: E501
        :type threshold: float
        """
        self.openapi_types = {
            'edge_attribute': str,
            'qedge_keys': List[str],
            'qnode_keys': List[str],
            'remove_above_or_below': str,
            'threshold': float
        }

        self.attribute_map = {
            'edge_attribute': 'edge_attribute',
            'qedge_keys': 'qedge_keys',
            'qnode_keys': 'qnode_keys',
            'remove_above_or_below': 'remove_above_or_below',
            'threshold': 'threshold'
        }

        self._edge_attribute = edge_attribute
        self._qedge_keys = qedge_keys
        self._qnode_keys = qnode_keys
        self._remove_above_or_below = remove_above_or_below
        self._threshold = threshold

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphContinuousKedgeAttributeParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphContinuousKedgeAttribute_parameters of this OperationFilterKgraphContinuousKedgeAttributeParameters.  # noqa: E501
        :rtype: OperationFilterKgraphContinuousKedgeAttributeParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def edge_attribute(self):
        """Gets the edge_attribute of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :return: The edge_attribute of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :rtype: str
        """
        return self._edge_attribute

    @edge_attribute.setter
    def edge_attribute(self, edge_attribute):
        """Sets the edge_attribute of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :type edge_attribute: str
        """
        if edge_attribute is None:
            raise ValueError("Invalid value for `edge_attribute`, must not be `None`")  # noqa: E501

        self._edge_attribute = edge_attribute

    @property
    def qedge_keys(self):
        """Gets the qedge_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        This indicates if you only want to remove edges with specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :return: The qedge_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :rtype: List[str]
        """
        return self._qedge_keys

    @qedge_keys.setter
    def qedge_keys(self, qedge_keys):
        """Sets the qedge_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        This indicates if you only want to remove edges with specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :param qedge_keys: The qedge_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :type qedge_keys: List[str]
        """

        self._qedge_keys = qedge_keys

    @property
    def qnode_keys(self):
        """Gets the qnode_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering. Allows us to know what to do with the nodes connected to edges that are removed.  # noqa: E501

        :return: The qnode_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :rtype: List[str]
        """
        return self._qnode_keys

    @qnode_keys.setter
    def qnode_keys(self, qnode_keys):
        """Sets the qnode_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering. Allows us to know what to do with the nodes connected to edges that are removed.  # noqa: E501

        :param qnode_keys: The qnode_keys of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :type qnode_keys: List[str]
        """

        self._qnode_keys = qnode_keys

    @property
    def remove_above_or_below(self):
        """Gets the remove_above_or_below of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        Indicates whether to remove above or below the given threshold.  # noqa: E501

        :return: The remove_above_or_below of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :rtype: str
        """
        return self._remove_above_or_below

    @remove_above_or_below.setter
    def remove_above_or_below(self, remove_above_or_below):
        """Sets the remove_above_or_below of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        Indicates whether to remove above or below the given threshold.  # noqa: E501

        :param remove_above_or_below: The remove_above_or_below of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :type remove_above_or_below: str
        """
        allowed_values = ["above", "below"]  # noqa: E501
        if remove_above_or_below not in allowed_values:
            raise ValueError(
                "Invalid value for `remove_above_or_below` ({0}), must be one of {1}"
                .format(remove_above_or_below, allowed_values)
            )

        self._remove_above_or_below = remove_above_or_below

    @property
    def threshold(self):
        """Gets the threshold of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        The value to compare attribute values to.  # noqa: E501

        :return: The threshold of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :rtype: float
        """
        return self._threshold

    @threshold.setter
    def threshold(self, threshold):
        """Sets the threshold of this OperationFilterKgraphContinuousKedgeAttributeParameters.

        The value to compare attribute values to.  # noqa: E501

        :param threshold: The threshold of this OperationFilterKgraphContinuousKedgeAttributeParameters.
        :type threshold: float
        """
        if threshold is None:
            raise ValueError("Invalid value for `threshold`, must not be `None`")  # noqa: E501

        self._threshold = threshold


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_discrete_kedge_attribute.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_filter_kgraph_discrete_kedge_attribute_parameters import OperationFilterKgraphDiscreteKedgeAttributeParameters
from openapi_server import util

from openapi_server.models.operation_filter_kgraph_discrete_kedge_attribute_parameters import OperationFilterKgraphDiscreteKedgeAttributeParameters  # noqa: E501

class OperationFilterKgraphDiscreteKedgeAttribute(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterKgraphDiscreteKedgeAttribute - a model defined in OpenAPI

        :param id: The id of this OperationFilterKgraphDiscreteKedgeAttribute.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterKgraphDiscreteKedgeAttribute.  # noqa: E501
        :type parameters: OperationFilterKgraphDiscreteKedgeAttributeParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationFilterKgraphDiscreteKedgeAttributeParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphDiscreteKedgeAttribute':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphDiscreteKedgeAttribute of this OperationFilterKgraphDiscreteKedgeAttribute.  # noqa: E501
        :rtype: OperationFilterKgraphDiscreteKedgeAttribute
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterKgraphDiscreteKedgeAttribute.


        :return: The id of this OperationFilterKgraphDiscreteKedgeAttribute.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterKgraphDiscreteKedgeAttribute.


        :param id: The id of this OperationFilterKgraphDiscreteKedgeAttribute.
        :type id: str
        """
        allowed_values = ["filter_kgraph_discrete_kedge_attribute"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterKgraphDiscreteKedgeAttribute.


        :return: The parameters of this OperationFilterKgraphDiscreteKedgeAttribute.
        :rtype: OperationFilterKgraphDiscreteKedgeAttributeParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterKgraphDiscreteKedgeAttribute.


        :param parameters: The parameters of this OperationFilterKgraphDiscreteKedgeAttribute.
        :type parameters: OperationFilterKgraphDiscreteKedgeAttributeParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_discrete_kedge_attribute_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterKgraphDiscreteKedgeAttributeParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, edge_attribute=None, qedge_keys=None, qnode_keys=[], remove_value=None):  # noqa: E501
        """OperationFilterKgraphDiscreteKedgeAttributeParameters - a model defined in OpenAPI

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphDiscreteKedgeAttributeParameters.  # noqa: E501
        :type edge_attribute: str
        :param qedge_keys: The qedge_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.  # noqa: E501
        :type qedge_keys: List[str]
        :param qnode_keys: The qnode_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.  # noqa: E501
        :type qnode_keys: List[str]
        :param remove_value: The remove_value of this OperationFilterKgraphDiscreteKedgeAttributeParameters.  # noqa: E501
        :type remove_value: object
        """
        self.openapi_types = {
            'edge_attribute': str,
            'qedge_keys': List[str],
            'qnode_keys': List[str],
            'remove_value': object
        }

        self.attribute_map = {
            'edge_attribute': 'edge_attribute',
            'qedge_keys': 'qedge_keys',
            'qnode_keys': 'qnode_keys',
            'remove_value': 'remove_value'
        }

        self._edge_attribute = edge_attribute
        self._qedge_keys = qedge_keys
        self._qnode_keys = qnode_keys
        self._remove_value = remove_value

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphDiscreteKedgeAttributeParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphDiscreteKedgeAttribute_parameters of this OperationFilterKgraphDiscreteKedgeAttributeParameters.  # noqa: E501
        :rtype: OperationFilterKgraphDiscreteKedgeAttributeParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def edge_attribute(self):
        """Gets the edge_attribute of this OperationFilterKgraphDiscreteKedgeAttributeParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :return: The edge_attribute of this OperationFilterKgraphDiscreteKedgeAttributeParameters.
        :rtype: str
        """
        return self._edge_attribute

    @edge_attribute.setter
    def edge_attribute(self, edge_attribute):
        """Sets the edge_attribute of this OperationFilterKgraphDiscreteKedgeAttributeParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphDiscreteKedgeAttributeParameters.
        :type edge_attribute: str
        """
        if edge_attribute is None:
            raise ValueError("Invalid value for `edge_attribute`, must not be `None`")  # noqa: E501

        self._edge_attribute = edge_attribute

    @property
    def qedge_keys(self):
        """Gets the qedge_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.

        This indicates if you only want to remove edges with specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :return: The qedge_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.
        :rtype: List[str]
        """
        return self._qedge_keys

    @qedge_keys.setter
    def qedge_keys(self, qedge_keys):
        """Sets the qedge_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.

        This indicates if you only want to remove edges with specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :param qedge_keys: The qedge_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.
        :type qedge_keys: List[str]
        """

        self._qedge_keys = qedge_keys

    @property
    def qnode_keys(self):
        """Gets the qnode_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering. Allows us to know what to do with the nodes connected to edges that are removed  # noqa: E501

        :return: The qnode_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.
        :rtype: List[str]
        """
        return self._qnode_keys

    @qnode_keys.setter
    def qnode_keys(self, qnode_keys):
        """Sets the qnode_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering. Allows us to know what to do with the nodes connected to edges that are removed  # noqa: E501

        :param qnode_keys: The qnode_keys of this OperationFilterKgraphDiscreteKedgeAttributeParameters.
        :type qnode_keys: List[str]
        """

        self._qnode_keys = qnode_keys

    @property
    def remove_value(self):
        """Gets the remove_value of this OperationFilterKgraphDiscreteKedgeAttributeParameters.

        The value for which all edges containing this value in the specified edge_attribute should be removed.  # noqa: E501

        :return: The remove_value of this OperationFilterKgraphDiscreteKedgeAttributeParameters.
        :rtype: object
        """
        return self._remove_value

    @remove_value.setter
    def remove_value(self, remove_value):
        """Sets the remove_value of this OperationFilterKgraphDiscreteKedgeAttributeParameters.

        The value for which all edges containing this value in the specified edge_attribute should be removed.  # noqa: E501

        :param remove_value: The remove_value of this OperationFilterKgraphDiscreteKedgeAttributeParameters.
        :type remove_value: object
        """
        if remove_value is None:
            raise ValueError("Invalid value for `remove_value`, must not be `None`")  # noqa: E501

        self._remove_value = remove_value


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_discrete_knode_attribute.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_filter_kgraph_discrete_knode_attribute_parameters import OperationFilterKgraphDiscreteKnodeAttributeParameters
from openapi_server import util

from openapi_server.models.operation_filter_kgraph_discrete_knode_attribute_parameters import OperationFilterKgraphDiscreteKnodeAttributeParameters  # noqa: E501

class OperationFilterKgraphDiscreteKnodeAttribute(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterKgraphDiscreteKnodeAttribute - a model defined in OpenAPI

        :param id: The id of this OperationFilterKgraphDiscreteKnodeAttribute.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterKgraphDiscreteKnodeAttribute.  # noqa: E501
        :type parameters: OperationFilterKgraphDiscreteKnodeAttributeParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationFilterKgraphDiscreteKnodeAttributeParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphDiscreteKnodeAttribute':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphDiscreteKnodeAttribute of this OperationFilterKgraphDiscreteKnodeAttribute.  # noqa: E501
        :rtype: OperationFilterKgraphDiscreteKnodeAttribute
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterKgraphDiscreteKnodeAttribute.


        :return: The id of this OperationFilterKgraphDiscreteKnodeAttribute.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterKgraphDiscreteKnodeAttribute.


        :param id: The id of this OperationFilterKgraphDiscreteKnodeAttribute.
        :type id: str
        """
        allowed_values = ["filter_kgraph_discrete_knode_attribute"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterKgraphDiscreteKnodeAttribute.


        :return: The parameters of this OperationFilterKgraphDiscreteKnodeAttribute.
        :rtype: OperationFilterKgraphDiscreteKnodeAttributeParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterKgraphDiscreteKnodeAttribute.


        :param parameters: The parameters of this OperationFilterKgraphDiscreteKnodeAttribute.
        :type parameters: OperationFilterKgraphDiscreteKnodeAttributeParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_discrete_knode_attribute_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterKgraphDiscreteKnodeAttributeParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, node_attribute=None, qnode_keys=None, remove_value=None):  # noqa: E501
        """OperationFilterKgraphDiscreteKnodeAttributeParameters - a model defined in OpenAPI

        :param node_attribute: The node_attribute of this OperationFilterKgraphDiscreteKnodeAttributeParameters.  # noqa: E501
        :type node_attribute: str
        :param qnode_keys: The qnode_keys of this OperationFilterKgraphDiscreteKnodeAttributeParameters.  # noqa: E501
        :type qnode_keys: List[str]
        :param remove_value: The remove_value of this OperationFilterKgraphDiscreteKnodeAttributeParameters.  # noqa: E501
        :type remove_value: object
        """
        self.openapi_types = {
            'node_attribute': str,
            'qnode_keys': List[str],
            'remove_value': object
        }

        self.attribute_map = {
            'node_attribute': 'node_attribute',
            'qnode_keys': 'qnode_keys',
            'remove_value': 'remove_value'
        }

        self._node_attribute = node_attribute
        self._qnode_keys = qnode_keys
        self._remove_value = remove_value

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphDiscreteKnodeAttributeParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphDiscreteKnodeAttribute_parameters of this OperationFilterKgraphDiscreteKnodeAttributeParameters.  # noqa: E501
        :rtype: OperationFilterKgraphDiscreteKnodeAttributeParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def node_attribute(self):
        """Gets the node_attribute of this OperationFilterKgraphDiscreteKnodeAttributeParameters.

        The name of the node attribute to filter on.  # noqa: E501

        :return: The node_attribute of this OperationFilterKgraphDiscreteKnodeAttributeParameters.
        :rtype: str
        """
        return self._node_attribute

    @node_attribute.setter
    def node_attribute(self, node_attribute):
        """Sets the node_attribute of this OperationFilterKgraphDiscreteKnodeAttributeParameters.

        The name of the node attribute to filter on.  # noqa: E501

        :param node_attribute: The node_attribute of this OperationFilterKgraphDiscreteKnodeAttributeParameters.
        :type node_attribute: str
        """
        if node_attribute is None:
            raise ValueError("Invalid value for `node_attribute`, must not be `None`")  # noqa: E501

        self._node_attribute = node_attribute

    @property
    def qnode_keys(self):
        """Gets the qnode_keys of this OperationFilterKgraphDiscreteKnodeAttributeParameters.

        This indicates if you only want to remove nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, all nodes will be considered when filtering.  # noqa: E501

        :return: The qnode_keys of this OperationFilterKgraphDiscreteKnodeAttributeParameters.
        :rtype: List[str]
        """
        return self._qnode_keys

    @qnode_keys.setter
    def qnode_keys(self, qnode_keys):
        """Sets the qnode_keys of this OperationFilterKgraphDiscreteKnodeAttributeParameters.

        This indicates if you only want to remove nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, all nodes will be considered when filtering.  # noqa: E501

        :param qnode_keys: The qnode_keys of this OperationFilterKgraphDiscreteKnodeAttributeParameters.
        :type qnode_keys: List[str]
        """

        self._qnode_keys = qnode_keys

    @property
    def remove_value(self):
        """Gets the remove_value of this OperationFilterKgraphDiscreteKnodeAttributeParameters.

        The value for which all edges containing this value in the specified edge_attribute should be removed.  # noqa: E501

        :return: The remove_value of this OperationFilterKgraphDiscreteKnodeAttributeParameters.
        :rtype: object
        """
        return self._remove_value

    @remove_value.setter
    def remove_value(self, remove_value):
        """Sets the remove_value of this OperationFilterKgraphDiscreteKnodeAttributeParameters.

        The value for which all edges containing this value in the specified edge_attribute should be removed.  # noqa: E501

        :param remove_value: The remove_value of this OperationFilterKgraphDiscreteKnodeAttributeParameters.
        :type remove_value: object
        """
        if remove_value is None:
            raise ValueError("Invalid value for `remove_value`, must not be `None`")  # noqa: E501

        self._remove_value = remove_value


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_orphans.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterKgraphOrphans(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterKgraphOrphans - a model defined in OpenAPI

        :param id: The id of this OperationFilterKgraphOrphans.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterKgraphOrphans.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphOrphans':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphOrphans of this OperationFilterKgraphOrphans.  # noqa: E501
        :rtype: OperationFilterKgraphOrphans
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterKgraphOrphans.


        :return: The id of this OperationFilterKgraphOrphans.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterKgraphOrphans.


        :param id: The id of this OperationFilterKgraphOrphans.
        :type id: str
        """
        allowed_values = ["filter_kgraph_orphans"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterKgraphOrphans.


        :return: The parameters of this OperationFilterKgraphOrphans.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterKgraphOrphans.


        :param parameters: The parameters of this OperationFilterKgraphOrphans.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_percentile.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_filter_kgraph_percentile_parameters import OperationFilterKgraphPercentileParameters
from openapi_server import util

from openapi_server.models.operation_filter_kgraph_percentile_parameters import OperationFilterKgraphPercentileParameters  # noqa: E501

class OperationFilterKgraphPercentile(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterKgraphPercentile - a model defined in OpenAPI

        :param id: The id of this OperationFilterKgraphPercentile.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterKgraphPercentile.  # noqa: E501
        :type parameters: OperationFilterKgraphPercentileParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationFilterKgraphPercentileParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphPercentile':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphPercentile of this OperationFilterKgraphPercentile.  # noqa: E501
        :rtype: OperationFilterKgraphPercentile
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterKgraphPercentile.


        :return: The id of this OperationFilterKgraphPercentile.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterKgraphPercentile.


        :param id: The id of this OperationFilterKgraphPercentile.
        :type id: str
        """
        allowed_values = ["filter_kgraph_percentile"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterKgraphPercentile.


        :return: The parameters of this OperationFilterKgraphPercentile.
        :rtype: OperationFilterKgraphPercentileParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterKgraphPercentile.


        :param parameters: The parameters of this OperationFilterKgraphPercentile.
        :type parameters: OperationFilterKgraphPercentileParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_percentile_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterKgraphPercentileParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, edge_attribute=None, qedge_keys=None, qnode_keys=[], remove_above_or_below='below', threshold=95):  # noqa: E501
        """OperationFilterKgraphPercentileParameters - a model defined in OpenAPI

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphPercentileParameters.  # noqa: E501
        :type edge_attribute: str
        :param qedge_keys: The qedge_keys of this OperationFilterKgraphPercentileParameters.  # noqa: E501
        :type qedge_keys: List[str]
        :param qnode_keys: The qnode_keys of this OperationFilterKgraphPercentileParameters.  # noqa: E501
        :type qnode_keys: List[str]
        :param remove_above_or_below: The remove_above_or_below of this OperationFilterKgraphPercentileParameters.  # noqa: E501
        :type remove_above_or_below: str
        :param threshold: The threshold of this OperationFilterKgraphPercentileParameters.  # noqa: E501
        :type threshold: float
        """
        self.openapi_types = {
            'edge_attribute': str,
            'qedge_keys': List[str],
            'qnode_keys': List[str],
            'remove_above_or_below': str,
            'threshold': float
        }

        self.attribute_map = {
            'edge_attribute': 'edge_attribute',
            'qedge_keys': 'qedge_keys',
            'qnode_keys': 'qnode_keys',
            'remove_above_or_below': 'remove_above_or_below',
            'threshold': 'threshold'
        }

        self._edge_attribute = edge_attribute
        self._qedge_keys = qedge_keys
        self._qnode_keys = qnode_keys
        self._remove_above_or_below = remove_above_or_below
        self._threshold = threshold

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphPercentileParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphPercentile_parameters of this OperationFilterKgraphPercentileParameters.  # noqa: E501
        :rtype: OperationFilterKgraphPercentileParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def edge_attribute(self):
        """Gets the edge_attribute of this OperationFilterKgraphPercentileParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :return: The edge_attribute of this OperationFilterKgraphPercentileParameters.
        :rtype: str
        """
        return self._edge_attribute

    @edge_attribute.setter
    def edge_attribute(self, edge_attribute):
        """Sets the edge_attribute of this OperationFilterKgraphPercentileParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphPercentileParameters.
        :type edge_attribute: str
        """
        if edge_attribute is None:
            raise ValueError("Invalid value for `edge_attribute`, must not be `None`")  # noqa: E501

        self._edge_attribute = edge_attribute

    @property
    def qedge_keys(self):
        """Gets the qedge_keys of this OperationFilterKgraphPercentileParameters.

        This indicates if you only want to filter on specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :return: The qedge_keys of this OperationFilterKgraphPercentileParameters.
        :rtype: List[str]
        """
        return self._qedge_keys

    @qedge_keys.setter
    def qedge_keys(self, qedge_keys):
        """Sets the qedge_keys of this OperationFilterKgraphPercentileParameters.

        This indicates if you only want to filter on specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :param qedge_keys: The qedge_keys of this OperationFilterKgraphPercentileParameters.
        :type qedge_keys: List[str]
        """

        self._qedge_keys = qedge_keys

    @property
    def qnode_keys(self):
        """Gets the qnode_keys of this OperationFilterKgraphPercentileParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering. Allows us to know what to do with the nodes connected to edges that are removed.  # noqa: E501

        :return: The qnode_keys of this OperationFilterKgraphPercentileParameters.
        :rtype: List[str]
        """
        return self._qnode_keys

    @qnode_keys.setter
    def qnode_keys(self, qnode_keys):
        """Sets the qnode_keys of this OperationFilterKgraphPercentileParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering. Allows us to know what to do with the nodes connected to edges that are removed.  # noqa: E501

        :param qnode_keys: The qnode_keys of this OperationFilterKgraphPercentileParameters.
        :type qnode_keys: List[str]
        """

        self._qnode_keys = qnode_keys

    @property
    def remove_above_or_below(self):
        """Gets the remove_above_or_below of this OperationFilterKgraphPercentileParameters.

        Indicates whether to remove above or below the given threshold.  # noqa: E501

        :return: The remove_above_or_below of this OperationFilterKgraphPercentileParameters.
        :rtype: str
        """
        return self._remove_above_or_below

    @remove_above_or_below.setter
    def remove_above_or_below(self, remove_above_or_below):
        """Sets the remove_above_or_below of this OperationFilterKgraphPercentileParameters.

        Indicates whether to remove above or below the given threshold.  # noqa: E501

        :param remove_above_or_below: The remove_above_or_below of this OperationFilterKgraphPercentileParameters.
        :type remove_above_or_below: str
        """
        allowed_values = ["above", "below"]  # noqa: E501
        if remove_above_or_below not in allowed_values:
            raise ValueError(
                "Invalid value for `remove_above_or_below` ({0}), must be one of {1}"
                .format(remove_above_or_below, allowed_values)
            )

        self._remove_above_or_below = remove_above_or_below

    @property
    def threshold(self):
        """Gets the threshold of this OperationFilterKgraphPercentileParameters.

        The percentile to threshold on.  # noqa: E501

        :return: The threshold of this OperationFilterKgraphPercentileParameters.
        :rtype: float
        """
        return self._threshold

    @threshold.setter
    def threshold(self, threshold):
        """Sets the threshold of this OperationFilterKgraphPercentileParameters.

        The percentile to threshold on.  # noqa: E501

        :param threshold: The threshold of this OperationFilterKgraphPercentileParameters.
        :type threshold: float
        """
        if threshold is not None and threshold > 100:  # noqa: E501
            raise ValueError("Invalid value for `threshold`, must be a value less than or equal to `100`")  # noqa: E501
        if threshold is not None and threshold < 0:  # noqa: E501
            raise ValueError("Invalid value for `threshold`, must be a value greater than or equal to `0`")  # noqa: E501

        self._threshold = threshold


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_std_dev.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_filter_kgraph_std_dev_parameters import OperationFilterKgraphStdDevParameters
from openapi_server import util

from openapi_server.models.operation_filter_kgraph_std_dev_parameters import OperationFilterKgraphStdDevParameters  # noqa: E501

class OperationFilterKgraphStdDev(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterKgraphStdDev - a model defined in OpenAPI

        :param id: The id of this OperationFilterKgraphStdDev.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterKgraphStdDev.  # noqa: E501
        :type parameters: OperationFilterKgraphStdDevParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationFilterKgraphStdDevParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphStdDev':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphStdDev of this OperationFilterKgraphStdDev.  # noqa: E501
        :rtype: OperationFilterKgraphStdDev
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterKgraphStdDev.


        :return: The id of this OperationFilterKgraphStdDev.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterKgraphStdDev.


        :param id: The id of this OperationFilterKgraphStdDev.
        :type id: str
        """
        allowed_values = ["filter_kgraph_std_dev"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterKgraphStdDev.


        :return: The parameters of this OperationFilterKgraphStdDev.
        :rtype: OperationFilterKgraphStdDevParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterKgraphStdDev.


        :param parameters: The parameters of this OperationFilterKgraphStdDev.
        :type parameters: OperationFilterKgraphStdDevParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_std_dev_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterKgraphStdDevParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, edge_attribute=None, num_sigma=1, plus_or_minus_std_dev='plus', qedge_keys=None, qnode_keys=[], remove_above_or_below='below'):  # noqa: E501
        """OperationFilterKgraphStdDevParameters - a model defined in OpenAPI

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphStdDevParameters.  # noqa: E501
        :type edge_attribute: str
        :param num_sigma: The num_sigma of this OperationFilterKgraphStdDevParameters.  # noqa: E501
        :type num_sigma: float
        :param plus_or_minus_std_dev: The plus_or_minus_std_dev of this OperationFilterKgraphStdDevParameters.  # noqa: E501
        :type plus_or_minus_std_dev: str
        :param qedge_keys: The qedge_keys of this OperationFilterKgraphStdDevParameters.  # noqa: E501
        :type qedge_keys: List[str]
        :param qnode_keys: The qnode_keys of this OperationFilterKgraphStdDevParameters.  # noqa: E501
        :type qnode_keys: List[str]
        :param remove_above_or_below: The remove_above_or_below of this OperationFilterKgraphStdDevParameters.  # noqa: E501
        :type remove_above_or_below: str
        """
        self.openapi_types = {
            'edge_attribute': str,
            'num_sigma': float,
            'plus_or_minus_std_dev': str,
            'qedge_keys': List[str],
            'qnode_keys': List[str],
            'remove_above_or_below': str
        }

        self.attribute_map = {
            'edge_attribute': 'edge_attribute',
            'num_sigma': 'num_sigma',
            'plus_or_minus_std_dev': 'plus_or_minus_std_dev',
            'qedge_keys': 'qedge_keys',
            'qnode_keys': 'qnode_keys',
            'remove_above_or_below': 'remove_above_or_below'
        }

        self._edge_attribute = edge_attribute
        self._num_sigma = num_sigma
        self._plus_or_minus_std_dev = plus_or_minus_std_dev
        self._qedge_keys = qedge_keys
        self._qnode_keys = qnode_keys
        self._remove_above_or_below = remove_above_or_below

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphStdDevParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphStdDev_parameters of this OperationFilterKgraphStdDevParameters.  # noqa: E501
        :rtype: OperationFilterKgraphStdDevParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def edge_attribute(self):
        """Gets the edge_attribute of this OperationFilterKgraphStdDevParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :return: The edge_attribute of this OperationFilterKgraphStdDevParameters.
        :rtype: str
        """
        return self._edge_attribute

    @edge_attribute.setter
    def edge_attribute(self, edge_attribute):
        """Sets the edge_attribute of this OperationFilterKgraphStdDevParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphStdDevParameters.
        :type edge_attribute: str
        """
        if edge_attribute is None:
            raise ValueError("Invalid value for `edge_attribute`, must not be `None`")  # noqa: E501

        self._edge_attribute = edge_attribute

    @property
    def num_sigma(self):
        """Gets the num_sigma of this OperationFilterKgraphStdDevParameters.

        The number of standard deviations to threshold on.  # noqa: E501

        :return: The num_sigma of this OperationFilterKgraphStdDevParameters.
        :rtype: float
        """
        return self._num_sigma

    @num_sigma.setter
    def num_sigma(self, num_sigma):
        """Sets the num_sigma of this OperationFilterKgraphStdDevParameters.

        The number of standard deviations to threshold on.  # noqa: E501

        :param num_sigma: The num_sigma of this OperationFilterKgraphStdDevParameters.
        :type num_sigma: float
        """
        if num_sigma is not None and num_sigma < 0:  # noqa: E501
            raise ValueError("Invalid value for `num_sigma`, must be a value greater than or equal to `0`")  # noqa: E501

        self._num_sigma = num_sigma

    @property
    def plus_or_minus_std_dev(self):
        """Gets the plus_or_minus_std_dev of this OperationFilterKgraphStdDevParameters.

        Indicate whether or not the threshold should be found using plus or minus the standard deviation. E.g. when plus_or_minus_std_dev is set to plus will set the cutoff for filtering as the mean + num_sigma * std_dev while setting plus_or_minus_std_dev to minus will set the cutoff as the mean - num_sigma * std_dev.  # noqa: E501

        :return: The plus_or_minus_std_dev of this OperationFilterKgraphStdDevParameters.
        :rtype: str
        """
        return self._plus_or_minus_std_dev

    @plus_or_minus_std_dev.setter
    def plus_or_minus_std_dev(self, plus_or_minus_std_dev):
        """Sets the plus_or_minus_std_dev of this OperationFilterKgraphStdDevParameters.

        Indicate whether or not the threshold should be found using plus or minus the standard deviation. E.g. when plus_or_minus_std_dev is set to plus will set the cutoff for filtering as the mean + num_sigma * std_dev while setting plus_or_minus_std_dev to minus will set the cutoff as the mean - num_sigma * std_dev.  # noqa: E501

        :param plus_or_minus_std_dev: The plus_or_minus_std_dev of this OperationFilterKgraphStdDevParameters.
        :type plus_or_minus_std_dev: str
        """
        allowed_values = ["plus", "minus"]  # noqa: E501
        if plus_or_minus_std_dev not in allowed_values:
            raise ValueError(
                "Invalid value for `plus_or_minus_std_dev` ({0}), must be one of {1}"
                .format(plus_or_minus_std_dev, allowed_values)
            )

        self._plus_or_minus_std_dev = plus_or_minus_std_dev

    @property
    def qedge_keys(self):
        """Gets the qedge_keys of this OperationFilterKgraphStdDevParameters.

        This indicates if you only want to filter on specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :return: The qedge_keys of this OperationFilterKgraphStdDevParameters.
        :rtype: List[str]
        """
        return self._qedge_keys

    @qedge_keys.setter
    def qedge_keys(self, qedge_keys):
        """Sets the qedge_keys of this OperationFilterKgraphStdDevParameters.

        This indicates if you only want to filter on specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :param qedge_keys: The qedge_keys of this OperationFilterKgraphStdDevParameters.
        :type qedge_keys: List[str]
        """

        self._qedge_keys = qedge_keys

    @property
    def qnode_keys(self):
        """Gets the qnode_keys of this OperationFilterKgraphStdDevParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering.  # noqa: E501

        :return: The qnode_keys of this OperationFilterKgraphStdDevParameters.
        :rtype: List[str]
        """
        return self._qnode_keys

    @qnode_keys.setter
    def qnode_keys(self, qnode_keys):
        """Sets the qnode_keys of this OperationFilterKgraphStdDevParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering.  # noqa: E501

        :param qnode_keys: The qnode_keys of this OperationFilterKgraphStdDevParameters.
        :type qnode_keys: List[str]
        """

        self._qnode_keys = qnode_keys

    @property
    def remove_above_or_below(self):
        """Gets the remove_above_or_below of this OperationFilterKgraphStdDevParameters.

        Indictes whether to remove above or below the given threshold.  # noqa: E501

        :return: The remove_above_or_below of this OperationFilterKgraphStdDevParameters.
        :rtype: str
        """
        return self._remove_above_or_below

    @remove_above_or_below.setter
    def remove_above_or_below(self, remove_above_or_below):
        """Sets the remove_above_or_below of this OperationFilterKgraphStdDevParameters.

        Indictes whether to remove above or below the given threshold.  # noqa: E501

        :param remove_above_or_below: The remove_above_or_below of this OperationFilterKgraphStdDevParameters.
        :type remove_above_or_below: str
        """
        allowed_values = ["above", "below"]  # noqa: E501
        if remove_above_or_below not in allowed_values:
            raise ValueError(
                "Invalid value for `remove_above_or_below` ({0}), must be one of {1}"
                .format(remove_above_or_below, allowed_values)
            )

        self._remove_above_or_below = remove_above_or_below


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_top_n.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_filter_kgraph_top_n_parameters import OperationFilterKgraphTopNParameters
from openapi_server import util

from openapi_server.models.operation_filter_kgraph_top_n_parameters import OperationFilterKgraphTopNParameters  # noqa: E501

class OperationFilterKgraphTopN(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterKgraphTopN - a model defined in OpenAPI

        :param id: The id of this OperationFilterKgraphTopN.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterKgraphTopN.  # noqa: E501
        :type parameters: OperationFilterKgraphTopNParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationFilterKgraphTopNParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphTopN':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphTopN of this OperationFilterKgraphTopN.  # noqa: E501
        :rtype: OperationFilterKgraphTopN
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterKgraphTopN.


        :return: The id of this OperationFilterKgraphTopN.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterKgraphTopN.


        :param id: The id of this OperationFilterKgraphTopN.
        :type id: str
        """
        allowed_values = ["filter_kgraph_top_n"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterKgraphTopN.


        :return: The parameters of this OperationFilterKgraphTopN.
        :rtype: OperationFilterKgraphTopNParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterKgraphTopN.


        :param parameters: The parameters of this OperationFilterKgraphTopN.
        :type parameters: OperationFilterKgraphTopNParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_kgraph_top_n_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterKgraphTopNParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, edge_attribute=None, keep_top_or_bottom='top', max_edges=50, qedge_keys=None, qnode_keys=[]):  # noqa: E501
        """OperationFilterKgraphTopNParameters - a model defined in OpenAPI

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphTopNParameters.  # noqa: E501
        :type edge_attribute: str
        :param keep_top_or_bottom: The keep_top_or_bottom of this OperationFilterKgraphTopNParameters.  # noqa: E501
        :type keep_top_or_bottom: str
        :param max_edges: The max_edges of this OperationFilterKgraphTopNParameters.  # noqa: E501
        :type max_edges: int
        :param qedge_keys: The qedge_keys of this OperationFilterKgraphTopNParameters.  # noqa: E501
        :type qedge_keys: List[str]
        :param qnode_keys: The qnode_keys of this OperationFilterKgraphTopNParameters.  # noqa: E501
        :type qnode_keys: List[str]
        """
        self.openapi_types = {
            'edge_attribute': str,
            'keep_top_or_bottom': str,
            'max_edges': int,
            'qedge_keys': List[str],
            'qnode_keys': List[str]
        }

        self.attribute_map = {
            'edge_attribute': 'edge_attribute',
            'keep_top_or_bottom': 'keep_top_or_bottom',
            'max_edges': 'max_edges',
            'qedge_keys': 'qedge_keys',
            'qnode_keys': 'qnode_keys'
        }

        self._edge_attribute = edge_attribute
        self._keep_top_or_bottom = keep_top_or_bottom
        self._max_edges = max_edges
        self._qedge_keys = qedge_keys
        self._qnode_keys = qnode_keys

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterKgraphTopNParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterKgraphTopN_parameters of this OperationFilterKgraphTopNParameters.  # noqa: E501
        :rtype: OperationFilterKgraphTopNParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def edge_attribute(self):
        """Gets the edge_attribute of this OperationFilterKgraphTopNParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :return: The edge_attribute of this OperationFilterKgraphTopNParameters.
        :rtype: str
        """
        return self._edge_attribute

    @edge_attribute.setter
    def edge_attribute(self, edge_attribute):
        """Sets the edge_attribute of this OperationFilterKgraphTopNParameters.

        The name of the edge attribute to filter on.  # noqa: E501

        :param edge_attribute: The edge_attribute of this OperationFilterKgraphTopNParameters.
        :type edge_attribute: str
        """
        if edge_attribute is None:
            raise ValueError("Invalid value for `edge_attribute`, must not be `None`")  # noqa: E501

        self._edge_attribute = edge_attribute

    @property
    def keep_top_or_bottom(self):
        """Gets the keep_top_or_bottom of this OperationFilterKgraphTopNParameters.

        Indicate whether or not the the top or bottom n values should be kept.  # noqa: E501

        :return: The keep_top_or_bottom of this OperationFilterKgraphTopNParameters.
        :rtype: str
        """
        return self._keep_top_or_bottom

    @keep_top_or_bottom.setter
    def keep_top_or_bottom(self, keep_top_or_bottom):
        """Sets the keep_top_or_bottom of this OperationFilterKgraphTopNParameters.

        Indicate whether or not the the top or bottom n values should be kept.  # noqa: E501

        :param keep_top_or_bottom: The keep_top_or_bottom of this OperationFilterKgraphTopNParameters.
        :type keep_top_or_bottom: str
        """
        allowed_values = ["top", "bottom"]  # noqa: E501
        if keep_top_or_bottom not in allowed_values:
            raise ValueError(
                "Invalid value for `keep_top_or_bottom` ({0}), must be one of {1}"
                .format(keep_top_or_bottom, allowed_values)
            )

        self._keep_top_or_bottom = keep_top_or_bottom

    @property
    def max_edges(self):
        """Gets the max_edges of this OperationFilterKgraphTopNParameters.

        The number of edges to keep.  # noqa: E501

        :return: The max_edges of this OperationFilterKgraphTopNParameters.
        :rtype: int
        """
        return self._max_edges

    @max_edges.setter
    def max_edges(self, max_edges):
        """Sets the max_edges of this OperationFilterKgraphTopNParameters.

        The number of edges to keep.  # noqa: E501

        :param max_edges: The max_edges of this OperationFilterKgraphTopNParameters.
        :type max_edges: int
        """
        if max_edges is not None and max_edges < 0:  # noqa: E501
            raise ValueError("Invalid value for `max_edges`, must be a value greater than or equal to `0`")  # noqa: E501

        self._max_edges = max_edges

    @property
    def qedge_keys(self):
        """Gets the qedge_keys of this OperationFilterKgraphTopNParameters.

        This indicates if you only want to filter on specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :return: The qedge_keys of this OperationFilterKgraphTopNParameters.
        :rtype: List[str]
        """
        return self._qedge_keys

    @qedge_keys.setter
    def qedge_keys(self, qedge_keys):
        """Sets the qedge_keys of this OperationFilterKgraphTopNParameters.

        This indicates if you only want to filter on specific edge_keys. If not provided or empty, all edges will be filtered on.  # noqa: E501

        :param qedge_keys: The qedge_keys of this OperationFilterKgraphTopNParameters.
        :type qedge_keys: List[str]
        """

        self._qedge_keys = qedge_keys

    @property
    def qnode_keys(self):
        """Gets the qnode_keys of this OperationFilterKgraphTopNParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering. Allows us to know what to do with the nodes connected to edges that are removed.  # noqa: E501

        :return: The qnode_keys of this OperationFilterKgraphTopNParameters.
        :rtype: List[str]
        """
        return self._qnode_keys

    @qnode_keys.setter
    def qnode_keys(self, qnode_keys):
        """Sets the qnode_keys of this OperationFilterKgraphTopNParameters.

        This indicates if you only want nodes corresponding to a specific list of qnode_keys to be removed. If not provided or empty, no nodes will be removed when filtering. Allows us to know what to do with the nodes connected to edges that are removed.  # noqa: E501

        :param qnode_keys: The qnode_keys of this OperationFilterKgraphTopNParameters.
        :type qnode_keys: List[str]
        """

        self._qnode_keys = qnode_keys


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_results.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterResults(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterResults - a model defined in OpenAPI

        :param id: The id of this OperationFilterResults.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterResults.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterResults':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterResults of this OperationFilterResults.  # noqa: E501
        :rtype: OperationFilterResults
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterResults.


        :return: The id of this OperationFilterResults.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterResults.


        :param id: The id of this OperationFilterResults.
        :type id: str
        """
        allowed_values = ["filter_results"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterResults.


        :return: The parameters of this OperationFilterResults.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterResults.


        :param parameters: The parameters of this OperationFilterResults.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_results_top_n.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_filter_results_top_n_parameters import OperationFilterResultsTopNParameters
from openapi_server import util

from openapi_server.models.operation_filter_results_top_n_parameters import OperationFilterResultsTopNParameters  # noqa: E501

class OperationFilterResultsTopN(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationFilterResultsTopN - a model defined in OpenAPI

        :param id: The id of this OperationFilterResultsTopN.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationFilterResultsTopN.  # noqa: E501
        :type parameters: OperationFilterResultsTopNParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationFilterResultsTopNParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterResultsTopN':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterResultsTopN of this OperationFilterResultsTopN.  # noqa: E501
        :rtype: OperationFilterResultsTopN
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationFilterResultsTopN.


        :return: The id of this OperationFilterResultsTopN.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationFilterResultsTopN.


        :param id: The id of this OperationFilterResultsTopN.
        :type id: str
        """
        allowed_values = ["filter_results_top_n"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationFilterResultsTopN.


        :return: The parameters of this OperationFilterResultsTopN.
        :rtype: OperationFilterResultsTopNParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationFilterResultsTopN.


        :param parameters: The parameters of this OperationFilterResultsTopN.
        :type parameters: OperationFilterResultsTopNParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_filter_results_top_n_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationFilterResultsTopNParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, max_results=None):  # noqa: E501
        """OperationFilterResultsTopNParameters - a model defined in OpenAPI

        :param max_results: The max_results of this OperationFilterResultsTopNParameters.  # noqa: E501
        :type max_results: int
        """
        self.openapi_types = {
            'max_results': int
        }

        self.attribute_map = {
            'max_results': 'max_results'
        }

        self._max_results = max_results

    @classmethod
    def from_dict(cls, dikt) -> 'OperationFilterResultsTopNParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationFilterResultsTopN_parameters of this OperationFilterResultsTopNParameters.  # noqa: E501
        :rtype: OperationFilterResultsTopNParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def max_results(self):
        """Gets the max_results of this OperationFilterResultsTopNParameters.

        The maximum number of results to return.  # noqa: E501

        :return: The max_results of this OperationFilterResultsTopNParameters.
        :rtype: int
        """
        return self._max_results

    @max_results.setter
    def max_results(self, max_results):
        """Sets the max_results of this OperationFilterResultsTopNParameters.

        The maximum number of results to return.  # noqa: E501

        :param max_results: The max_results of this OperationFilterResultsTopNParameters.
        :type max_results: int
        """
        if max_results is None:
            raise ValueError("Invalid value for `max_results`, must not be `None`")  # noqa: E501
        if max_results is not None and max_results < 0:  # noqa: E501
            raise ValueError("Invalid value for `max_results`, must be a value greater than or equal to `0`")  # noqa: E501

        self._max_results = max_results


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_lookup.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationLookup(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationLookup - a model defined in OpenAPI

        :param id: The id of this OperationLookup.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationLookup.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationLookup':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationLookup of this OperationLookup.  # noqa: E501
        :rtype: OperationLookup
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationLookup.


        :return: The id of this OperationLookup.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationLookup.


        :param id: The id of this OperationLookup.
        :type id: str
        """
        allowed_values = ["lookup"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationLookup.


        :return: The parameters of this OperationLookup.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationLookup.


        :param parameters: The parameters of this OperationLookup.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_lookup_and_score.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.one_ofobjectobject import OneOfobjectobject
from openapi_server import util

from openapi_server.models.one_ofobjectobject import OneOfobjectobject  # noqa: E501

class OperationLookupAndScore(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None, runner_parameters=None, unique=True):  # noqa: E501
        """OperationLookupAndScore - a model defined in OpenAPI

        :param id: The id of this OperationLookupAndScore.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationLookupAndScore.  # noqa: E501
        :type parameters: object
        :param runner_parameters: The runner_parameters of this OperationLookupAndScore.  # noqa: E501
        :type runner_parameters: OneOfobjectobject
        :param unique: The unique of this OperationLookupAndScore.  # noqa: E501
        :type unique: bool
        """
        self.openapi_types = {
            'id': str,
            'parameters': object,
            'runner_parameters': OneOfobjectobject,
            'unique': bool
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters',
            'runner_parameters': 'runner_parameters',
            'unique': 'unique'
        }

        self._id = id
        self._parameters = parameters
        self._runner_parameters = runner_parameters
        self._unique = unique

    @classmethod
    def from_dict(cls, dikt) -> 'OperationLookupAndScore':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationLookupAndScore of this OperationLookupAndScore.  # noqa: E501
        :rtype: OperationLookupAndScore
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationLookupAndScore.


        :return: The id of this OperationLookupAndScore.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationLookupAndScore.


        :param id: The id of this OperationLookupAndScore.
        :type id: str
        """
        allowed_values = ["lookup_and_score"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationLookupAndScore.


        :return: The parameters of this OperationLookupAndScore.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationLookupAndScore.


        :param parameters: The parameters of this OperationLookupAndScore.
        :type parameters: object
        """

        self._parameters = parameters

    @property
    def runner_parameters(self):
        """Gets the runner_parameters of this OperationLookupAndScore.


        :return: The runner_parameters of this OperationLookupAndScore.
        :rtype: OneOfobjectobject
        """
        return self._runner_parameters

    @runner_parameters.setter
    def runner_parameters(self, runner_parameters):
        """Sets the runner_parameters of this OperationLookupAndScore.


        :param runner_parameters: The runner_parameters of this OperationLookupAndScore.
        :type runner_parameters: OneOfobjectobject
        """

        self._runner_parameters = runner_parameters

    @property
    def unique(self):
        """Gets the unique of this OperationLookupAndScore.


        :return: The unique of this OperationLookupAndScore.
        :rtype: bool
        """
        return self._unique

    @unique.setter
    def unique(self, unique):
        """Sets the unique of this OperationLookupAndScore.


        :param unique: The unique of this OperationLookupAndScore.
        :type unique: bool
        """

        self._unique = unique


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_overlay.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationOverlay(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationOverlay - a model defined in OpenAPI

        :param id: The id of this OperationOverlay.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationOverlay.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationOverlay':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationOverlay of this OperationOverlay.  # noqa: E501
        :rtype: OperationOverlay
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationOverlay.


        :return: The id of this OperationOverlay.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationOverlay.


        :param id: The id of this OperationOverlay.
        :type id: str
        """
        allowed_values = ["overlay"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationOverlay.


        :return: The parameters of this OperationOverlay.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationOverlay.


        :param parameters: The parameters of this OperationOverlay.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_overlay_compute_jaccard.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_overlay_compute_jaccard_parameters import OperationOverlayComputeJaccardParameters
from openapi_server import util

from openapi_server.models.operation_overlay_compute_jaccard_parameters import OperationOverlayComputeJaccardParameters  # noqa: E501

class OperationOverlayComputeJaccard(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationOverlayComputeJaccard - a model defined in OpenAPI

        :param id: The id of this OperationOverlayComputeJaccard.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationOverlayComputeJaccard.  # noqa: E501
        :type parameters: OperationOverlayComputeJaccardParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationOverlayComputeJaccardParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationOverlayComputeJaccard':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationOverlayComputeJaccard of this OperationOverlayComputeJaccard.  # noqa: E501
        :rtype: OperationOverlayComputeJaccard
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationOverlayComputeJaccard.


        :return: The id of this OperationOverlayComputeJaccard.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationOverlayComputeJaccard.


        :param id: The id of this OperationOverlayComputeJaccard.
        :type id: str
        """
        allowed_values = ["overlay_compute_jaccard"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationOverlayComputeJaccard.


        :return: The parameters of this OperationOverlayComputeJaccard.
        :rtype: OperationOverlayComputeJaccardParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationOverlayComputeJaccard.


        :param parameters: The parameters of this OperationOverlayComputeJaccard.
        :type parameters: OperationOverlayComputeJaccardParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_overlay_compute_jaccard_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationOverlayComputeJaccardParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, end_node_keys=None, intermediate_node_key=None, virtual_relation_label=None):  # noqa: E501
        """OperationOverlayComputeJaccardParameters - a model defined in OpenAPI

        :param end_node_keys: The end_node_keys of this OperationOverlayComputeJaccardParameters.  # noqa: E501
        :type end_node_keys: List[str]
        :param intermediate_node_key: The intermediate_node_key of this OperationOverlayComputeJaccardParameters.  # noqa: E501
        :type intermediate_node_key: str
        :param virtual_relation_label: The virtual_relation_label of this OperationOverlayComputeJaccardParameters.  # noqa: E501
        :type virtual_relation_label: str
        """
        self.openapi_types = {
            'end_node_keys': List[str],
            'intermediate_node_key': str,
            'virtual_relation_label': str
        }

        self.attribute_map = {
            'end_node_keys': 'end_node_keys',
            'intermediate_node_key': 'intermediate_node_key',
            'virtual_relation_label': 'virtual_relation_label'
        }

        self._end_node_keys = end_node_keys
        self._intermediate_node_key = intermediate_node_key
        self._virtual_relation_label = virtual_relation_label

    @classmethod
    def from_dict(cls, dikt) -> 'OperationOverlayComputeJaccardParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationOverlayComputeJaccard_parameters of this OperationOverlayComputeJaccardParameters.  # noqa: E501
        :rtype: OperationOverlayComputeJaccardParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def end_node_keys(self):
        """Gets the end_node_keys of this OperationOverlayComputeJaccardParameters.

        A list of qnode keys specifying the ending nodes.  # noqa: E501

        :return: The end_node_keys of this OperationOverlayComputeJaccardParameters.
        :rtype: List[str]
        """
        return self._end_node_keys

    @end_node_keys.setter
    def end_node_keys(self, end_node_keys):
        """Sets the end_node_keys of this OperationOverlayComputeJaccardParameters.

        A list of qnode keys specifying the ending nodes.  # noqa: E501

        :param end_node_keys: The end_node_keys of this OperationOverlayComputeJaccardParameters.
        :type end_node_keys: List[str]
        """
        if end_node_keys is None:
            raise ValueError("Invalid value for `end_node_keys`, must not be `None`")  # noqa: E501

        self._end_node_keys = end_node_keys

    @property
    def intermediate_node_key(self):
        """Gets the intermediate_node_key of this OperationOverlayComputeJaccardParameters.

        A qnode key specifying the intermediate node.  # noqa: E501

        :return: The intermediate_node_key of this OperationOverlayComputeJaccardParameters.
        :rtype: str
        """
        return self._intermediate_node_key

    @intermediate_node_key.setter
    def intermediate_node_key(self, intermediate_node_key):
        """Sets the intermediate_node_key of this OperationOverlayComputeJaccardParameters.

        A qnode key specifying the intermediate node.  # noqa: E501

        :param intermediate_node_key: The intermediate_node_key of this OperationOverlayComputeJaccardParameters.
        :type intermediate_node_key: str
        """
        if intermediate_node_key is None:
            raise ValueError("Invalid value for `intermediate_node_key`, must not be `None`")  # noqa: E501

        self._intermediate_node_key = intermediate_node_key

    @property
    def virtual_relation_label(self):
        """Gets the virtual_relation_label of this OperationOverlayComputeJaccardParameters.

        The key of the query graph edge that corresponds to the knowledge graph edges that were added by this operation.  # noqa: E501

        :return: The virtual_relation_label of this OperationOverlayComputeJaccardParameters.
        :rtype: str
        """
        return self._virtual_relation_label

    @virtual_relation_label.setter
    def virtual_relation_label(self, virtual_relation_label):
        """Sets the virtual_relation_label of this OperationOverlayComputeJaccardParameters.

        The key of the query graph edge that corresponds to the knowledge graph edges that were added by this operation.  # noqa: E501

        :param virtual_relation_label: The virtual_relation_label of this OperationOverlayComputeJaccardParameters.
        :type virtual_relation_label: str
        """
        if virtual_relation_label is None:
            raise ValueError("Invalid value for `virtual_relation_label`, must not be `None`")  # noqa: E501

        self._virtual_relation_label = virtual_relation_label


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_overlay_compute_ngd.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_overlay_compute_ngd_parameters import OperationOverlayComputeNgdParameters
from openapi_server import util

from openapi_server.models.operation_overlay_compute_ngd_parameters import OperationOverlayComputeNgdParameters  # noqa: E501

class OperationOverlayComputeNgd(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationOverlayComputeNgd - a model defined in OpenAPI

        :param id: The id of this OperationOverlayComputeNgd.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationOverlayComputeNgd.  # noqa: E501
        :type parameters: OperationOverlayComputeNgdParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationOverlayComputeNgdParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationOverlayComputeNgd':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationOverlayComputeNgd of this OperationOverlayComputeNgd.  # noqa: E501
        :rtype: OperationOverlayComputeNgd
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationOverlayComputeNgd.


        :return: The id of this OperationOverlayComputeNgd.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationOverlayComputeNgd.


        :param id: The id of this OperationOverlayComputeNgd.
        :type id: str
        """
        allowed_values = ["overlay_compute_ngd"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationOverlayComputeNgd.


        :return: The parameters of this OperationOverlayComputeNgd.
        :rtype: OperationOverlayComputeNgdParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationOverlayComputeNgd.


        :param parameters: The parameters of this OperationOverlayComputeNgd.
        :type parameters: OperationOverlayComputeNgdParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_overlay_compute_ngd_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationOverlayComputeNgdParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, qnode_keys=None, virtual_relation_label=None):  # noqa: E501
        """OperationOverlayComputeNgdParameters - a model defined in OpenAPI

        :param qnode_keys: The qnode_keys of this OperationOverlayComputeNgdParameters.  # noqa: E501
        :type qnode_keys: List[str]
        :param virtual_relation_label: The virtual_relation_label of this OperationOverlayComputeNgdParameters.  # noqa: E501
        :type virtual_relation_label: str
        """
        self.openapi_types = {
            'qnode_keys': List[str],
            'virtual_relation_label': str
        }

        self.attribute_map = {
            'qnode_keys': 'qnode_keys',
            'virtual_relation_label': 'virtual_relation_label'
        }

        self._qnode_keys = qnode_keys
        self._virtual_relation_label = virtual_relation_label

    @classmethod
    def from_dict(cls, dikt) -> 'OperationOverlayComputeNgdParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationOverlayComputeNgd_parameters of this OperationOverlayComputeNgdParameters.  # noqa: E501
        :rtype: OperationOverlayComputeNgdParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def qnode_keys(self):
        """Gets the qnode_keys of this OperationOverlayComputeNgdParameters.

        A list of qnode keys to overlay pairwise edges onto. Must be be a list of at least 2 valid qnodes.  # noqa: E501

        :return: The qnode_keys of this OperationOverlayComputeNgdParameters.
        :rtype: List[str]
        """
        return self._qnode_keys

    @qnode_keys.setter
    def qnode_keys(self, qnode_keys):
        """Sets the qnode_keys of this OperationOverlayComputeNgdParameters.

        A list of qnode keys to overlay pairwise edges onto. Must be be a list of at least 2 valid qnodes.  # noqa: E501

        :param qnode_keys: The qnode_keys of this OperationOverlayComputeNgdParameters.
        :type qnode_keys: List[str]
        """
        if qnode_keys is None:
            raise ValueError("Invalid value for `qnode_keys`, must not be `None`")  # noqa: E501

        self._qnode_keys = qnode_keys

    @property
    def virtual_relation_label(self):
        """Gets the virtual_relation_label of this OperationOverlayComputeNgdParameters.

        An label to help identify the virtual edge in the relation field  # noqa: E501

        :return: The virtual_relation_label of this OperationOverlayComputeNgdParameters.
        :rtype: str
        """
        return self._virtual_relation_label

    @virtual_relation_label.setter
    def virtual_relation_label(self, virtual_relation_label):
        """Sets the virtual_relation_label of this OperationOverlayComputeNgdParameters.

        An label to help identify the virtual edge in the relation field  # noqa: E501

        :param virtual_relation_label: The virtual_relation_label of this OperationOverlayComputeNgdParameters.
        :type virtual_relation_label: str
        """
        if virtual_relation_label is None:
            raise ValueError("Invalid value for `virtual_relation_label`, must not be `None`")  # noqa: E501

        self._virtual_relation_label = virtual_relation_label


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_overlay_connect_knodes.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationOverlayConnectKnodes(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationOverlayConnectKnodes - a model defined in OpenAPI

        :param id: The id of this OperationOverlayConnectKnodes.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationOverlayConnectKnodes.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationOverlayConnectKnodes':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationOverlayConnectKnodes of this OperationOverlayConnectKnodes.  # noqa: E501
        :rtype: OperationOverlayConnectKnodes
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationOverlayConnectKnodes.


        :return: The id of this OperationOverlayConnectKnodes.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationOverlayConnectKnodes.


        :param id: The id of this OperationOverlayConnectKnodes.
        :type id: str
        """
        allowed_values = ["overlay_connect_knodes"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationOverlayConnectKnodes.


        :return: The parameters of this OperationOverlayConnectKnodes.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationOverlayConnectKnodes.


        :param parameters: The parameters of this OperationOverlayConnectKnodes.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_overlay_fisher_exact_test.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_overlay_fisher_exact_test_parameters import OperationOverlayFisherExactTestParameters
from openapi_server import util

from openapi_server.models.operation_overlay_fisher_exact_test_parameters import OperationOverlayFisherExactTestParameters  # noqa: E501

class OperationOverlayFisherExactTest(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationOverlayFisherExactTest - a model defined in OpenAPI

        :param id: The id of this OperationOverlayFisherExactTest.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationOverlayFisherExactTest.  # noqa: E501
        :type parameters: OperationOverlayFisherExactTestParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationOverlayFisherExactTestParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationOverlayFisherExactTest':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationOverlayFisherExactTest of this OperationOverlayFisherExactTest.  # noqa: E501
        :rtype: OperationOverlayFisherExactTest
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationOverlayFisherExactTest.


        :return: The id of this OperationOverlayFisherExactTest.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationOverlayFisherExactTest.


        :param id: The id of this OperationOverlayFisherExactTest.
        :type id: str
        """
        allowed_values = ["overlay_fisher_exact_test"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationOverlayFisherExactTest.


        :return: The parameters of this OperationOverlayFisherExactTest.
        :rtype: OperationOverlayFisherExactTestParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationOverlayFisherExactTest.


        :param parameters: The parameters of this OperationOverlayFisherExactTest.
        :type parameters: OperationOverlayFisherExactTestParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_overlay_fisher_exact_test_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationOverlayFisherExactTestParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, object_qnode_key=None, rel_edge_key=None, subject_qnode_key=None, virtual_relation_label=None):  # noqa: E501
        """OperationOverlayFisherExactTestParameters - a model defined in OpenAPI

        :param object_qnode_key: The object_qnode_key of this OperationOverlayFisherExactTestParameters.  # noqa: E501
        :type object_qnode_key: str
        :param rel_edge_key: The rel_edge_key of this OperationOverlayFisherExactTestParameters.  # noqa: E501
        :type rel_edge_key: str
        :param subject_qnode_key: The subject_qnode_key of this OperationOverlayFisherExactTestParameters.  # noqa: E501
        :type subject_qnode_key: str
        :param virtual_relation_label: The virtual_relation_label of this OperationOverlayFisherExactTestParameters.  # noqa: E501
        :type virtual_relation_label: str
        """
        self.openapi_types = {
            'object_qnode_key': str,
            'rel_edge_key': str,
            'subject_qnode_key': str,
            'virtual_relation_label': str
        }

        self.attribute_map = {
            'object_qnode_key': 'object_qnode_key',
            'rel_edge_key': 'rel_edge_key',
            'subject_qnode_key': 'subject_qnode_key',
            'virtual_relation_label': 'virtual_relation_label'
        }

        self._object_qnode_key = object_qnode_key
        self._rel_edge_key = rel_edge_key
        self._subject_qnode_key = subject_qnode_key
        self._virtual_relation_label = virtual_relation_label

    @classmethod
    def from_dict(cls, dikt) -> 'OperationOverlayFisherExactTestParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationOverlayFisherExactTest_parameters of this OperationOverlayFisherExactTestParameters.  # noqa: E501
        :rtype: OperationOverlayFisherExactTestParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def object_qnode_key(self):
        """Gets the object_qnode_key of this OperationOverlayFisherExactTestParameters.

        A specific object query node id.  # noqa: E501

        :return: The object_qnode_key of this OperationOverlayFisherExactTestParameters.
        :rtype: str
        """
        return self._object_qnode_key

    @object_qnode_key.setter
    def object_qnode_key(self, object_qnode_key):
        """Sets the object_qnode_key of this OperationOverlayFisherExactTestParameters.

        A specific object query node id.  # noqa: E501

        :param object_qnode_key: The object_qnode_key of this OperationOverlayFisherExactTestParameters.
        :type object_qnode_key: str
        """
        if object_qnode_key is None:
            raise ValueError("Invalid value for `object_qnode_key`, must not be `None`")  # noqa: E501

        self._object_qnode_key = object_qnode_key

    @property
    def rel_edge_key(self):
        """Gets the rel_edge_key of this OperationOverlayFisherExactTestParameters.

        A specific Qedge id connected to both subject nodes and object nodes in message KG (optional, otherwise all edges connected to both subject nodes and object nodes in message KG are considered).  # noqa: E501

        :return: The rel_edge_key of this OperationOverlayFisherExactTestParameters.
        :rtype: str
        """
        return self._rel_edge_key

    @rel_edge_key.setter
    def rel_edge_key(self, rel_edge_key):
        """Sets the rel_edge_key of this OperationOverlayFisherExactTestParameters.

        A specific Qedge id connected to both subject nodes and object nodes in message KG (optional, otherwise all edges connected to both subject nodes and object nodes in message KG are considered).  # noqa: E501

        :param rel_edge_key: The rel_edge_key of this OperationOverlayFisherExactTestParameters.
        :type rel_edge_key: str
        """

        self._rel_edge_key = rel_edge_key

    @property
    def subject_qnode_key(self):
        """Gets the subject_qnode_key of this OperationOverlayFisherExactTestParameters.

        A specific subject query node id.  # noqa: E501

        :return: The subject_qnode_key of this OperationOverlayFisherExactTestParameters.
        :rtype: str
        """
        return self._subject_qnode_key

    @subject_qnode_key.setter
    def subject_qnode_key(self, subject_qnode_key):
        """Sets the subject_qnode_key of this OperationOverlayFisherExactTestParameters.

        A specific subject query node id.  # noqa: E501

        :param subject_qnode_key: The subject_qnode_key of this OperationOverlayFisherExactTestParameters.
        :type subject_qnode_key: str
        """
        if subject_qnode_key is None:
            raise ValueError("Invalid value for `subject_qnode_key`, must not be `None`")  # noqa: E501

        self._subject_qnode_key = subject_qnode_key

    @property
    def virtual_relation_label(self):
        """Gets the virtual_relation_label of this OperationOverlayFisherExactTestParameters.

        An label to help identify the virtual edge.  # noqa: E501

        :return: The virtual_relation_label of this OperationOverlayFisherExactTestParameters.
        :rtype: str
        """
        return self._virtual_relation_label

    @virtual_relation_label.setter
    def virtual_relation_label(self, virtual_relation_label):
        """Sets the virtual_relation_label of this OperationOverlayFisherExactTestParameters.

        An label to help identify the virtual edge.  # noqa: E501

        :param virtual_relation_label: The virtual_relation_label of this OperationOverlayFisherExactTestParameters.
        :type virtual_relation_label: str
        """
        if virtual_relation_label is None:
            raise ValueError("Invalid value for `virtual_relation_label`, must not be `None`")  # noqa: E501

        self._virtual_relation_label = virtual_relation_label


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_restate.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationRestate(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationRestate - a model defined in OpenAPI

        :param id: The id of this OperationRestate.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationRestate.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationRestate':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationRestate of this OperationRestate.  # noqa: E501
        :rtype: OperationRestate
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationRestate.


        :return: The id of this OperationRestate.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationRestate.


        :param id: The id of this OperationRestate.
        :type id: str
        """
        allowed_values = ["restate"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationRestate.


        :return: The parameters of this OperationRestate.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationRestate.


        :param parameters: The parameters of this OperationRestate.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_score.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationScore(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationScore - a model defined in OpenAPI

        :param id: The id of this OperationScore.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationScore.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationScore':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationScore of this OperationScore.  # noqa: E501
        :rtype: OperationScore
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationScore.


        :return: The id of this OperationScore.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationScore.


        :param id: The id of this OperationScore.
        :type id: str
        """
        allowed_values = ["score"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationScore.


        :return: The parameters of this OperationScore.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationScore.


        :param parameters: The parameters of this OperationScore.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_sort_results.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationSortResults(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationSortResults - a model defined in OpenAPI

        :param id: The id of this OperationSortResults.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationSortResults.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationSortResults':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationSortResults of this OperationSortResults.  # noqa: E501
        :rtype: OperationSortResults
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationSortResults.


        :return: The id of this OperationSortResults.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationSortResults.


        :param id: The id of this OperationSortResults.
        :type id: str
        """
        allowed_values = ["sort_results"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationSortResults.


        :return: The parameters of this OperationSortResults.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationSortResults.


        :param parameters: The parameters of this OperationSortResults.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_sort_results_edge_attribute.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_sort_results_edge_attribute_parameters import OperationSortResultsEdgeAttributeParameters
from openapi_server import util

from openapi_server.models.operation_sort_results_edge_attribute_parameters import OperationSortResultsEdgeAttributeParameters  # noqa: E501

class OperationSortResultsEdgeAttribute(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationSortResultsEdgeAttribute - a model defined in OpenAPI

        :param id: The id of this OperationSortResultsEdgeAttribute.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationSortResultsEdgeAttribute.  # noqa: E501
        :type parameters: OperationSortResultsEdgeAttributeParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationSortResultsEdgeAttributeParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationSortResultsEdgeAttribute':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationSortResultsEdgeAttribute of this OperationSortResultsEdgeAttribute.  # noqa: E501
        :rtype: OperationSortResultsEdgeAttribute
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationSortResultsEdgeAttribute.


        :return: The id of this OperationSortResultsEdgeAttribute.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationSortResultsEdgeAttribute.


        :param id: The id of this OperationSortResultsEdgeAttribute.
        :type id: str
        """
        allowed_values = ["sort_results_edge_attribute"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationSortResultsEdgeAttribute.


        :return: The parameters of this OperationSortResultsEdgeAttribute.
        :rtype: OperationSortResultsEdgeAttributeParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationSortResultsEdgeAttribute.


        :param parameters: The parameters of this OperationSortResultsEdgeAttribute.
        :type parameters: OperationSortResultsEdgeAttributeParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_sort_results_edge_attribute_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationSortResultsEdgeAttributeParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, ascending_or_descending=None, edge_attribute=None, qedge_keys=None):  # noqa: E501
        """OperationSortResultsEdgeAttributeParameters - a model defined in OpenAPI

        :param ascending_or_descending: The ascending_or_descending of this OperationSortResultsEdgeAttributeParameters.  # noqa: E501
        :type ascending_or_descending: str
        :param edge_attribute: The edge_attribute of this OperationSortResultsEdgeAttributeParameters.  # noqa: E501
        :type edge_attribute: str
        :param qedge_keys: The qedge_keys of this OperationSortResultsEdgeAttributeParameters.  # noqa: E501
        :type qedge_keys: List[str]
        """
        self.openapi_types = {
            'ascending_or_descending': str,
            'edge_attribute': str,
            'qedge_keys': List[str]
        }

        self.attribute_map = {
            'ascending_or_descending': 'ascending_or_descending',
            'edge_attribute': 'edge_attribute',
            'qedge_keys': 'qedge_keys'
        }

        self._ascending_or_descending = ascending_or_descending
        self._edge_attribute = edge_attribute
        self._qedge_keys = qedge_keys

    @classmethod
    def from_dict(cls, dikt) -> 'OperationSortResultsEdgeAttributeParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationSortResultsEdgeAttribute_parameters of this OperationSortResultsEdgeAttributeParameters.  # noqa: E501
        :rtype: OperationSortResultsEdgeAttributeParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def ascending_or_descending(self):
        """Gets the ascending_or_descending of this OperationSortResultsEdgeAttributeParameters.

        Indicates whether results should be sorted in ascending or descending order.  # noqa: E501

        :return: The ascending_or_descending of this OperationSortResultsEdgeAttributeParameters.
        :rtype: str
        """
        return self._ascending_or_descending

    @ascending_or_descending.setter
    def ascending_or_descending(self, ascending_or_descending):
        """Sets the ascending_or_descending of this OperationSortResultsEdgeAttributeParameters.

        Indicates whether results should be sorted in ascending or descending order.  # noqa: E501

        :param ascending_or_descending: The ascending_or_descending of this OperationSortResultsEdgeAttributeParameters.
        :type ascending_or_descending: str
        """
        allowed_values = ["ascending", "descending"]  # noqa: E501
        if ascending_or_descending not in allowed_values:
            raise ValueError(
                "Invalid value for `ascending_or_descending` ({0}), must be one of {1}"
                .format(ascending_or_descending, allowed_values)
            )

        self._ascending_or_descending = ascending_or_descending

    @property
    def edge_attribute(self):
        """Gets the edge_attribute of this OperationSortResultsEdgeAttributeParameters.

        The name of the edge attribute to order by.  # noqa: E501

        :return: The edge_attribute of this OperationSortResultsEdgeAttributeParameters.
        :rtype: str
        """
        return self._edge_attribute

    @edge_attribute.setter
    def edge_attribute(self, edge_attribute):
        """Sets the edge_attribute of this OperationSortResultsEdgeAttributeParameters.

        The name of the edge attribute to order by.  # noqa: E501

        :param edge_attribute: The edge_attribute of this OperationSortResultsEdgeAttributeParameters.
        :type edge_attribute: str
        """
        if edge_attribute is None:
            raise ValueError("Invalid value for `edge_attribute`, must not be `None`")  # noqa: E501

        self._edge_attribute = edge_attribute

    @property
    def qedge_keys(self):
        """Gets the qedge_keys of this OperationSortResultsEdgeAttributeParameters.

        This indicates if you only want to consider edges with specific edge_keys. If not provided or empty, all edges will be looked at.  # noqa: E501

        :return: The qedge_keys of this OperationSortResultsEdgeAttributeParameters.
        :rtype: List[str]
        """
        return self._qedge_keys

    @qedge_keys.setter
    def qedge_keys(self, qedge_keys):
        """Sets the qedge_keys of this OperationSortResultsEdgeAttributeParameters.

        This indicates if you only want to consider edges with specific edge_keys. If not provided or empty, all edges will be looked at.  # noqa: E501

        :param qedge_keys: The qedge_keys of this OperationSortResultsEdgeAttributeParameters.
        :type qedge_keys: List[str]
        """

        self._qedge_keys = qedge_keys


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_sort_results_node_attribute.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_sort_results_node_attribute_parameters import OperationSortResultsNodeAttributeParameters
from openapi_server import util

from openapi_server.models.operation_sort_results_node_attribute_parameters import OperationSortResultsNodeAttributeParameters  # noqa: E501

class OperationSortResultsNodeAttribute(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationSortResultsNodeAttribute - a model defined in OpenAPI

        :param id: The id of this OperationSortResultsNodeAttribute.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationSortResultsNodeAttribute.  # noqa: E501
        :type parameters: OperationSortResultsNodeAttributeParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationSortResultsNodeAttributeParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationSortResultsNodeAttribute':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationSortResultsNodeAttribute of this OperationSortResultsNodeAttribute.  # noqa: E501
        :rtype: OperationSortResultsNodeAttribute
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationSortResultsNodeAttribute.


        :return: The id of this OperationSortResultsNodeAttribute.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationSortResultsNodeAttribute.


        :param id: The id of this OperationSortResultsNodeAttribute.
        :type id: str
        """
        allowed_values = ["sort_results_node_attribute"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationSortResultsNodeAttribute.


        :return: The parameters of this OperationSortResultsNodeAttribute.
        :rtype: OperationSortResultsNodeAttributeParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationSortResultsNodeAttribute.


        :param parameters: The parameters of this OperationSortResultsNodeAttribute.
        :type parameters: OperationSortResultsNodeAttributeParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_sort_results_node_attribute_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationSortResultsNodeAttributeParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, ascending_or_descending=None, node_attribute=None, qnode_keys=None):  # noqa: E501
        """OperationSortResultsNodeAttributeParameters - a model defined in OpenAPI

        :param ascending_or_descending: The ascending_or_descending of this OperationSortResultsNodeAttributeParameters.  # noqa: E501
        :type ascending_or_descending: str
        :param node_attribute: The node_attribute of this OperationSortResultsNodeAttributeParameters.  # noqa: E501
        :type node_attribute: str
        :param qnode_keys: The qnode_keys of this OperationSortResultsNodeAttributeParameters.  # noqa: E501
        :type qnode_keys: List[str]
        """
        self.openapi_types = {
            'ascending_or_descending': str,
            'node_attribute': str,
            'qnode_keys': List[str]
        }

        self.attribute_map = {
            'ascending_or_descending': 'ascending_or_descending',
            'node_attribute': 'node_attribute',
            'qnode_keys': 'qnode_keys'
        }

        self._ascending_or_descending = ascending_or_descending
        self._node_attribute = node_attribute
        self._qnode_keys = qnode_keys

    @classmethod
    def from_dict(cls, dikt) -> 'OperationSortResultsNodeAttributeParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationSortResultsNodeAttribute_parameters of this OperationSortResultsNodeAttributeParameters.  # noqa: E501
        :rtype: OperationSortResultsNodeAttributeParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def ascending_or_descending(self):
        """Gets the ascending_or_descending of this OperationSortResultsNodeAttributeParameters.

        Indicates whether results should be sorted in ascending or descending order.  # noqa: E501

        :return: The ascending_or_descending of this OperationSortResultsNodeAttributeParameters.
        :rtype: str
        """
        return self._ascending_or_descending

    @ascending_or_descending.setter
    def ascending_or_descending(self, ascending_or_descending):
        """Sets the ascending_or_descending of this OperationSortResultsNodeAttributeParameters.

        Indicates whether results should be sorted in ascending or descending order.  # noqa: E501

        :param ascending_or_descending: The ascending_or_descending of this OperationSortResultsNodeAttributeParameters.
        :type ascending_or_descending: str
        """
        allowed_values = ["ascending", "descending"]  # noqa: E501
        if ascending_or_descending not in allowed_values:
            raise ValueError(
                "Invalid value for `ascending_or_descending` ({0}), must be one of {1}"
                .format(ascending_or_descending, allowed_values)
            )

        self._ascending_or_descending = ascending_or_descending

    @property
    def node_attribute(self):
        """Gets the node_attribute of this OperationSortResultsNodeAttributeParameters.

        The name of the node attribute to order by.  # noqa: E501

        :return: The node_attribute of this OperationSortResultsNodeAttributeParameters.
        :rtype: str
        """
        return self._node_attribute

    @node_attribute.setter
    def node_attribute(self, node_attribute):
        """Sets the node_attribute of this OperationSortResultsNodeAttributeParameters.

        The name of the node attribute to order by.  # noqa: E501

        :param node_attribute: The node_attribute of this OperationSortResultsNodeAttributeParameters.
        :type node_attribute: str
        """
        if node_attribute is None:
            raise ValueError("Invalid value for `node_attribute`, must not be `None`")  # noqa: E501

        self._node_attribute = node_attribute

    @property
    def qnode_keys(self):
        """Gets the qnode_keys of this OperationSortResultsNodeAttributeParameters.

        This indicates if you only want to consider nodes with specific node_keys. If not provided or empty, all nodes will be looked at.  # noqa: E501

        :return: The qnode_keys of this OperationSortResultsNodeAttributeParameters.
        :rtype: List[str]
        """
        return self._qnode_keys

    @qnode_keys.setter
    def qnode_keys(self, qnode_keys):
        """Sets the qnode_keys of this OperationSortResultsNodeAttributeParameters.

        This indicates if you only want to consider nodes with specific node_keys. If not provided or empty, all nodes will be looked at.  # noqa: E501

        :param qnode_keys: The qnode_keys of this OperationSortResultsNodeAttributeParameters.
        :type qnode_keys: List[str]
        """

        self._qnode_keys = qnode_keys


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_sort_results_score.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.operation_sort_results_score_parameters import OperationSortResultsScoreParameters
from openapi_server import util

from openapi_server.models.operation_sort_results_score_parameters import OperationSortResultsScoreParameters  # noqa: E501

class OperationSortResultsScore(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationSortResultsScore - a model defined in OpenAPI

        :param id: The id of this OperationSortResultsScore.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationSortResultsScore.  # noqa: E501
        :type parameters: OperationSortResultsScoreParameters
        """
        self.openapi_types = {
            'id': str,
            'parameters': OperationSortResultsScoreParameters
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationSortResultsScore':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationSortResultsScore of this OperationSortResultsScore.  # noqa: E501
        :rtype: OperationSortResultsScore
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationSortResultsScore.


        :return: The id of this OperationSortResultsScore.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationSortResultsScore.


        :param id: The id of this OperationSortResultsScore.
        :type id: str
        """
        allowed_values = ["sort_results_score"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationSortResultsScore.


        :return: The parameters of this OperationSortResultsScore.
        :rtype: OperationSortResultsScoreParameters
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationSortResultsScore.


        :param parameters: The parameters of this OperationSortResultsScore.
        :type parameters: OperationSortResultsScoreParameters
        """
        if parameters is None:
            raise ValueError("Invalid value for `parameters`, must not be `None`")  # noqa: E501

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operation_sort_results_score_parameters.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationSortResultsScoreParameters(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, ascending_or_descending=None):  # noqa: E501
        """OperationSortResultsScoreParameters - a model defined in OpenAPI

        :param ascending_or_descending: The ascending_or_descending of this OperationSortResultsScoreParameters.  # noqa: E501
        :type ascending_or_descending: str
        """
        self.openapi_types = {
            'ascending_or_descending': str
        }

        self.attribute_map = {
            'ascending_or_descending': 'ascending_or_descending'
        }

        self._ascending_or_descending = ascending_or_descending

    @classmethod
    def from_dict(cls, dikt) -> 'OperationSortResultsScoreParameters':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationSortResultsScore_parameters of this OperationSortResultsScoreParameters.  # noqa: E501
        :rtype: OperationSortResultsScoreParameters
        """
        return util.deserialize_model(dikt, cls)

    @property
    def ascending_or_descending(self):
        """Gets the ascending_or_descending of this OperationSortResultsScoreParameters.

        Indicates whether results should be sorted in ascending or descending order.  # noqa: E501

        :return: The ascending_or_descending of this OperationSortResultsScoreParameters.
        :rtype: str
        """
        return self._ascending_or_descending

    @ascending_or_descending.setter
    def ascending_or_descending(self, ascending_or_descending):
        """Sets the ascending_or_descending of this OperationSortResultsScoreParameters.

        Indicates whether results should be sorted in ascending or descending order.  # noqa: E501

        :param ascending_or_descending: The ascending_or_descending of this OperationSortResultsScoreParameters.
        :type ascending_or_descending: str
        """
        allowed_values = ["ascending", "descending"]  # noqa: E501
        if ascending_or_descending not in allowed_values:
            raise ValueError(
                "Invalid value for `ascending_or_descending` ({0}), must be one of {1}"
                .format(ascending_or_descending, allowed_values)
            )

        self._ascending_or_descending = ascending_or_descending


# code/UI/OpenAPI/python-flask-server/openapi_server/models/operations.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.message import Message
from openapi_server import util

from openapi_server.models.message import Message  # noqa: E501

class Operations(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, message_uris=None, messages=None, actions=None, options=None):  # noqa: E501
        """Operations - a model defined in OpenAPI

        :param message_uris: The message_uris of this Operations.  # noqa: E501
        :type message_uris: List[str]
        :param messages: The messages of this Operations.  # noqa: E501
        :type messages: List[Message]
        :param actions: The actions of this Operations.  # noqa: E501
        :type actions: List[str]
        :param options: The options of this Operations.  # noqa: E501
        :type options: Dict[str, object]
        """
        self.openapi_types = {
            'message_uris': List[str],
            'messages': List[Message],
            'actions': List[str],
            'options': Dict[str, object]
        }

        self.attribute_map = {
            'message_uris': 'message_uris',
            'messages': 'messages',
            'actions': 'actions',
            'options': 'options'
        }

        self._message_uris = message_uris
        self._messages = messages
        self._actions = actions
        self._options = options

    @classmethod
    def from_dict(cls, dikt) -> 'Operations':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Operations of this Operations.  # noqa: E501
        :rtype: Operations
        """
        return util.deserialize_model(dikt, cls)

    @property
    def message_uris(self):
        """Gets the message_uris of this Operations.

        List of URIs for Message or Response objects to fetch and process  # noqa: E501

        :return: The message_uris of this Operations.
        :rtype: List[str]
        """
        return self._message_uris

    @message_uris.setter
    def message_uris(self, message_uris):
        """Sets the message_uris of this Operations.

        List of URIs for Message or Response objects to fetch and process  # noqa: E501

        :param message_uris: The message_uris of this Operations.
        :type message_uris: List[str]
        """

        self._message_uris = message_uris

    @property
    def messages(self):
        """Gets the messages of this Operations.

        List of Message objects to process  # noqa: E501

        :return: The messages of this Operations.
        :rtype: List[Message]
        """
        return self._messages

    @messages.setter
    def messages(self, messages):
        """Sets the messages of this Operations.

        List of Message objects to process  # noqa: E501

        :param messages: The messages of this Operations.
        :type messages: List[Message]
        """

        self._messages = messages

    @property
    def actions(self):
        """Gets the actions of this Operations.

        List of order-dependent operations to execute  # noqa: E501

        :return: The actions of this Operations.
        :rtype: List[str]
        """
        return self._actions

    @actions.setter
    def actions(self, actions):
        """Sets the actions of this Operations.

        List of order-dependent operations to execute  # noqa: E501

        :param actions: The actions of this Operations.
        :type actions: List[str]
        """

        self._actions = actions

    @property
    def options(self):
        """Gets the options of this Operations.

        Map of order independent options that apply during processing  # noqa: E501

        :return: The options of this Operations.
        :rtype: Dict[str, object]
        """
        return self._options

    @options.setter
    def options(self, options):
        """Sets the options of this Operations.

        Map of order independent options that apply during processing  # noqa: E501

        :param options: The options of this Operations.
        :type options: Dict[str, object]
        """

        self._options = options


# code/UI/OpenAPI/python-flask-server/openapi_server/models/q_edge.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute_constraint import AttributeConstraint
from openapi_server.models.qualifier_constraint import QualifierConstraint
from openapi_server import util

from openapi_server.models.attribute_constraint import AttributeConstraint  # noqa: E501
from openapi_server.models.qualifier_constraint import QualifierConstraint  # noqa: E501

class QEdge(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, knowledge_type=None, predicates=None, subject=None, object=None, attribute_constraints=[], qualifier_constraints=[], exclude=None, option_group_id=None):  # noqa: E501
        """QEdge - a model defined in OpenAPI

        :param knowledge_type: The knowledge_type of this QEdge.  # noqa: E501
        :type knowledge_type: str
        :param predicates: The predicates of this QEdge.  # noqa: E501
        :type predicates: List[str]
        :param subject: The subject of this QEdge.  # noqa: E501
        :type subject: str
        :param object: The object of this QEdge.  # noqa: E501
        :type object: str
        :param attribute_constraints: The attribute_constraints of this QEdge.  # noqa: E501
        :type attribute_constraints: List[AttributeConstraint]
        :param qualifier_constraints: The qualifier_constraints of this QEdge.  # noqa: E501
        :type qualifier_constraints: List[QualifierConstraint]
        :param exclude: The exclude of this QEdge.  # noqa: E501
        :type exclude: bool
        :param option_group_id: The option_group_id of this QEdge.  # noqa: E501
        :type option_group_id: str
        """
        self.openapi_types = {
            'knowledge_type': str,
            'predicates': List[str],
            'subject': str,
            'object': str,
            'attribute_constraints': List[AttributeConstraint],
            'qualifier_constraints': List[QualifierConstraint],
            'exclude': bool,
            'option_group_id': str
        }

        self.attribute_map = {
            'knowledge_type': 'knowledge_type',
            'predicates': 'predicates',
            'subject': 'subject',
            'object': 'object',
            'attribute_constraints': 'attribute_constraints',
            'qualifier_constraints': 'qualifier_constraints',
            'exclude': 'exclude',
            'option_group_id': 'option_group_id'
        }

        self._knowledge_type = knowledge_type
        self._predicates = predicates
        self._subject = subject
        self._object = object
        self._attribute_constraints = attribute_constraints
        self._qualifier_constraints = qualifier_constraints
        self._exclude = exclude
        self._option_group_id = option_group_id

    @classmethod
    def from_dict(cls, dikt) -> 'QEdge':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The QEdge of this QEdge.  # noqa: E501
        :rtype: QEdge
        """
        return util.deserialize_model(dikt, cls)

    @property
    def knowledge_type(self):
        """Gets the knowledge_type of this QEdge.

        Indicates the type of knowledge that the client wants from the server between the subject and object. If the value is 'lookup', then the client wants direct lookup information from knowledge sources. If the value is 'inferred', then the client wants the server to get creative and connect the subject and object in more speculative and non-direct-lookup ways. If this property is absent or null, it MUST be assumed to mean 'lookup'. This feature is currently experimental and may be further extended in the future.  # noqa: E501

        :return: The knowledge_type of this QEdge.
        :rtype: str
        """
        return self._knowledge_type

    @knowledge_type.setter
    def knowledge_type(self, knowledge_type):
        """Sets the knowledge_type of this QEdge.

        Indicates the type of knowledge that the client wants from the server between the subject and object. If the value is 'lookup', then the client wants direct lookup information from knowledge sources. If the value is 'inferred', then the client wants the server to get creative and connect the subject and object in more speculative and non-direct-lookup ways. If this property is absent or null, it MUST be assumed to mean 'lookup'. This feature is currently experimental and may be further extended in the future.  # noqa: E501

        :param knowledge_type: The knowledge_type of this QEdge.
        :type knowledge_type: str
        """

        self._knowledge_type = knowledge_type

    @property
    def predicates(self):
        """Gets the predicates of this QEdge.


        :return: The predicates of this QEdge.
        :rtype: List[str]
        """
        return self._predicates

    @predicates.setter
    def predicates(self, predicates):
        """Sets the predicates of this QEdge.


        :param predicates: The predicates of this QEdge.
        :type predicates: List[str]
        """
        if predicates is not None and len(predicates) < 1:
            raise ValueError("Invalid value for `predicates`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._predicates = predicates

    @property
    def subject(self):
        """Gets the subject of this QEdge.

        Corresponds to the map key identifier of the subject concept node anchoring the query filter pattern for the query relationship edge.  # noqa: E501

        :return: The subject of this QEdge.
        :rtype: str
        """
        return self._subject

    @subject.setter
    def subject(self, subject):
        """Sets the subject of this QEdge.

        Corresponds to the map key identifier of the subject concept node anchoring the query filter pattern for the query relationship edge.  # noqa: E501

        :param subject: The subject of this QEdge.
        :type subject: str
        """
        if subject is None:
            raise ValueError("Invalid value for `subject`, must not be `None`")  # noqa: E501

        self._subject = subject

    @property
    def object(self):
        """Gets the object of this QEdge.

        Corresponds to the map key identifier of the object concept node anchoring the query filter pattern for the query relationship edge.  # noqa: E501

        :return: The object of this QEdge.
        :rtype: str
        """
        return self._object

    @object.setter
    def object(self, object):
        """Sets the object of this QEdge.

        Corresponds to the map key identifier of the object concept node anchoring the query filter pattern for the query relationship edge.  # noqa: E501

        :param object: The object of this QEdge.
        :type object: str
        """
        if object is None:
            raise ValueError("Invalid value for `object`, must not be `None`")  # noqa: E501

        self._object = object

    @property
    def attribute_constraints(self):
        """Gets the attribute_constraints of this QEdge.

        A list of attribute contraints applied to a query edge. If there are multiple items, they must all be true (equivalent to AND)  # noqa: E501

        :return: The attribute_constraints of this QEdge.
        :rtype: List[AttributeConstraint]
        """
        return self._attribute_constraints

    @attribute_constraints.setter
    def attribute_constraints(self, attribute_constraints):
        """Sets the attribute_constraints of this QEdge.

        A list of attribute contraints applied to a query edge. If there are multiple items, they must all be true (equivalent to AND)  # noqa: E501

        :param attribute_constraints: The attribute_constraints of this QEdge.
        :type attribute_constraints: List[AttributeConstraint]
        """

        self._attribute_constraints = attribute_constraints

    @property
    def qualifier_constraints(self):
        """Gets the qualifier_constraints of this QEdge.

        A list of QualifierConstraints that provide nuance to the QEdge. If multiple QualifierConstraints are provided, there is an OR relationship between them. If the QEdge has multiple predicates or if the QNodes that correspond to the subject or object of this QEdge have multiple categories or multiple curies, then qualifier_constraints MUST NOT be specified because these complex use cases are not supported at this time.  # noqa: E501

        :return: The qualifier_constraints of this QEdge.
        :rtype: List[QualifierConstraint]
        """
        return self._qualifier_constraints

    @qualifier_constraints.setter
    def qualifier_constraints(self, qualifier_constraints):
        """Sets the qualifier_constraints of this QEdge.

        A list of QualifierConstraints that provide nuance to the QEdge. If multiple QualifierConstraints are provided, there is an OR relationship between them. If the QEdge has multiple predicates or if the QNodes that correspond to the subject or object of this QEdge have multiple categories or multiple curies, then qualifier_constraints MUST NOT be specified because these complex use cases are not supported at this time.  # noqa: E501

        :param qualifier_constraints: The qualifier_constraints of this QEdge.
        :type qualifier_constraints: List[QualifierConstraint]
        """

        self._qualifier_constraints = qualifier_constraints

    @property
    def exclude(self):
        """Gets the exclude of this QEdge.

        If set to true, then all subgraphs containing this edge are excluded from the final results. (optional)  # noqa: E501

        :return: The exclude of this QEdge.
        :rtype: bool
        """
        return self._exclude

    @exclude.setter
    def exclude(self, exclude):
        """Sets the exclude of this QEdge.

        If set to true, then all subgraphs containing this edge are excluded from the final results. (optional)  # noqa: E501

        :param exclude: The exclude of this QEdge.
        :type exclude: bool
        """

        self._exclude = exclude

    @property
    def option_group_id(self):
        """Gets the option_group_id of this QEdge.

        Optional string acting as a label on a set of nodes and/or edges indicating that they belong to a group that are to be evaluated as a group.   # noqa: E501

        :return: The option_group_id of this QEdge.
        :rtype: str
        """
        return self._option_group_id

    @option_group_id.setter
    def option_group_id(self, option_group_id):
        """Sets the option_group_id of this QEdge.

        Optional string acting as a label on a set of nodes and/or edges indicating that they belong to a group that are to be evaluated as a group.   # noqa: E501

        :param option_group_id: The option_group_id of this QEdge.
        :type option_group_id: str
        """

        self._option_group_id = option_group_id


# code/UI/OpenAPI/python-flask-server/openapi_server/models/q_node.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute_constraint import AttributeConstraint
from openapi_server import util

from openapi_server.models.attribute_constraint import AttributeConstraint  # noqa: E501

class QNode(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, ids=None, categories=None, is_set=False, constraints=[], option_group_id=None):  # noqa: E501
        """QNode - a model defined in OpenAPI

        :param ids: The ids of this QNode.  # noqa: E501
        :type ids: List[str]
        :param categories: The categories of this QNode.  # noqa: E501
        :type categories: List[str]
        :param is_set: The is_set of this QNode.  # noqa: E501
        :type is_set: bool
        :param constraints: The constraints of this QNode.  # noqa: E501
        :type constraints: List[AttributeConstraint]
        :param option_group_id: The option_group_id of this QNode.  # noqa: E501
        :type option_group_id: str
        """
        self.openapi_types = {
            'ids': List[str],
            'categories': List[str],
            'is_set': bool,
            'constraints': List[AttributeConstraint],
            'option_group_id': str
        }

        self.attribute_map = {
            'ids': 'ids',
            'categories': 'categories',
            'is_set': 'is_set',
            'constraints': 'constraints',
            'option_group_id': 'option_group_id'
        }

        self._ids = ids
        self._categories = categories
        self._is_set = is_set
        self._constraints = constraints
        self._option_group_id = option_group_id

    @classmethod
    def from_dict(cls, dikt) -> 'QNode':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The QNode of this QNode.  # noqa: E501
        :rtype: QNode
        """
        return util.deserialize_model(dikt, cls)

    @property
    def ids(self):
        """Gets the ids of this QNode.

        CURIE identifier for this node  # noqa: E501

        :return: The ids of this QNode.
        :rtype: List[str]
        """
        return self._ids

    @ids.setter
    def ids(self, ids):
        """Sets the ids of this QNode.

        CURIE identifier for this node  # noqa: E501

        :param ids: The ids of this QNode.
        :type ids: List[str]
        """
        if ids is not None and len(ids) < 1:
            raise ValueError("Invalid value for `ids`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._ids = ids

    @property
    def categories(self):
        """Gets the categories of this QNode.


        :return: The categories of this QNode.
        :rtype: List[str]
        """
        return self._categories

    @categories.setter
    def categories(self, categories):
        """Sets the categories of this QNode.


        :param categories: The categories of this QNode.
        :type categories: List[str]
        """
        if categories is not None and len(categories) < 1:
            raise ValueError("Invalid value for `categories`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._categories = categories

    @property
    def is_set(self):
        """Gets the is_set of this QNode.

        Boolean that if set to true, indicates that this QNode MAY have multiple KnowledgeGraph Nodes bound to it within each Result. The nodes in a set should be considered as a set of independent nodes, rather than a set of dependent nodes, i.e., the answer would still be valid if the nodes in the set were instead returned individually. Multiple QNodes may have is_set=True. If a QNode (n1) with is_set=True is connected to a QNode (n2) with is_set=False, each n1 must be connected to n2. If a QNode (n1) with is_set=True is connected to a QNode (n2) with is_set=True, each n1 must be connected to at least one n2.  # noqa: E501

        :return: The is_set of this QNode.
        :rtype: bool
        """
        return self._is_set

    @is_set.setter
    def is_set(self, is_set):
        """Sets the is_set of this QNode.

        Boolean that if set to true, indicates that this QNode MAY have multiple KnowledgeGraph Nodes bound to it within each Result. The nodes in a set should be considered as a set of independent nodes, rather than a set of dependent nodes, i.e., the answer would still be valid if the nodes in the set were instead returned individually. Multiple QNodes may have is_set=True. If a QNode (n1) with is_set=True is connected to a QNode (n2) with is_set=False, each n1 must be connected to n2. If a QNode (n1) with is_set=True is connected to a QNode (n2) with is_set=True, each n1 must be connected to at least one n2.  # noqa: E501

        :param is_set: The is_set of this QNode.
        :type is_set: bool
        """

        self._is_set = is_set

    @property
    def constraints(self):
        """Gets the constraints of this QNode.

        A list of contraints applied to a query node. If there are multiple items, they must all be true (equivalent to AND)  # noqa: E501

        :return: The constraints of this QNode.
        :rtype: List[AttributeConstraint]
        """
        return self._constraints

    @constraints.setter
    def constraints(self, constraints):
        """Sets the constraints of this QNode.

        A list of contraints applied to a query node. If there are multiple items, they must all be true (equivalent to AND)  # noqa: E501

        :param constraints: The constraints of this QNode.
        :type constraints: List[AttributeConstraint]
        """

        self._constraints = constraints

    @property
    def option_group_id(self):
        """Gets the option_group_id of this QNode.

        Optional string acting as a label on a set of nodes and/or edges indicating that they belong to a group that are to be evaluated as a group.   # noqa: E501

        :return: The option_group_id of this QNode.
        :rtype: str
        """
        return self._option_group_id

    @option_group_id.setter
    def option_group_id(self, option_group_id):
        """Sets the option_group_id of this QNode.

        Optional string acting as a label on a set of nodes and/or edges indicating that they belong to a group that are to be evaluated as a group.   # noqa: E501

        :param option_group_id: The option_group_id of this QNode.
        :type option_group_id: str
        """

        self._option_group_id = option_group_id


# code/UI/OpenAPI/python-flask-server/openapi_server/models/qualifier.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class Qualifier(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, qualifier_type_id=None, qualifier_value=None):  # noqa: E501
        """Qualifier - a model defined in OpenAPI

        :param qualifier_type_id: The qualifier_type_id of this Qualifier.  # noqa: E501
        :type qualifier_type_id: str
        :param qualifier_value: The qualifier_value of this Qualifier.  # noqa: E501
        :type qualifier_value: str
        """
        self.openapi_types = {
            'qualifier_type_id': str,
            'qualifier_value': str
        }

        self.attribute_map = {
            'qualifier_type_id': 'qualifier_type_id',
            'qualifier_value': 'qualifier_value'
        }

        self._qualifier_type_id = qualifier_type_id
        self._qualifier_value = qualifier_value

    @classmethod
    def from_dict(cls, dikt) -> 'Qualifier':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Qualifier of this Qualifier.  # noqa: E501
        :rtype: Qualifier
        """
        return util.deserialize_model(dikt, cls)

    @property
    def qualifier_type_id(self):
        """Gets the qualifier_type_id of this Qualifier.

        The category of the qualifier, drawn from a hierarchy of qualifier slots in the Biolink model (e.g. subject_aspect, subject_direction, object_aspect, object_direction, etc).  # noqa: E501

        :return: The qualifier_type_id of this Qualifier.
        :rtype: str
        """
        return self._qualifier_type_id

    @qualifier_type_id.setter
    def qualifier_type_id(self, qualifier_type_id):
        """Sets the qualifier_type_id of this Qualifier.

        The category of the qualifier, drawn from a hierarchy of qualifier slots in the Biolink model (e.g. subject_aspect, subject_direction, object_aspect, object_direction, etc).  # noqa: E501

        :param qualifier_type_id: The qualifier_type_id of this Qualifier.
        :type qualifier_type_id: str
        """
        if qualifier_type_id is None:
            raise ValueError("Invalid value for `qualifier_type_id`, must not be `None`")  # noqa: E501

        self._qualifier_type_id = qualifier_type_id

    @property
    def qualifier_value(self):
        """Gets the qualifier_value of this Qualifier.

        The value associated with the type of the qualifier, drawn from a set of controlled values by the type as specified in the Biolink model (e.g. 'expression' or 'abundance' for the qualifier type 'subject_aspect', etc).  # noqa: E501

        :return: The qualifier_value of this Qualifier.
        :rtype: str
        """
        return self._qualifier_value

    @qualifier_value.setter
    def qualifier_value(self, qualifier_value):
        """Sets the qualifier_value of this Qualifier.

        The value associated with the type of the qualifier, drawn from a set of controlled values by the type as specified in the Biolink model (e.g. 'expression' or 'abundance' for the qualifier type 'subject_aspect', etc).  # noqa: E501

        :param qualifier_value: The qualifier_value of this Qualifier.
        :type qualifier_value: str
        """
        if qualifier_value is None:
            raise ValueError("Invalid value for `qualifier_value`, must not be `None`")  # noqa: E501

        self._qualifier_value = qualifier_value


# code/UI/OpenAPI/python-flask-server/openapi_server/models/qualifier_constraint.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.qualifier import Qualifier
from openapi_server import util

from openapi_server.models.qualifier import Qualifier  # noqa: E501

class QualifierConstraint(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, qualifier_set=None):  # noqa: E501
        """QualifierConstraint - a model defined in OpenAPI

        :param qualifier_set: The qualifier_set of this QualifierConstraint.  # noqa: E501
        :type qualifier_set: List[Qualifier]
        """
        self.openapi_types = {
            'qualifier_set': List[Qualifier]
        }

        self.attribute_map = {
            'qualifier_set': 'qualifier_set'
        }

        self._qualifier_set = qualifier_set

    @classmethod
    def from_dict(cls, dikt) -> 'QualifierConstraint':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The QualifierConstraint of this QualifierConstraint.  # noqa: E501
        :rtype: QualifierConstraint
        """
        return util.deserialize_model(dikt, cls)

    @property
    def qualifier_set(self):
        """Gets the qualifier_set of this QualifierConstraint.

        A set of Qualifiers that serves to add nuance to a query, by constraining allowed values held by Qualifiers on queried Edges.  # noqa: E501

        :return: The qualifier_set of this QualifierConstraint.
        :rtype: List[Qualifier]
        """
        return self._qualifier_set

    @qualifier_set.setter
    def qualifier_set(self, qualifier_set):
        """Sets the qualifier_set of this QualifierConstraint.

        A set of Qualifiers that serves to add nuance to a query, by constraining allowed values held by Qualifiers on queried Edges.  # noqa: E501

        :param qualifier_set: The qualifier_set of this QualifierConstraint.
        :type qualifier_set: List[Qualifier]
        """
        if qualifier_set is None:
            raise ValueError("Invalid value for `qualifier_set`, must not be `None`")  # noqa: E501

        self._qualifier_set = qualifier_set


# code/UI/OpenAPI/python-flask-server/openapi_server/models/query.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_level import LogLevel
from openapi_server.models.message import Message
from openapi_server.models.any_type import AnyType
from openapi_server.models.operations import Operations
from openapi_server import util

from openapi_server.models.log_level import LogLevel  # noqa: E501
from openapi_server.models.message import Message  # noqa: E501
from openapi_server.models.any_type import AnyType  # noqa: E501
from openapi_server.models.operations import Operations  # noqa: E501

class Query(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, message=None, log_level=None, workflow=None, submitter=None, bypass_cache=False, stream_progress=False, enforce_edge_directionality=False, return_minimal_metadata=False, max_results=100, page_size=100, page_number=1, operations=None):  # noqa: E501
        """Query - a model defined in OpenAPI

        :param message: The message of this Query.  # noqa: E501
        :type message: Message
        :param log_level: The log_level of this Query.  # noqa: E501
        :type log_level: LogLevel
        :param workflow: The workflow of this Query.  # noqa: E501
        :type workflow: List[AnyType]
        :param submitter: The submitter of this Query.  # noqa: E501
        :type submitter: str
        :param bypass_cache: The bypass_cache of this Query.  # noqa: E501
        :type bypass_cache: bool
        :param stream_progress: The stream_progress of this Query.  # noqa: E501
        :type stream_progress: bool
        :param enforce_edge_directionality: The enforce_edge_directionality of this Query.  # noqa: E501
        :type enforce_edge_directionality: bool
        :param return_minimal_metadata: The return_minimal_metadata of this Query.  # noqa: E501
        :type return_minimal_metadata: bool
        :param max_results: The max_results of this Query.  # noqa: E501
        :type max_results: int
        :param page_size: The page_size of this Query.  # noqa: E501
        :type page_size: int
        :param page_number: The page_number of this Query.  # noqa: E501
        :type page_number: int
        :param operations: The operations of this Query.  # noqa: E501
        :type operations: Operations
        """
        self.openapi_types = {
            'message': Message,
            'log_level': LogLevel,
            'workflow': List[AnyType],
            'submitter': str,
            'bypass_cache': bool,
            'stream_progress': bool,
            'enforce_edge_directionality': bool,
            'return_minimal_metadata': bool,
            'max_results': int,
            'page_size': int,
            'page_number': int,
            'operations': Operations
        }

        self.attribute_map = {
            'message': 'message',
            'log_level': 'log_level',
            'workflow': 'workflow',
            'submitter': 'submitter',
            'bypass_cache': 'bypass_cache',
            'stream_progress': 'stream_progress',
            'enforce_edge_directionality': 'enforce_edge_directionality',
            'return_minimal_metadata': 'return_minimal_metadata',
            'max_results': 'max_results',
            'page_size': 'page_size',
            'page_number': 'page_number',
            'operations': 'operations'
        }

        self._message = message
        self._log_level = log_level
        self._workflow = workflow
        self._submitter = submitter
        self._bypass_cache = bypass_cache
        self._stream_progress = stream_progress
        self._enforce_edge_directionality = enforce_edge_directionality
        self._return_minimal_metadata = return_minimal_metadata
        self._max_results = max_results
        self._page_size = page_size
        self._page_number = page_number
        self._operations = operations

    @classmethod
    def from_dict(cls, dikt) -> 'Query':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Query of this Query.  # noqa: E501
        :rtype: Query
        """
        return util.deserialize_model(dikt, cls)

    @property
    def message(self):
        """Gets the message of this Query.


        :return: The message of this Query.
        :rtype: Message
        """
        return self._message

    @message.setter
    def message(self, message):
        """Sets the message of this Query.


        :param message: The message of this Query.
        :type message: Message
        """
        if message is None:
            raise ValueError("Invalid value for `message`, must not be `None`")  # noqa: E501

        self._message = message

    @property
    def log_level(self):
        """Gets the log_level of this Query.

        The least critical level of logs to return  # noqa: E501

        :return: The log_level of this Query.
        :rtype: LogLevel
        """
        return self._log_level

    @log_level.setter
    def log_level(self, log_level):
        """Sets the log_level of this Query.

        The least critical level of logs to return  # noqa: E501

        :param log_level: The log_level of this Query.
        :type log_level: LogLevel
        """

        self._log_level = log_level

    @property
    def workflow(self):
        """Gets the workflow of this Query.

        List of workflow steps to be executed.  # noqa: E501

        :return: The workflow of this Query.
        :rtype: List[AnyType]
        """
        return self._workflow

    @workflow.setter
    def workflow(self, workflow):
        """Sets the workflow of this Query.

        List of workflow steps to be executed.  # noqa: E501

        :param workflow: The workflow of this Query.
        :type workflow: List[AnyType]
        """

        self._workflow = workflow

    @property
    def submitter(self):
        """Gets the submitter of this Query.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :return: The submitter of this Query.
        :rtype: str
        """
        return self._submitter

    @submitter.setter
    def submitter(self, submitter):
        """Sets the submitter of this Query.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :param submitter: The submitter of this Query.
        :type submitter: str
        """

        self._submitter = submitter

    @property
    def bypass_cache(self):
        """Gets the bypass_cache of this Query.

        Set to true in order to bypass any possible cached response and try to answer the query from scratch  # noqa: E501

        :return: The bypass_cache of this Query.
        :rtype: bool
        """
        return self._bypass_cache

    @bypass_cache.setter
    def bypass_cache(self, bypass_cache):
        """Sets the bypass_cache of this Query.

        Set to true in order to bypass any possible cached response and try to answer the query from scratch  # noqa: E501

        :param bypass_cache: The bypass_cache of this Query.
        :type bypass_cache: bool
        """

        self._bypass_cache = bypass_cache

    @property
    def stream_progress(self):
        """Gets the stream_progress of this Query.

        Set to true in order to receive a stream of LogEntry objects as the query is progressing  # noqa: E501

        :return: The stream_progress of this Query.
        :rtype: bool
        """
        return self._stream_progress

    @stream_progress.setter
    def stream_progress(self, stream_progress):
        """Sets the stream_progress of this Query.

        Set to true in order to receive a stream of LogEntry objects as the query is progressing  # noqa: E501

        :param stream_progress: The stream_progress of this Query.
        :type stream_progress: bool
        """

        self._stream_progress = stream_progress

    @property
    def enforce_edge_directionality(self):
        """Gets the enforce_edge_directionality of this Query.

        Set to true in order to enforce edge directionality during queries. By default, edge directionality can be reversed without penalty.  # noqa: E501

        :return: The enforce_edge_directionality of this Query.
        :rtype: bool
        """
        return self._enforce_edge_directionality

    @enforce_edge_directionality.setter
    def enforce_edge_directionality(self, enforce_edge_directionality):
        """Sets the enforce_edge_directionality of this Query.

        Set to true in order to enforce edge directionality during queries. By default, edge directionality can be reversed without penalty.  # noqa: E501

        :param enforce_edge_directionality: The enforce_edge_directionality of this Query.
        :type enforce_edge_directionality: bool
        """

        self._enforce_edge_directionality = enforce_edge_directionality

    @property
    def return_minimal_metadata(self):
        """Gets the return_minimal_metadata of this Query.

        Set to true in order to return only the most minimal metadata. By default, rich metadata for nodes and edges are supplied, which may cause a very large and slower Response.  # noqa: E501

        :return: The return_minimal_metadata of this Query.
        :rtype: bool
        """
        return self._return_minimal_metadata

    @return_minimal_metadata.setter
    def return_minimal_metadata(self, return_minimal_metadata):
        """Sets the return_minimal_metadata of this Query.

        Set to true in order to return only the most minimal metadata. By default, rich metadata for nodes and edges are supplied, which may cause a very large and slower Response.  # noqa: E501

        :param return_minimal_metadata: The return_minimal_metadata of this Query.
        :type return_minimal_metadata: bool
        """

        self._return_minimal_metadata = return_minimal_metadata

    @property
    def max_results(self):
        """Gets the max_results of this Query.

        Maximum number of individual results to return  # noqa: E501

        :return: The max_results of this Query.
        :rtype: int
        """
        return self._max_results

    @max_results.setter
    def max_results(self, max_results):
        """Sets the max_results of this Query.

        Maximum number of individual results to return  # noqa: E501

        :param max_results: The max_results of this Query.
        :type max_results: int
        """

        self._max_results = max_results

    @property
    def page_size(self):
        """Gets the page_size of this Query.

        Split the results into pages with this number of results each  # noqa: E501

        :return: The page_size of this Query.
        :rtype: int
        """
        return self._page_size

    @page_size.setter
    def page_size(self, page_size):
        """Sets the page_size of this Query.

        Split the results into pages with this number of results each  # noqa: E501

        :param page_size: The page_size of this Query.
        :type page_size: int
        """

        self._page_size = page_size

    @property
    def page_number(self):
        """Gets the page_number of this Query.

        Page number of results when the number of results exceeds the page_size, with page 1 as the first page  # noqa: E501

        :return: The page_number of this Query.
        :rtype: int
        """
        return self._page_number

    @page_number.setter
    def page_number(self, page_number):
        """Sets the page_number of this Query.

        Page number of results when the number of results exceeds the page_size, with page 1 as the first page  # noqa: E501

        :param page_number: The page_number of this Query.
        :type page_number: int
        """

        self._page_number = page_number

    @property
    def operations(self):
        """Gets the operations of this Query.

        Container for one or more Message objects or identifiers for one or more Messages along with a processing plan and options for how those messages should be processed and returned  # noqa: E501

        :return: The operations of this Query.
        :rtype: Operations
        """
        return self._operations

    @operations.setter
    def operations(self, operations):
        """Sets the operations of this Query.

        Container for one or more Message objects or identifiers for one or more Messages along with a processing plan and options for how those messages should be processed and returned  # noqa: E501

        :param operations: The operations of this Query.
        :type operations: Operations
        """

        self._operations = operations


# code/UI/OpenAPI/python-flask-server/openapi_server/models/query_graph.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.q_edge import QEdge
from openapi_server.models.q_node import QNode
from openapi_server import util

from openapi_server.models.q_edge import QEdge  # noqa: E501
from openapi_server.models.q_node import QNode  # noqa: E501

class QueryGraph(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, nodes=None, edges=None):  # noqa: E501
        """QueryGraph - a model defined in OpenAPI

        :param nodes: The nodes of this QueryGraph.  # noqa: E501
        :type nodes: Dict[str, QNode]
        :param edges: The edges of this QueryGraph.  # noqa: E501
        :type edges: Dict[str, QEdge]
        """
        self.openapi_types = {
            'nodes': Dict[str, QNode],
            'edges': Dict[str, QEdge]
        }

        self.attribute_map = {
            'nodes': 'nodes',
            'edges': 'edges'
        }

        self._nodes = nodes
        self._edges = edges

    @classmethod
    def from_dict(cls, dikt) -> 'QueryGraph':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The QueryGraph of this QueryGraph.  # noqa: E501
        :rtype: QueryGraph
        """
        return util.deserialize_model(dikt, cls)

    @property
    def nodes(self):
        """Gets the nodes of this QueryGraph.

        The node specifications. The keys of this map are unique node identifiers and the corresponding values include the constraints on bound nodes.  # noqa: E501

        :return: The nodes of this QueryGraph.
        :rtype: Dict[str, QNode]
        """
        return self._nodes

    @nodes.setter
    def nodes(self, nodes):
        """Sets the nodes of this QueryGraph.

        The node specifications. The keys of this map are unique node identifiers and the corresponding values include the constraints on bound nodes.  # noqa: E501

        :param nodes: The nodes of this QueryGraph.
        :type nodes: Dict[str, QNode]
        """
        if nodes is None:
            raise ValueError("Invalid value for `nodes`, must not be `None`")  # noqa: E501

        self._nodes = nodes

    @property
    def edges(self):
        """Gets the edges of this QueryGraph.

        The edge specifications. The keys of this map are unique edge identifiers and the corresponding values include the constraints on bound edges, in addition to specifying the subject and object QNodes.  # noqa: E501

        :return: The edges of this QueryGraph.
        :rtype: Dict[str, QEdge]
        """
        return self._edges

    @edges.setter
    def edges(self, edges):
        """Sets the edges of this QueryGraph.

        The edge specifications. The keys of this map are unique edge identifiers and the corresponding values include the constraints on bound edges, in addition to specifying the subject and object QNodes.  # noqa: E501

        :param edges: The edges of this QueryGraph.
        :type edges: Dict[str, QEdge]
        """
        if edges is None:
            raise ValueError("Invalid value for `edges`, must not be `None`")  # noqa: E501

        self._edges = edges


# code/UI/OpenAPI/python-flask-server/openapi_server/models/question.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class Question(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, text=None, language=None):  # noqa: E501
        """Question - a model defined in OpenAPI

        :param text: The text of this Question.  # noqa: E501
        :type text: str
        :param language: The language of this Question.  # noqa: E501
        :type language: str
        """
        self.openapi_types = {
            'text': str,
            'language': str
        }

        self.attribute_map = {
            'text': 'text',
            'language': 'language'
        }

        self._text = text
        self._language = language

    @classmethod
    def from_dict(cls, dikt) -> 'Question':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Question of this Question.  # noqa: E501
        :rtype: Question
        """
        return util.deserialize_model(dikt, cls)

    @property
    def text(self):
        """Gets the text of this Question.

        Free text question  # noqa: E501

        :return: The text of this Question.
        :rtype: str
        """
        return self._text

    @text.setter
    def text(self, text):
        """Sets the text of this Question.

        Free text question  # noqa: E501

        :param text: The text of this Question.
        :type text: str
        """

        self._text = text

    @property
    def language(self):
        """Gets the language of this Question.

        Human language in which the question is posed  # noqa: E501

        :return: The language of this Question.
        :rtype: str
        """
        return self._language

    @language.setter
    def language(self, language):
        """Sets the language of this Question.

        Human language in which the question is posed  # noqa: E501

        :param language: The language of this Question.
        :type language: str
        """
        allowed_values = ["English"]  # noqa: E501
        if language not in allowed_values:
            raise ValueError(
                "Invalid value for `language` ({0}), must be one of {1}"
                .format(language, allowed_values)
            )

        self._language = language


# code/UI/OpenAPI/python-flask-server/openapi_server/models/resource_role_enum.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class ResourceRoleEnum(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    """
    allowed enum values
    """
    PRIMARY_KNOWLEDGE_SOURCE = "primary_knowledge_source"
    AGGREGATOR_KNOWLEDGE_SOURCE = "aggregator_knowledge_source"
    SUPPORTING_DATA_SOURCE = "supporting_data_source"
    def __init__(self):  # noqa: E501
        """ResourceRoleEnum - a model defined in OpenAPI

        """
        self.openapi_types = {
        }

        self.attribute_map = {
        }

    @classmethod
    def from_dict(cls, dikt) -> 'ResourceRoleEnum':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The ResourceRoleEnum of this ResourceRoleEnum.  # noqa: E501
        :rtype: ResourceRoleEnum
        """
        return util.deserialize_model(dikt, cls)


# code/UI/OpenAPI/python-flask-server/openapi_server/models/response.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_entry import LogEntry
from openapi_server.models.message import Message
from openapi_server.models.any_type import AnyType
from openapi_server.models.operations import Operations
from openapi_server import util

from openapi_server.models.log_entry import LogEntry  # noqa: E501
from openapi_server.models.message import Message  # noqa: E501
from openapi_server.models.any_type import AnyType  # noqa: E501
from openapi_server.models.operations import Operations  # noqa: E501

class Response(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, message=None, status=None, description=None, logs=None, workflow=None, submitter=None, operations=None, reasoner_id=None, tool_version=None, schema_version=None, datetime=None, table_column_names=None, original_question=None, restated_question=None, query_options=None, context=None, type=None, id=None, validation_result=None, total_results_count=None, info=None):  # noqa: E501
        """Response - a model defined in OpenAPI

        :param message: The message of this Response.  # noqa: E501
        :type message: Message
        :param status: The status of this Response.  # noqa: E501
        :type status: str
        :param description: The description of this Response.  # noqa: E501
        :type description: str
        :param logs: The logs of this Response.  # noqa: E501
        :type logs: List[LogEntry]
        :param workflow: The workflow of this Response.  # noqa: E501
        :type workflow: List[AnyType]
        :param submitter: The submitter of this Response.  # noqa: E501
        :type submitter: str
        :param operations: The operations of this Response.  # noqa: E501
        :type operations: Operations
        :param reasoner_id: The reasoner_id of this Response.  # noqa: E501
        :type reasoner_id: str
        :param tool_version: The tool_version of this Response.  # noqa: E501
        :type tool_version: str
        :param schema_version: The schema_version of this Response.  # noqa: E501
        :type schema_version: str
        :param datetime: The datetime of this Response.  # noqa: E501
        :type datetime: str
        :param table_column_names: The table_column_names of this Response.  # noqa: E501
        :type table_column_names: List[str]
        :param original_question: The original_question of this Response.  # noqa: E501
        :type original_question: str
        :param restated_question: The restated_question of this Response.  # noqa: E501
        :type restated_question: str
        :param query_options: The query_options of this Response.  # noqa: E501
        :type query_options: object
        :param context: The context of this Response.  # noqa: E501
        :type context: str
        :param type: The type of this Response.  # noqa: E501
        :type type: str
        :param id: The id of this Response.  # noqa: E501
        :type id: str
        :param validation_result: The validation_result of this Response.  # noqa: E501
        :type validation_result: object
        :param total_results_count: The total_results_count of this Response.  # noqa: E501
        :type total_results_count: int
        :param info: The info of this Response.  # noqa: E501
        :type info: str
        """
        self.openapi_types = {
            'message': Message,
            'status': str,
            'description': str,
            'logs': List[LogEntry],
            'workflow': List[AnyType],
            'submitter': str,
            'operations': Operations,
            'reasoner_id': str,
            'tool_version': str,
            'schema_version': str,
            'datetime': str,
            'table_column_names': List[str],
            'original_question': str,
            'restated_question': str,
            'query_options': object,
            'context': str,
            'type': str,
            'id': str,
            'validation_result': object,
            'total_results_count': int,
            'info': str
        }

        self.attribute_map = {
            'message': 'message',
            'status': 'status',
            'description': 'description',
            'logs': 'logs',
            'workflow': 'workflow',
            'submitter': 'submitter',
            'operations': 'operations',
            'reasoner_id': 'reasoner_id',
            'tool_version': 'tool_version',
            'schema_version': 'schema_version',
            'datetime': 'datetime',
            'table_column_names': 'table_column_names',
            'original_question': 'original_question',
            'restated_question': 'restated_question',
            'query_options': 'query_options',
            'context': 'context',
            'type': 'type',
            'id': 'id',
            'validation_result': 'validation_result',
            'total_results_count': 'total_results_count',
            'info': 'info'
        }

        self._message = message
        self._status = status
        self._description = description
        self._logs = logs
        self._workflow = workflow
        self._submitter = submitter
        self._operations = operations
        self._reasoner_id = reasoner_id
        self._tool_version = tool_version
        self._schema_version = schema_version
        self._datetime = datetime
        self._table_column_names = table_column_names
        self._original_question = original_question
        self._restated_question = restated_question
        self._query_options = query_options
        self._context = context
        self._type = type
        self._id = id
        self._validation_result = validation_result
        self._total_results_count = total_results_count
        self._info = info

    @classmethod
    def from_dict(cls, dikt) -> 'Response':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Response of this Response.  # noqa: E501
        :rtype: Response
        """
        return util.deserialize_model(dikt, cls)

    @property
    def message(self):
        """Gets the message of this Response.


        :return: The message of this Response.
        :rtype: Message
        """
        return self._message

    @message.setter
    def message(self, message):
        """Sets the message of this Response.


        :param message: The message of this Response.
        :type message: Message
        """
        if message is None:
            raise ValueError("Invalid value for `message`, must not be `None`")  # noqa: E501

        self._message = message

    @property
    def status(self):
        """Gets the status of this Response.

        One of a standardized set of short codes, e.g. Success, QueryNotTraversable, KPsNotAvailable  # noqa: E501

        :return: The status of this Response.
        :rtype: str
        """
        return self._status

    @status.setter
    def status(self, status):
        """Sets the status of this Response.

        One of a standardized set of short codes, e.g. Success, QueryNotTraversable, KPsNotAvailable  # noqa: E501

        :param status: The status of this Response.
        :type status: str
        """

        self._status = status

    @property
    def description(self):
        """Gets the description of this Response.

        A brief human-readable description of the outcome  # noqa: E501

        :return: The description of this Response.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this Response.

        A brief human-readable description of the outcome  # noqa: E501

        :param description: The description of this Response.
        :type description: str
        """

        self._description = description

    @property
    def logs(self):
        """Gets the logs of this Response.

        Log entries containing errors, warnings, debugging information, etc  # noqa: E501

        :return: The logs of this Response.
        :rtype: List[LogEntry]
        """
        return self._logs

    @logs.setter
    def logs(self, logs):
        """Sets the logs of this Response.

        Log entries containing errors, warnings, debugging information, etc  # noqa: E501

        :param logs: The logs of this Response.
        :type logs: List[LogEntry]
        """

        self._logs = logs

    @property
    def workflow(self):
        """Gets the workflow of this Response.

        A list of operations that were executed.  # noqa: E501

        :return: The workflow of this Response.
        :rtype: List[AnyType]
        """
        return self._workflow

    @workflow.setter
    def workflow(self, workflow):
        """Sets the workflow of this Response.

        A list of operations that were executed.  # noqa: E501

        :param workflow: The workflow of this Response.
        :type workflow: List[AnyType]
        """

        self._workflow = workflow

    @property
    def submitter(self):
        """Gets the submitter of this Response.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :return: The submitter of this Response.
        :rtype: str
        """
        return self._submitter

    @submitter.setter
    def submitter(self, submitter):
        """Sets the submitter of this Response.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :param submitter: The submitter of this Response.
        :type submitter: str
        """

        self._submitter = submitter

    @property
    def operations(self):
        """Gets the operations of this Response.

        Container for one or more Message objects or identifiers for one or more Messages along with the processing plan and options for how those messages were processed and returned  # noqa: E501

        :return: The operations of this Response.
        :rtype: Operations
        """
        return self._operations

    @operations.setter
    def operations(self, operations):
        """Sets the operations of this Response.

        Container for one or more Message objects or identifiers for one or more Messages along with the processing plan and options for how those messages were processed and returned  # noqa: E501

        :param operations: The operations of this Response.
        :type operations: Operations
        """

        self._operations = operations

    @property
    def reasoner_id(self):
        """Gets the reasoner_id of this Response.

        Identifier string of the reasoner that provided this response (one of ARAX, Robokop, etc.)  # noqa: E501

        :return: The reasoner_id of this Response.
        :rtype: str
        """
        return self._reasoner_id

    @reasoner_id.setter
    def reasoner_id(self, reasoner_id):
        """Sets the reasoner_id of this Response.

        Identifier string of the reasoner that provided this response (one of ARAX, Robokop, etc.)  # noqa: E501

        :param reasoner_id: The reasoner_id of this Response.
        :type reasoner_id: str
        """

        self._reasoner_id = reasoner_id

    @property
    def tool_version(self):
        """Gets the tool_version of this Response.

        Version label of the tool that generated this response  # noqa: E501

        :return: The tool_version of this Response.
        :rtype: str
        """
        return self._tool_version

    @tool_version.setter
    def tool_version(self, tool_version):
        """Sets the tool_version of this Response.

        Version label of the tool that generated this response  # noqa: E501

        :param tool_version: The tool_version of this Response.
        :type tool_version: str
        """

        self._tool_version = tool_version

    @property
    def schema_version(self):
        """Gets the schema_version of this Response.

        Version label of this TRAPI schema  # noqa: E501

        :return: The schema_version of this Response.
        :rtype: str
        """
        return self._schema_version

    @schema_version.setter
    def schema_version(self, schema_version):
        """Sets the schema_version of this Response.

        Version label of this TRAPI schema  # noqa: E501

        :param schema_version: The schema_version of this Response.
        :type schema_version: str
        """

        self._schema_version = schema_version

    @property
    def datetime(self):
        """Gets the datetime of this Response.

        Datetime string for the time that this response was generated  # noqa: E501

        :return: The datetime of this Response.
        :rtype: str
        """
        return self._datetime

    @datetime.setter
    def datetime(self, datetime):
        """Sets the datetime of this Response.

        Datetime string for the time that this response was generated  # noqa: E501

        :param datetime: The datetime of this Response.
        :type datetime: str
        """

        self._datetime = datetime

    @property
    def table_column_names(self):
        """Gets the table_column_names of this Response.

        List of column names that corresponds to the row_data for each result  # noqa: E501

        :return: The table_column_names of this Response.
        :rtype: List[str]
        """
        return self._table_column_names

    @table_column_names.setter
    def table_column_names(self, table_column_names):
        """Sets the table_column_names of this Response.

        List of column names that corresponds to the row_data for each result  # noqa: E501

        :param table_column_names: The table_column_names of this Response.
        :type table_column_names: List[str]
        """

        self._table_column_names = table_column_names

    @property
    def original_question(self):
        """Gets the original_question of this Response.

        The original question text typed in by the user  # noqa: E501

        :return: The original_question of this Response.
        :rtype: str
        """
        return self._original_question

    @original_question.setter
    def original_question(self, original_question):
        """Sets the original_question of this Response.

        The original question text typed in by the user  # noqa: E501

        :param original_question: The original_question of this Response.
        :type original_question: str
        """

        self._original_question = original_question

    @property
    def restated_question(self):
        """Gets the restated_question of this Response.

        A precise restatement of the question, as understood by the Translator, for which the answer applies. The user should verify that the restated question matches the intent of their original question (it might not).  # noqa: E501

        :return: The restated_question of this Response.
        :rtype: str
        """
        return self._restated_question

    @restated_question.setter
    def restated_question(self, restated_question):
        """Sets the restated_question of this Response.

        A precise restatement of the question, as understood by the Translator, for which the answer applies. The user should verify that the restated question matches the intent of their original question (it might not).  # noqa: E501

        :param restated_question: The restated_question of this Response.
        :type restated_question: str
        """

        self._restated_question = restated_question

    @property
    def query_options(self):
        """Gets the query_options of this Response.

        Dict of options that can be sent with the query. Options are tool specific and not stipulated here  # noqa: E501

        :return: The query_options of this Response.
        :rtype: object
        """
        return self._query_options

    @query_options.setter
    def query_options(self, query_options):
        """Sets the query_options of this Response.

        Dict of options that can be sent with the query. Options are tool specific and not stipulated here  # noqa: E501

        :param query_options: The query_options of this Response.
        :type query_options: object
        """

        self._query_options = query_options

    @property
    def context(self):
        """Gets the context of this Response.

        JSON-LD context URI  # noqa: E501

        :return: The context of this Response.
        :rtype: str
        """
        return self._context

    @context.setter
    def context(self, context):
        """Sets the context of this Response.

        JSON-LD context URI  # noqa: E501

        :param context: The context of this Response.
        :type context: str
        """

        self._context = context

    @property
    def type(self):
        """Gets the type of this Response.

        Entity type of this response  # noqa: E501

        :return: The type of this Response.
        :rtype: str
        """
        return self._type

    @type.setter
    def type(self, type):
        """Sets the type of this Response.

        Entity type of this response  # noqa: E501

        :param type: The type of this Response.
        :type type: str
        """

        self._type = type

    @property
    def id(self):
        """Gets the id of this Response.

        URI for this response  # noqa: E501

        :return: The id of this Response.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this Response.

        URI for this response  # noqa: E501

        :param id: The id of this Response.
        :type id: str
        """

        self._id = id

    @property
    def validation_result(self):
        """Gets the validation_result of this Response.

        Validation results and other summary stats computed for this Response.  # noqa: E501

        :return: The validation_result of this Response.
        :rtype: object
        """
        return self._validation_result

    @validation_result.setter
    def validation_result(self, validation_result):
        """Sets the validation_result of this Response.

        Validation results and other summary stats computed for this Response.  # noqa: E501

        :param validation_result: The validation_result of this Response.
        :type validation_result: object
        """

        self._validation_result = validation_result

    @property
    def total_results_count(self):
        """Gets the total_results_count of this Response.

        The total number of results that were generated prior to any filtering.  # noqa: E501

        :return: The total_results_count of this Response.
        :rtype: int
        """
        return self._total_results_count

    @total_results_count.setter
    def total_results_count(self, total_results_count):
        """Sets the total_results_count of this Response.

        The total number of results that were generated prior to any filtering.  # noqa: E501

        :param total_results_count: The total_results_count of this Response.
        :type total_results_count: int
        """

        self._total_results_count = total_results_count

    @property
    def info(self):
        """Gets the info of this Response.

        A placholder for including some additional information  # noqa: E501

        :return: The info of this Response.
        :rtype: str
        """
        return self._info

    @info.setter
    def info(self, info):
        """Sets the info of this Response.

        A placholder for including some additional information  # noqa: E501

        :param info: The info of this Response.
        :type info: str
        """

        self._info = info



# code/UI/OpenAPI/python-flask-server/openapi_server/models/result.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.analysis import Analysis
from openapi_server.models.node_binding import NodeBinding
from openapi_server.models.any_type import AnyType
from openapi_server import util

from openapi_server.models.analysis import Analysis  # noqa: E501
from openapi_server.models.node_binding import NodeBinding  # noqa: E501
from openapi_server.models.any_type import AnyType  # noqa: E501

class Result(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, node_bindings=None, analyses=None, id=None, description=None, essence=None, essence_category=None, row_data=None, score=None, score_name=None, score_direction=None, confidence=None, result_group=None, result_group_similarity_score=None, reasoner_id=None):  # noqa: E501
        """Result - a model defined in OpenAPI

        :param node_bindings: The node_bindings of this Result.  # noqa: E501
        :type node_bindings: Dict[str, List[NodeBinding]]
        :param analyses: The analyses of this Result.  # noqa: E501
        :type analyses: List[Analysis]
        :param id: The id of this Result.  # noqa: E501
        :type id: str
        :param description: The description of this Result.  # noqa: E501
        :type description: str
        :param essence: The essence of this Result.  # noqa: E501
        :type essence: str
        :param essence_category: The essence_category of this Result.  # noqa: E501
        :type essence_category: str
        :param row_data: The row_data of this Result.  # noqa: E501
        :type row_data: List[AnyType]
        :param score: The score of this Result.  # noqa: E501
        :type score: float
        :param score_name: The score_name of this Result.  # noqa: E501
        :type score_name: str
        :param score_direction: The score_direction of this Result.  # noqa: E501
        :type score_direction: str
        :param confidence: The confidence of this Result.  # noqa: E501
        :type confidence: float
        :param result_group: The result_group of this Result.  # noqa: E501
        :type result_group: int
        :param result_group_similarity_score: The result_group_similarity_score of this Result.  # noqa: E501
        :type result_group_similarity_score: float
        :param reasoner_id: The reasoner_id of this Result.  # noqa: E501
        :type reasoner_id: str
        """
        self.openapi_types = {
            'node_bindings': Dict[str, List[NodeBinding]],
            'analyses': List[Analysis],
            'id': str,
            'description': str,
            'essence': str,
            'essence_category': str,
            'row_data': List[AnyType],
            'score': float,
            'score_name': str,
            'score_direction': str,
            'confidence': float,
            'result_group': int,
            'result_group_similarity_score': float,
            'reasoner_id': str
        }

        self.attribute_map = {
            'node_bindings': 'node_bindings',
            'analyses': 'analyses',
            'id': 'id',
            'description': 'description',
            'essence': 'essence',
            'essence_category': 'essence_category',
            'row_data': 'row_data',
            'score': 'score',
            'score_name': 'score_name',
            'score_direction': 'score_direction',
            'confidence': 'confidence',
            'result_group': 'result_group',
            'result_group_similarity_score': 'result_group_similarity_score',
            'reasoner_id': 'reasoner_id'
        }

        self._node_bindings = node_bindings
        self._analyses = analyses
        self._id = id
        self._description = description
        self._essence = essence
        self._essence_category = essence_category
        self._row_data = row_data
        self._score = score
        self._score_name = score_name
        self._score_direction = score_direction
        self._confidence = confidence
        self._result_group = result_group
        self._result_group_similarity_score = result_group_similarity_score
        self._reasoner_id = reasoner_id

    @classmethod
    def from_dict(cls, dikt) -> 'Result':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Result of this Result.  # noqa: E501
        :rtype: Result
        """
        return util.deserialize_model(dikt, cls)

    @property
    def node_bindings(self):
        """Gets the node_bindings of this Result.

        The dictionary of Input Query Graph to Result Knowledge Graph node bindings where the dictionary keys are the key identifiers of the Query Graph nodes and the associated values of those keys are instances of NodeBinding schema type (see below). This value is an array of NodeBindings since a given query node may have multiple knowledge graph Node bindings in the result.  # noqa: E501

        :return: The node_bindings of this Result.
        :rtype: Dict[str, List[NodeBinding]]
        """
        return self._node_bindings

    @node_bindings.setter
    def node_bindings(self, node_bindings):
        """Sets the node_bindings of this Result.

        The dictionary of Input Query Graph to Result Knowledge Graph node bindings where the dictionary keys are the key identifiers of the Query Graph nodes and the associated values of those keys are instances of NodeBinding schema type (see below). This value is an array of NodeBindings since a given query node may have multiple knowledge graph Node bindings in the result.  # noqa: E501

        :param node_bindings: The node_bindings of this Result.
        :type node_bindings: Dict[str, List[NodeBinding]]
        """
        if node_bindings is None:
            raise ValueError("Invalid value for `node_bindings`, must not be `None`")  # noqa: E501

        self._node_bindings = node_bindings

    @property
    def analyses(self):
        """Gets the analyses of this Result.

        The list of all Analysis components that contribute to the result. See below for Analysis components.  # noqa: E501

        :return: The analyses of this Result.
        :rtype: List[Analysis]
        """
        return self._analyses

    @analyses.setter
    def analyses(self, analyses):
        """Sets the analyses of this Result.

        The list of all Analysis components that contribute to the result. See below for Analysis components.  # noqa: E501

        :param analyses: The analyses of this Result.
        :type analyses: List[Analysis]
        """
        if analyses is None:
            raise ValueError("Invalid value for `analyses`, must not be `None`")  # noqa: E501

        self._analyses = analyses

    @property
    def id(self):
        """Gets the id of this Result.

        URI for this result  # noqa: E501

        :return: The id of this Result.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this Result.

        URI for this result  # noqa: E501

        :param id: The id of this Result.
        :type id: str
        """

        self._id = id

    @property
    def description(self):
        """Gets the description of this Result.

        A free text description of this result answer from the reasoner  # noqa: E501

        :return: The description of this Result.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this Result.

        A free text description of this result answer from the reasoner  # noqa: E501

        :param description: The description of this Result.
        :type description: str
        """

        self._description = description

    @property
    def essence(self):
        """Gets the essence of this Result.

        A single string that is the terse essence of the result (useful for simple answers)  # noqa: E501

        :return: The essence of this Result.
        :rtype: str
        """
        return self._essence

    @essence.setter
    def essence(self, essence):
        """Sets the essence of this Result.

        A single string that is the terse essence of the result (useful for simple answers)  # noqa: E501

        :param essence: The essence of this Result.
        :type essence: str
        """

        self._essence = essence

    @property
    def essence_category(self):
        """Gets the essence_category of this Result.

        A Translator BioLink bioentity category of the essence  # noqa: E501

        :return: The essence_category of this Result.
        :rtype: str
        """
        return self._essence_category

    @essence_category.setter
    def essence_category(self, essence_category):
        """Sets the essence_category of this Result.

        A Translator BioLink bioentity category of the essence  # noqa: E501

        :param essence_category: The essence_category of this Result.
        :type essence_category: str
        """

        self._essence_category = essence_category

    @property
    def row_data(self):
        """Gets the row_data of this Result.

        An arbitrary list of values that captures the essence of the result that can be turned into a tabular result across all answers (each result is a row) for a user that wants simplified tabular output  # noqa: E501

        :return: The row_data of this Result.
        :rtype: List[AnyType]
        """
        return self._row_data

    @row_data.setter
    def row_data(self, row_data):
        """Sets the row_data of this Result.

        An arbitrary list of values that captures the essence of the result that can be turned into a tabular result across all answers (each result is a row) for a user that wants simplified tabular output  # noqa: E501

        :param row_data: The row_data of this Result.
        :type row_data: List[AnyType]
        """

        self._row_data = row_data

    @property
    def score(self):
        """Gets the score of this Result.

        A numerical score associated with this result indicating the relevance or confidence of this result relative to others in the returned set. Higher MUST be better.  # noqa: E501

        :return: The score of this Result.
        :rtype: float
        """
        return self._score

    @score.setter
    def score(self, score):
        """Sets the score of this Result.

        A numerical score associated with this result indicating the relevance or confidence of this result relative to others in the returned set. Higher MUST be better.  # noqa: E501

        :param score: The score of this Result.
        :type score: float
        """

        self._score = score

    @property
    def score_name(self):
        """Gets the score_name of this Result.

        Name for the score  # noqa: E501

        :return: The score_name of this Result.
        :rtype: str
        """
        return self._score_name

    @score_name.setter
    def score_name(self, score_name):
        """Sets the score_name of this Result.

        Name for the score  # noqa: E501

        :param score_name: The score_name of this Result.
        :type score_name: str
        """

        self._score_name = score_name

    @property
    def score_direction(self):
        """Gets the score_direction of this Result.

        Sorting indicator for the score: one of higher_is_better or lower_is_better  # noqa: E501

        :return: The score_direction of this Result.
        :rtype: str
        """
        return self._score_direction

    @score_direction.setter
    def score_direction(self, score_direction):
        """Sets the score_direction of this Result.

        Sorting indicator for the score: one of higher_is_better or lower_is_better  # noqa: E501

        :param score_direction: The score_direction of this Result.
        :type score_direction: str
        """

        self._score_direction = score_direction

    @property
    def confidence(self):
        """Gets the confidence of this Result.

        Confidence metric for this result, a value between (inclusive)  0.0 (no confidence) and 1.0 (highest confidence)  # noqa: E501

        :return: The confidence of this Result.
        :rtype: float
        """
        return self._confidence

    @confidence.setter
    def confidence(self, confidence):
        """Sets the confidence of this Result.

        Confidence metric for this result, a value between (inclusive)  0.0 (no confidence) and 1.0 (highest confidence)  # noqa: E501

        :param confidence: The confidence of this Result.
        :type confidence: float
        """

        self._confidence = confidence

    @property
    def result_group(self):
        """Gets the result_group of this Result.

        An integer group number for results for use in cases where several results should be grouped together. Also useful to control sorting ascending.  # noqa: E501

        :return: The result_group of this Result.
        :rtype: int
        """
        return self._result_group

    @result_group.setter
    def result_group(self, result_group):
        """Sets the result_group of this Result.

        An integer group number for results for use in cases where several results should be grouped together. Also useful to control sorting ascending.  # noqa: E501

        :param result_group: The result_group of this Result.
        :type result_group: int
        """

        self._result_group = result_group

    @property
    def result_group_similarity_score(self):
        """Gets the result_group_similarity_score of this Result.

        A score that denotes the similarity of this result to other members of the result_group  # noqa: E501

        :return: The result_group_similarity_score of this Result.
        :rtype: float
        """
        return self._result_group_similarity_score

    @result_group_similarity_score.setter
    def result_group_similarity_score(self, result_group_similarity_score):
        """Sets the result_group_similarity_score of this Result.

        A score that denotes the similarity of this result to other members of the result_group  # noqa: E501

        :param result_group_similarity_score: The result_group_similarity_score of this Result.
        :type result_group_similarity_score: float
        """

        self._result_group_similarity_score = result_group_similarity_score

    @property
    def reasoner_id(self):
        """Gets the reasoner_id of this Result.

        Identifier string of the reasoner that provided this result (e.g., ARAX, Robokop, etc.)  # noqa: E501

        :return: The reasoner_id of this Result.
        :rtype: str
        """
        return self._reasoner_id

    @reasoner_id.setter
    def reasoner_id(self, reasoner_id):
        """Sets the reasoner_id of this Result.

        Identifier string of the reasoner that provided this result (e.g., ARAX, Robokop, etc.)  # noqa: E501

        :param reasoner_id: The reasoner_id of this Result.
        :type reasoner_id: str
        """

        self._reasoner_id = reasoner_id


# code/UI/OpenAPI/python-flask-server/openapi_server/models/retrieval_source.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.resource_role_enum import ResourceRoleEnum
from openapi_server import util

from openapi_server.models.resource_role_enum import ResourceRoleEnum  # noqa: E501

class RetrievalSource(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, resource=None, resource_role=None, upstream_resources=None, source_record_urls=None):  # noqa: E501
        """RetrievalSource - a model defined in OpenAPI

        :param resource: The resource of this RetrievalSource.  # noqa: E501
        :type resource: str
        :param resource_role: The resource_role of this RetrievalSource.  # noqa: E501
        :type resource_role: ResourceRoleEnum
        :param upstream_resources: The upstream_resources of this RetrievalSource.  # noqa: E501
        :type upstream_resources: List[str]
        :param source_record_urls: The source_record_urls of this RetrievalSource.  # noqa: E501
        :type source_record_urls: List[str]
        """
        self.openapi_types = {
            'resource': str,
            'resource_role': ResourceRoleEnum,
            'upstream_resources': List[str],
            'source_record_urls': List[str]
        }

        self.attribute_map = {
            'resource': 'resource',
            'resource_role': 'resource_role',
            'upstream_resources': 'upstream_resources',
            'source_record_urls': 'source_record_urls'
        }

        self._resource = resource
        self._resource_role = resource_role
        self._upstream_resources = upstream_resources
        self._source_record_urls = source_record_urls

    @classmethod
    def from_dict(cls, dikt) -> 'RetrievalSource':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The RetrievalSource of this RetrievalSource.  # noqa: E501
        :rtype: RetrievalSource
        """
        return util.deserialize_model(dikt, cls)

    @property
    def resource(self):
        """Gets the resource of this RetrievalSource.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The resource of this RetrievalSource.
        :rtype: str
        """
        return self._resource

    @resource.setter
    def resource(self, resource):
        """Sets the resource of this RetrievalSource.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param resource: The resource of this RetrievalSource.
        :type resource: str
        """
        if resource is None:
            raise ValueError("Invalid value for `resource`, must not be `None`")  # noqa: E501

        self._resource = resource

    @property
    def resource_role(self):
        """Gets the resource_role of this RetrievalSource.


        :return: The resource_role of this RetrievalSource.
        :rtype: ResourceRoleEnum
        """
        return self._resource_role

    @resource_role.setter
    def resource_role(self, resource_role):
        """Sets the resource_role of this RetrievalSource.


        :param resource_role: The resource_role of this RetrievalSource.
        :type resource_role: ResourceRoleEnum
        """
        if resource_role is None:
            raise ValueError("Invalid value for `resource_role`, must not be `None`")  # noqa: E501

        self._resource_role = resource_role

    @property
    def upstream_resources(self):
        """Gets the upstream_resources of this RetrievalSource.

        An upstream InformationResource from which the resource being described directly retrieved a record of the knowledge expressed in the Edge, or data used to generate this knowledge. This is an array because there are cases where a merged Edge holds knowledge that was retrieved from multiple sources. e.g. an Edge provided by the ARAGORN ARA can expressing knowledge it retrieved from both the automat-mychem-info and molepro KPs, which both provided it with records of this single fact.  # noqa: E501

        :return: The upstream_resources of this RetrievalSource.
        :rtype: List[str]
        """
        return self._upstream_resources

    @upstream_resources.setter
    def upstream_resources(self, upstream_resources):
        """Sets the upstream_resources of this RetrievalSource.

        An upstream InformationResource from which the resource being described directly retrieved a record of the knowledge expressed in the Edge, or data used to generate this knowledge. This is an array because there are cases where a merged Edge holds knowledge that was retrieved from multiple sources. e.g. an Edge provided by the ARAGORN ARA can expressing knowledge it retrieved from both the automat-mychem-info and molepro KPs, which both provided it with records of this single fact.  # noqa: E501

        :param upstream_resources: The upstream_resources of this RetrievalSource.
        :type upstream_resources: List[str]
        """

        self._upstream_resources = upstream_resources

    @property
    def source_record_urls(self):
        """Gets the source_record_urls of this RetrievalSource.

        A URL linking to a specific web page or document provided by the  source, that contains a record of the knowledge expressed in the  Edge. If the knowledge is contained in more than one web page on  an Information Resource's site, urls MAY be provided for each.  For example, Therapeutic Targets Database (TTD) has separate web  pages for 'Imatinib' and its protein target KIT, both of which hold  the claim that 'the KIT protein is a therapeutic target for Imatinib'.           # noqa: E501

        :return: The source_record_urls of this RetrievalSource.
        :rtype: List[str]
        """
        return self._source_record_urls

    @source_record_urls.setter
    def source_record_urls(self, source_record_urls):
        """Sets the source_record_urls of this RetrievalSource.

        A URL linking to a specific web page or document provided by the  source, that contains a record of the knowledge expressed in the  Edge. If the knowledge is contained in more than one web page on  an Information Resource's site, urls MAY be provided for each.  For example, Therapeutic Targets Database (TTD) has separate web  pages for 'Imatinib' and its protein target KIT, both of which hold  the claim that 'the KIT protein is a therapeutic target for Imatinib'.           # noqa: E501

        :param source_record_urls: The source_record_urls of this RetrievalSource.
        :type source_record_urls: List[str]
        """

        self._source_record_urls = source_record_urls


# code/UI/OpenAPI/python-flask-server/openapi_server/test/__init__.py

import logging

import connexion
from flask_testing import TestCase

from openapi_server.encoder import JSONEncoder


class BaseTestCase(TestCase):

    def create_app(self):
        logging.getLogger('connexion.operation').setLevel('ERROR')
        app = connexion.App(__name__, specification_dir='../openapi/')
        app.app.json_encoder = JSONEncoder
        app.add_api('openapi.yaml', pythonic_params=True)
        return app.app


# code/UI/OpenAPI/python-flask-server/openapi_server/test/test_entity_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.test import BaseTestCase


class TestEntityController(BaseTestCase):
    """EntityController integration test stubs"""

    def test_get_entity_by_string(self):
        """Test case for get_entity_by_string

        Obtain the CURIE and type of some entity by name
        """
        headers = { 
            'Accept': 'application/json',
        }
        response = self.client.open(
            '/api/arax/v1/entity/{search_string}'.format(search_string='search_string_example'),
            method='GET',
            headers=headers)
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/openapi_server/test/test_example_questions_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.test import BaseTestCase


class TestExampleQuestionsController(BaseTestCase):
    """ExampleQuestionsController integration test stubs"""

    def test_example_questions(self):
        """Test case for example_questions

        Request a list of example questions that ARAX can answer
        """
        headers = { 
            'Accept': 'application/json',
        }
        response = self.client.open(
            '/api/arax/v1/exampleQuestions',
            method='GET',
            headers=headers)
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/openapi_server/test/test_predicates_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.test import BaseTestCase


class TestPredicatesController(BaseTestCase):
    """PredicatesController integration test stubs"""

    def test_predicates_get(self):
        """Test case for predicates_get

        Get supported relationships by source and target
        """
        headers = { 
            'Accept': 'application/json',
        }
        response = self.client.open(
            '/api/arax/v1/predicates',
            method='GET',
            headers=headers)
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/openapi_server/test/test_pubmed_mesh_ngd_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.models.mesh_ngd_response import MeshNgdResponse  # noqa: E501
from openapi_server.test import BaseTestCase


class TestPubmedMeshNgdController(BaseTestCase):
    """PubmedMeshNgdController integration test stubs"""

    def test_pubmed_mesh_ngd(self):
        """Test case for pubmed_mesh_ngd

        Query to get the Normalized Google Distance between two MeSH terms based on co-occurrence in all PubMed article annotations
        """
        headers = { 
            'Accept': 'application/json',
        }
        response = self.client.open(
            '/api/arax/v1/PubmedMeshNgd/{term1}/{term2}'.format(term1='term1_example', term2='term2_example'),
            method='GET',
            headers=headers)
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/openapi_server/test/test_query_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.models.response import Response  # noqa: E501
from openapi_server.test import BaseTestCase


class TestQueryController(BaseTestCase):
    """QueryController integration test stubs"""

    def test_query(self):
        """Test case for query

        Query reasoner via one of several inputs
        """
        request_body = None
        query_string = [('bypass_cache', False)]
        headers = { 
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
        response = self.client.open(
            '/api/arax/v1/query',
            method='POST',
            headers=headers,
            data=json.dumps(request_body),
            content_type='application/json',
            query_string=query_string)
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/openapi_server/test/test_response_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.models.response import Response  # noqa: E501
from openapi_server.test import BaseTestCase


class TestResponseController(BaseTestCase):
    """ResponseController integration test stubs"""

    def test_get_response(self):
        """Test case for get_response

        Request a previously stored response from the server
        """
        headers = { 
            'Accept': 'application/json',
        }
        response = self.client.open(
            '/api/arax/v1/response/{response_id}'.format(response_id=56),
            method='GET',
            headers=headers)
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/openapi_server/test/test_translate_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.models.query import Query  # noqa: E501
from openapi_server.test import BaseTestCase


class TestTranslateController(BaseTestCase):
    """TranslateController integration test stubs"""

    def test_translate(self):
        """Test case for translate

        Translate natural language question into a standardized query
        """
        request_body = None
        headers = { 
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
        response = self.client.open(
            '/api/arax/v1/translate',
            method='POST',
            headers=headers,
            data=json.dumps(request_body),
            content_type='application/json')
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/openapi_server/__main__.py

#!/usr/bin/env python3

import connexion, flask, flask_cors
import logging
import json
import openapi_server.encoder
import os, sys, signal, atexit

logging.basicConfig(level=logging.INFO)  # can change this to logging.DEBUG for debuggging

@atexit.register
def ignore_sigchld():
    logging.debug("Setting SIGCHLD to SIG_IGN before exiting")
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)

    
def receive_sigchld(signal_number, frame):
    if signal_number == signal.SIGCHLD:
        while True:
            try:
                pid, _ = os.waitpid(-1, os.WNOHANG)
                logging.debug(f"PID returned from call to os.waitpid: {pid}")
                if pid == 0:
                    break
            except ChildProcessError as e:
                logging.debug(repr(e) + "; this is expected if there are no more child processes to reap")
                break

def receive_sigpipe(signal_number, frame):
    if signal_number == signal.SIGPIPE:
        logging.error("pipe error")

def main():
    app = connexion.App(__name__, specification_dir='./openapi/')
    app.app.json_encoder = openapi_server.encoder.JSONEncoder
    app.add_api('openapi.yaml',
                arguments={'title': 'ARAX Translator Reasoner'},
                pythonic_params=True)
    flask_cors.CORS(app.app)
    signal.signal(signal.SIGCHLD, receive_sigchld)
    signal.signal(signal.SIGPIPE, receive_sigpipe)

    #### Read any load configuration details for this instance
    try:
        with open('openapi_server/flask_config.json') as infile:
            local_config = json.load(infile)
    except:
        local_config = { "port": 5000 }

    #### Start the service
    app.run(port=local_config['port'], threaded=True)


if __name__ == '__main__':
    main()


# code/UI/OpenAPI/python-flask-server/openapi_server/encoder.py

from connexion.apps.flask_app import FlaskJSONEncoder
import six

from openapi_server.models.base_model_ import Model


class JSONEncoder(FlaskJSONEncoder):
    include_nulls = False

    def default(self, o):
        if isinstance(o, Model):
            dikt = {}
            for attr, _ in six.iteritems(o.openapi_types):
                value = getattr(o, attr)
                if value is None and not self.include_nulls:
                    continue
                attr = o.attribute_map[attr]
                dikt[attr] = value
            return dikt
        return FlaskJSONEncoder.default(self, o)


# code/UI/OpenAPI/python-flask-server/openapi_server/typing_utils.py

# coding: utf-8

import sys

if sys.version_info < (3, 7):
    import typing

    def is_generic(klass):
        """ Determine whether klass is a generic class """
        return type(klass) == typing.GenericMeta

    def is_dict(klass):
        """ Determine whether klass is a Dict """
        return klass.__extra__ == dict

    def is_list(klass):
        """ Determine whether klass is a List """
        return klass.__extra__ == list

else:

    def is_generic(klass):
        """ Determine whether klass is a generic class """
        return hasattr(klass, '__origin__')

    def is_dict(klass):
        """ Determine whether klass is a Dict """
        return klass.__origin__ == dict

    def is_list(klass):
        """ Determine whether klass is a List """
        return klass.__origin__ == list


# code/UI/OpenAPI/python-flask-server/openapi_server/util-prev.py

import datetime

import six
import typing


def _deserialize(data, klass):
    """Deserializes dict, list, str into an object.

    :param data: dict, list or str.
    :param klass: class literal, or string of class name.

    :return: object.
    """
    if data is None:
        return None

    if klass in six.integer_types or klass in (float, str, bool):
        return _deserialize_primitive(data, klass)
    elif klass == object:
        return _deserialize_object(data)
    elif klass == datetime.date:
        return deserialize_date(data)
    elif klass == datetime.datetime:
        return deserialize_datetime(data)
    elif hasattr(klass, '__origin__'):
        eprint(klass.__origin__)
        if klass.__origin__ == list:
            return _deserialize_list(data, klass.__args__[0])
        if klass.__origin__ == dict:
            return _deserialize_dict(data, klass.__args__[1])
    else:
        return deserialize_model(data, klass)


def _deserialize_primitive(data, klass):
    """Deserializes to primitive type.

    :param data: data to deserialize.
    :param klass: class literal.

    :return: int, long, float, str, bool.
    :rtype: int | long | float | str | bool
    """
    try:
        value = klass(data)
    except UnicodeEncodeError:
        value = six.u(data)
    except TypeError:
        value = data
    return value


def _deserialize_object(value):
    """Return an original value.

    :return: object.
    """
    return value


def deserialize_date(string):
    """Deserializes string to date.

    :param string: str.
    :type string: str
    :return: date.
    :rtype: date
    """
    try:
        from dateutil.parser import parse
        return parse(string).date()
    except ImportError:
        return string


def deserialize_datetime(string):
    """Deserializes string to datetime.

    The string should be in iso8601 datetime format.

    :param string: str.
    :type string: str
    :return: datetime.
    :rtype: datetime
    """
    try:
        from dateutil.parser import parse
        return parse(string)
    except ImportError:
        return string


def deserialize_model(data, klass):
    """Deserializes list or dict to model.

    :param data: dict, list.
    :type data: dict | list
    :param klass: class literal.
    :return: model object.
    """
    instance = klass()

    if not instance.openapi_types:
        return data

    for attr, attr_type in six.iteritems(instance.openapi_types):
        if data is not None \
                and instance.attribute_map[attr] in data \
                and isinstance(data, (list, dict)):
            value = data[instance.attribute_map[attr]]
            setattr(instance, attr, _deserialize(value, attr_type))

    return instance


def _deserialize_list(data, boxed_type):
    """Deserializes a list and its elements.

    :param data: list to deserialize.
    :type data: list
    :param boxed_type: class literal.

    :return: deserialized list.
    :rtype: list
    """
    return [_deserialize(sub_data, boxed_type)
            for sub_data in data]


def _deserialize_dict(data, boxed_type):
    """Deserializes a dict and its elements.

    :param data: dict to deserialize.
    :type data: dict
    :param boxed_type: class literal.

    :return: deserialized dict.
    :rtype: dict
    """
    return {k: _deserialize(v, boxed_type)
            for k, v in six.iteritems(data)}


# code/UI/OpenAPI/python-flask-server/openapi_server/util.py

import datetime

import six
import typing
from openapi_server import typing_utils


def _deserialize(data, klass):
    """Deserializes dict, list, str into an object.

    :param data: dict, list or str.
    :param klass: class literal, or string of class name.

    :return: object.
    """
    if data is None:
        return None

    if klass in six.integer_types or klass in (float, str, bool, bytearray):
        return _deserialize_primitive(data, klass)
    elif klass == object:
        return _deserialize_object(data)
    elif klass == datetime.date:
        return deserialize_date(data)
    elif klass == datetime.datetime:
        return deserialize_datetime(data)
    elif typing_utils.is_generic(klass):
        if typing_utils.is_list(klass):
            return _deserialize_list(data, klass.__args__[0])
        if typing_utils.is_dict(klass):
            return _deserialize_dict(data, klass.__args__[1])
    else:
        return deserialize_model(data, klass)


def _deserialize_primitive(data, klass):
    """Deserializes to primitive type.

    :param data: data to deserialize.
    :param klass: class literal.

    :return: int, long, float, str, bool.
    :rtype: int | long | float | str | bool
    """
    try:
        value = klass(data)
    except UnicodeEncodeError:
        value = six.u(data)
    except TypeError:
        value = data
    return value


def _deserialize_object(value):
    """Return an original value.

    :return: object.
    """
    return value


def deserialize_date(string):
    """Deserializes string to date.

    :param string: str.
    :type string: str
    :return: date.
    :rtype: date
    """
    if string is None:
      return None
    
    try:
        from dateutil.parser import parse
        return parse(string).date()
    except ImportError:
        return string


def deserialize_datetime(string):
    """Deserializes string to datetime.

    The string should be in iso8601 datetime format.

    :param string: str.
    :type string: str
    :return: datetime.
    :rtype: datetime
    """
    if string is None:
      return None
    
    try:
        from dateutil.parser import parse
        return parse(string)
    except ImportError:
        return string


def deserialize_model(data, klass):
    """Deserializes list or dict to model.

    :param data: dict, list.
    :type data: dict | list
    :param klass: class literal.
    :return: model object.
    """
    instance = klass()

    if not instance.openapi_types:
        return data

    for attr, attr_type in six.iteritems(instance.openapi_types):
        if data is not None \
                and instance.attribute_map[attr] in data \
                and isinstance(data, (list, dict)):
            value = data[instance.attribute_map[attr]]
            setattr(instance, attr, _deserialize(value, attr_type))

    return instance


def _deserialize_list(data, boxed_type):
    """Deserializes a list and its elements.

    :param data: list to deserialize.
    :type data: list
    :param boxed_type: class literal.

    :return: deserialized list.
    :rtype: list
    """
    return [_deserialize(sub_data, boxed_type)
            for sub_data in data]


def _deserialize_dict(data, boxed_type):
    """Deserializes a dict and its elements.

    :param data: dict to deserialize.
    :type data: dict
    :param boxed_type: class literal.

    :return: deserialized dict.
    :rtype: dict
    """
    return {k: _deserialize(v, boxed_type)
            for k, v in six.iteritems(data)}


# code/UI/OpenAPI/python-flask-server/workflow2yaml.py

#!/usr/bin/python3
import sys
def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs)

import os
import sys
import re
import json
import requests
import json
import yaml



############################################ Main ############################################################
def main():

    #### Parse command line options
    import argparse
    argparser = argparse.ArgumentParser(description='Fetch and convert the latest workflow operations definitions and convert to YAML')
    argparser.add_argument('--verbose', action='count', help='If set, print more information about ongoing processing' )
    params = argparser.parse_args()

    response_content = requests.get('http://standards.ncats.io/operation/1.3.2/schema', headers={'accept': 'application/json'})
    status_code = response_content.status_code

    if status_code != 200:
        eprint("Cannot fetch operation.json")
        return

    #### Unpack the response content into a dict
    try:
        response_dict = response_content.json()
    except:
        eprint("Cannot decode request response")
        return

    print(json.dumps(response_dict,indent=2))

    response_dict['components'] = {}
    response_dict['components']['schemas'] = response_dict['$defs']
    del response_dict['$defs']
    
    # replace $def with components/schemas for all of this list items in anyOf
    response_dict['anyOf'] = [{'$ref': x['$ref'].replace('$defs','components/schemas')} for x in response_dict['anyOf']]
    print(yaml.dump(response_dict))

    return


if __name__ == "__main__": main()


# code/UI/OpenAPI/python-flask-server/yaml_reformat.py

#!/usr/bin/python3
import yaml



############################################ Main ############################################################
def main():

    input_filename = 'RTX_OA3_TRAPI1.3_ARAX.yaml'
    output_filename = 'RTX_OA3_TRAPI1.3_ARAX_reformat.yaml'

    with open(input_filename) as infile:
        content = yaml.safe_load(infile)

    with open(output_filename, 'w') as outfile:
        yaml.dump(content, outfile)

    return


if __name__ == "__main__": main()


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/controllers/asyncquery_controller.py

import connexion
import six

import os
import sys

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../../../../../ARAX/ARAXQuery")
from ARAX_query import ARAXQuery


def asyncquery(request_body):  # noqa: E501
    """Initiate a query with a callback to receive the response

     # noqa: E501

    :param request_body: Query information to be submitted
    :type request_body: Dict[str, ]

    :rtype: AsyncQueryResponse
    """

    # Note that we never even get here if the request_body is not schema-valid JSON

    query = connexion.request.get_json()

    #### Record the remote IP address in the query for now so it is available downstream
    try:
        query['remote_address'] = connexion.request.headers['x-forwarded-for']
    except:
        query['remote_address'] = '???'

    araxq = ARAXQuery()

    envelope = araxq.query_return_message(query, mode='RTXKG2')
    http_status = 200
    if hasattr(envelope, 'http_status'):
        http_status = envelope.http_status
    return(envelope,http_status)



# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/controllers/asyncquery_status_controller.py

import connexion
import six

from openapi_server.models.async_query_status_response import AsyncQueryStatusResponse  # noqa: E501
from openapi_server import util


def asyncquery_status(job_id):  # noqa: E501
    """Retrieve the current status of a previously submitted asyncquery given its job_id

     # noqa: E501

    :param job_id: Identifier of the job for status request
    :type job_id: str

    :rtype: AsyncQueryStatusResponse
    """
    return 'do some magic!'


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/controllers/entity_controller.py

import connexion
import six

from openapi_server.models.entity_query import EntityQuery  # noqa: E501
from openapi_server import util

import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../../../../../ARAX/NodeSynonymizer")
from node_synonymizer import NodeSynonymizer


def get_entity(q):  # noqa: E501
    """Obtain CURIE and synonym information about a search term

     # noqa: E501

    :param q: A string to search by (name, abbreviation, CURIE, etc.). The parameter may be repeated for multiple search strings.
    :type q: List[str]

    :rtype: object
    """
    synonymizer = NodeSynonymizer()
    response = synonymizer.get_normalizer_results(q)

    return response


def post_entity(body):  # noqa: E501
    """Obtain CURIE and synonym information about search terms

     # noqa: E501

    :param body: List of terms to get information about
    :type body: 

    :rtype: EntityQuery
    """

    synonymizer = NodeSynonymizer()
    response = synonymizer.get_normalizer_results(body)

    return response



# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/controllers/meta_knowledge_graph_controller.py

import connexion
import six
import os
import sys

from openapi_server.models.meta_knowledge_graph import MetaKnowledgeGraph  # noqa: E501
from openapi_server import util

sys.path.append(os.path.dirname(os.path.abspath(__file__))+"/../../../../../../ARAX/KnowledgeSources")
from knowledge_source_metadata import KnowledgeSourceMetadata


def meta_knowledge_graph(format_=None):  # noqa: E501
    """Meta knowledge graph representation of this TRAPI web service.

     # noqa: E501

    :param format_: Provide meta_knowledge_graph information in a format other than the default. Default value is &#39;full&#39;. Also permitted is &#39;simple&#39;
    :type format: str

    :rtype: MetaKnowledgeGraph
    """
    ksm = KnowledgeSourceMetadata()
    return(ksm.get_meta_knowledge_graph(format_=format_))



# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/controllers/query_controller.py

import connexion, flask
import json
import os, sys, signal
import resource
import logging
import traceback
from typing import Iterable, Callable

rlimit_child_process_bytes = 34359738368  # 32 GiB

sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../../../../../../ARAX/ARAXQuery")
import ARAX_query

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)) + "/../models")
import response


def child_receive_sigpipe(signal_number, frame):
    if signal_number == signal.SIGPIPE:
        logging.info("[query_controller]: child process detected a SIGPIPE; exiting python")
        os._exit(0)

def run_query_dict_in_child_process(query_dict: dict,
                                    query_runner: Callable) -> Iterable[str]:
    logging.debug("[query_controller]: Creating pipe and forking a child to handle the query")
    read_fd, write_fd = os.pipe()

    # always flush stdout and stderr before calling fork(); someone could have turned off auto-flushing and we don't want double-output
    sys.stderr.flush()
    sys.stdout.flush()

    pid = os.fork()

    if pid == 0: # I am the child process
        sys.stdout = open('/dev/null', 'w')         # parent and child process should not share the same stdout stream object
        sys.stdin = open('/dev/null', 'r')          # parent and child process should not share the same stdin stream object
        os.close(read_fd)                   # child doesn't read from the pipe, it writes to it 
        resource.setrlimit(resource.RLIMIT_AS, (rlimit_child_process_bytes, rlimit_child_process_bytes))  # set a virtual memory limit for the child process
        signal.signal(signal.SIGPIPE, child_receive_sigpipe) # get rid of signal handler so we don't double-print to the log on SIGPIPE error
        signal.signal(signal.SIGCHLD, signal.SIG_IGN) # disregard any SIGCHLD signal in the child process
        try:
            with os.fdopen(write_fd, "w") as write_fo:  # child process needs to get a stream object for the file descriptor `write_fd`
                json_string_generator = query_runner(query_dict)
                for json_string in json_string_generator:
                    write_fo.write(json_string)
                    write_fo.flush()
        except BaseException as e:
            print(f"Exception in query_controller.run_query_dict_in_child_process: {type(e)}\n{traceback.print_exc()}", file=sys.stderr)
            os._exit(1)
        os._exit(0)
    elif pid > 0: # I am the parent process
        os.close(write_fd)  # the parent does not write to the pipe, it reads from it
        logging.debug(f"[query_controller]: child process pid={pid}")
        read_fo = os.fdopen(read_fd, "r")
    else:
        logging.error("[query_controller]: fork() unsuccessful")
        assert False, "********** fork() unsuccessful; something went very wrong *********"
    return read_fo


def _run_query_and_return_json_generator_nonstream(query_dict: dict) -> Iterable[str]:
    envelope = ARAX_query.ARAXQuery().query_return_message(query_dict, mode='RTXKG2')
    envelope_dict = envelope.to_dict()
    if hasattr(envelope, 'http_status'):
        envelope_dict['http_status'] = envelope.http_status
    else:
        envelope_dict['http_status'] = 200
    return (json.dumps(envelope_dict), )


def _run_query_and_return_json_generator_stream(query_dict: dict) -> Iterable[str]:
    return ARAX_query.ARAXQuery().query_return_stream(query_dict, mode='RTXKG2')


def query(request_body):  # noqa: E501
    """Initiate a query and wait to receive a Response

     # noqa: E501

    :param request_body: Query information to be submitted
    :type request_body: Dict[str, ]

    :rtype: Response
    """

    # Note that we never even get here if the request_body is not schema-valid JSON

    query = connexion.request.get_json()  # :QUESTION: why don't we use `request_body`?

    #### Record the remote IP address in the query for now so it is available downstream
    try:
        query['remote_address'] = connexion.request.headers['x-forwarded-for']
    except:
        query['remote_address'] = '???'

    mime_type = 'application/json'

    if query.get('stream_progress', False):  # if stream_progress is specified and if it is True:

        fork_mode = True # :DEBUG: can turn this to False to disable fork-mode
        http_status = None
        mime_type = 'text/event-stream'

        if not fork_mode:
            json_generator = _run_query_and_return_json_generator_stream(query)
        else:
            json_generator = run_query_dict_in_child_process(query,
                                                             _run_query_and_return_json_generator_stream)

        resp_obj = flask.Response(json_generator, mimetype=mime_type)
    # Else perform the query and return the result
        http_status = None

    else:
        json_generator = run_query_dict_in_child_process(query,
                                                         _run_query_and_return_json_generator_nonstream)
        the_dict = json.loads(next(json_generator))
        http_status = the_dict.get('http_status', 200)
        resp_obj = response.Response.from_dict(the_dict)
        resp_obj.http_status = http_status

    return (resp_obj, http_status)


# :TESTING: vvvvvvvvvvvvvvvvvv
# if __name__ == "__main__":
#     signal.signal(signal.SIGPIPE, signal.SIG_IGN)
#     query_dict = {
#         "operations": {
#             "actions": [
#                 "add_qnode(ids=[CHEMBL.COMPOUND:CHEMBL112], key=n00)",
#                 "add_qnode(ids=[UniProtKB:P55000], key=n01)",
#                 "add_qedge(subject=n00, object=n01, key=e00)",
#                 "expand(edge_key=e00,kp=RTX-KG2)",
#                 "resultify()",
#                 "return(message=true, store=false)",
#             ]
#         }
#     }
#     for json_str in run_query_dict_in_child_process(query_dict, _run_query_and_return_json_generator_stream):
#         print(json_str)
# :TESTING: ^^^^^^^^^^^^^^^^^^


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/controllers/security_controller_.py

from typing import List




# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/controllers/status_controller.py

import six
import os
import sys

from openapi_server import util
from ARAX_query_tracker import ARAXQueryTracker


def get_status(last_n_hours=None, id_=None, terminate_pid=None, authorization=None):  # noqa: E501
    """Obtain status information about the endpoint

     # noqa: E501

    :param last_n_hours: Limit results to the past N hours
    :type last_n_hours: int
    :param id: Identifier of the log entry
    :type id: int
    :param terminate_pid: PID of an ongoing query to terminate
    :type terminate_pid: int
    :param authorization: Authorization string required for certain calls to status
    :type authorization: str

    :rtype: object
    """

    query_tracker = ARAXQueryTracker()
    if terminate_pid is not None:
        status = query_tracker.terminate_job(terminate_pid, authorization)
    else:
        status = query_tracker.get_status(last_n_hours=last_n_hours, id_=id_)
    return status


def get_logs(mode=None):  # noqa: E501
    """Get log information from the server

     # noqa: E501

    :param mode: Specify the log sending mode
    :type mode: string

    :rtype: string
    """

    if mode is not None and mode == 'env':
        return str(os.environ)

    query_tracker = ARAXQueryTracker()
    status = query_tracker.get_logs(mode=mode)
    return status


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/test/__init__.py

import logging

import connexion
from flask_testing import TestCase

from openapi_server.encoder import JSONEncoder


class BaseTestCase(TestCase):

    def create_app(self):
        logging.getLogger('connexion.operation').setLevel('ERROR')
        app = connexion.App(__name__, specification_dir='../openapi/')
        app.app.json_encoder = JSONEncoder
        app.add_api('openapi.yaml', pythonic_params=True)
        return app.app


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/test/test_entity_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.test import BaseTestCase


class TestEntityController(BaseTestCase):
    """EntityController integration test stubs"""

    def test_get_entity(self):
        """Test case for get_entity

        Obtain CURIE and synonym information about a search term
        """
        query_string = [('q', ["MESH:D014867","NCIT:C34373"])]
        headers = { 
            'Accept': 'application/json',
        }
        response = self.client.open(
            '/api/rtxkg2/v1.0/entity',
            method='GET',
            headers=headers,
            query_string=query_string)
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/test/test_predicates_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.test import BaseTestCase


class TestPredicatesController(BaseTestCase):
    """PredicatesController integration test stubs"""

    def test_predicates(self):
        """Test case for predicates

        Get supported relationships by source and target
        """
        headers = { 
            'Accept': 'application/json',
        }
        response = self.client.open(
            '/api/rtxkg2/v1.0/predicates',
            method='GET',
            headers=headers)
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/test/test_query_controller.py

# coding: utf-8

from __future__ import absolute_import
import unittest

from flask import json
from six import BytesIO

from openapi_server.models.response import Response  # noqa: E501
from openapi_server.test import BaseTestCase


class TestQueryController(BaseTestCase):
    """QueryController integration test stubs"""

    def test_query(self):
        """Test case for query

        Query reasoner via one of several inputs
        """
        request_body = None
        query_string = [('bypass_cache', False)]
        headers = { 
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
        response = self.client.open(
            '/api/rtxkg2/v1.0/query',
            method='POST',
            headers=headers,
            data=json.dumps(request_body),
            content_type='application/json',
            query_string=query_string)
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))


if __name__ == '__main__':
    unittest.main()


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/__main__.py

#!/usr/bin/env python3

import connexion, flask, flask_cors
import logging
import json
import openapi_server.encoder
import os, sys, signal, atexit

logging.basicConfig(level=logging.INFO)  # can change this to logging.DEBUG for debuggging

@atexit.register
def ignore_sigchld():
    logging.debug("Setting SIGCHLD to SIG_IGN before exiting")
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)


def receive_sigchld(signal_number, frame):
    if signal_number == signal.SIGCHLD:
        while True:
            try:
                pid, _ = os.waitpid(-1, os.WNOHANG)
                logging.debug(f"PID returned from call to os.waitpid: {pid}")
                if pid == 0:
                    break
            except ChildProcessError as e:
                logging.debug(repr(e) + "; this is expected if there are no more child processes to reap")
                break

def receive_sigpipe(signal_number, frame):
    if signal_number == signal.SIGPIPE:
        logging.error("pipe error")

def main():
    app = connexion.App(__name__, specification_dir='./openapi/')
    app.app.json_encoder = openapi_server.encoder.JSONEncoder
    app.add_api('openapi.yaml',
                arguments={'title': 'RTX KG2 Translator KP'},
                pythonic_params=True)
    flask_cors.CORS(app.app)
    signal.signal(signal.SIGCHLD, receive_sigchld)
    signal.signal(signal.SIGPIPE, receive_sigpipe)

    #### Read any load configuration details for this instance
    try:
        with open('openapi_server/flask_config.json') as infile:
            local_config = json.load(infile)
    except:
        local_config = { "port": 5008 }

    #### Start the service
    app.run(port=local_config['port'], threaded=True)


if __name__ == '__main__':
    main()


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/encoder.py

from connexion.apps.flask_app import FlaskJSONEncoder
import six

from openapi_server.models.base_model_ import Model


class JSONEncoder(FlaskJSONEncoder):
    include_nulls = False

    def default(self, o):
        if isinstance(o, Model):
            dikt = {}
            for attr, _ in six.iteritems(o.openapi_types):
                value = getattr(o, attr)
                if value is None and not self.include_nulls:
                    continue
                attr = o.attribute_map[attr]
                dikt[attr] = value
            return dikt
        return FlaskJSONEncoder.default(self, o)


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/typing_utils.py

# coding: utf-8

import sys

if sys.version_info < (3, 7):
    import typing

    def is_generic(klass):
        """ Determine whether klass is a generic class """
        return type(klass) == typing.GenericMeta

    def is_dict(klass):
        """ Determine whether klass is a Dict """
        return klass.__extra__ == dict

    def is_list(klass):
        """ Determine whether klass is a List """
        return klass.__extra__ == list

else:

    def is_generic(klass):
        """ Determine whether klass is a generic class """
        return hasattr(klass, '__origin__')

    def is_dict(klass):
        """ Determine whether klass is a Dict """
        return klass.__origin__ == dict

    def is_list(klass):
        """ Determine whether klass is a List """
        return klass.__origin__ == list


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/util.py

import datetime

import six
import typing
from openapi_server import typing_utils


def _deserialize(data, klass):
    """Deserializes dict, list, str into an object.

    :param data: dict, list or str.
    :param klass: class literal, or string of class name.

    :return: object.
    """
    if data is None:
        return None

    if klass in six.integer_types or klass in (float, str, bool, bytearray):
        return _deserialize_primitive(data, klass)
    elif klass == object:
        return _deserialize_object(data)
    elif klass == datetime.date:
        return deserialize_date(data)
    elif klass == datetime.datetime:
        return deserialize_datetime(data)
    elif typing_utils.is_generic(klass):
        if typing_utils.is_list(klass):
            return _deserialize_list(data, klass.__args__[0])
        if typing_utils.is_dict(klass):
            return _deserialize_dict(data, klass.__args__[1])
    else:
        return deserialize_model(data, klass)


def _deserialize_primitive(data, klass):
    """Deserializes to primitive type.

    :param data: data to deserialize.
    :param klass: class literal.

    :return: int, long, float, str, bool.
    :rtype: int | long | float | str | bool
    """
    try:
        value = klass(data)
    except UnicodeEncodeError:
        value = six.u(data)
    except TypeError:
        value = data
    return value


def _deserialize_object(value):
    """Return an original value.

    :return: object.
    """
    return value


def deserialize_date(string):
    """Deserializes string to date.

    :param string: str.
    :type string: str
    :return: date.
    :rtype: date
    """
    if string is None:
      return None
    
    try:
        from dateutil.parser import parse
        return parse(string).date()
    except ImportError:
        return string


def deserialize_datetime(string):
    """Deserializes string to datetime.

    The string should be in iso8601 datetime format.

    :param string: str.
    :type string: str
    :return: datetime.
    :rtype: datetime
    """
    if string is None:
      return None
    
    try:
        from dateutil.parser import parse
        return parse(string)
    except ImportError:
        return string


def deserialize_model(data, klass):
    """Deserializes list or dict to model.

    :param data: dict, list.
    :type data: dict | list
    :param klass: class literal.
    :return: model object.
    """
    instance = klass()

    if not instance.openapi_types:
        return data

    for attr, attr_type in six.iteritems(instance.openapi_types):
        if data is not None \
                and instance.attribute_map[attr] in data \
                and isinstance(data, (list, dict)):
            value = data[instance.attribute_map[attr]]
            setattr(instance, attr, _deserialize(value, attr_type))

    return instance


def _deserialize_list(data, boxed_type):
    """Deserializes a list and its elements.

    :param data: list to deserialize.
    :type data: list
    :param boxed_type: class literal.

    :return: deserialized list.
    :rtype: list
    """
    return [_deserialize(sub_data, boxed_type)
            for sub_data in data]


def _deserialize_dict(data, boxed_type):
    """Deserializes a dict and its elements.

    :param data: dict to deserialize.
    :type data: dict
    :param boxed_type: class literal.

    :return: deserialized dict.
    :rtype: dict
    """
    return {k: _deserialize(v, boxed_type)
            for k, v in six.iteritems(data)}


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/__init__.py

# coding: utf-8

# flake8: noqa
from __future__ import absolute_import
# import models into model package
from openapi_server.models.async_query import AsyncQuery
from openapi_server.models.attribute import Attribute
from openapi_server.models.attribute_constraint import AttributeConstraint
from openapi_server.models.edge import Edge
from openapi_server.models.edge_binding import EdgeBinding
from openapi_server.models.entity_query import EntityQuery
from openapi_server.models.knowledge_graph import KnowledgeGraph
from openapi_server.models.log_entry import LogEntry
from openapi_server.models.log_level import LogLevel
from openapi_server.models.message import Message
from openapi_server.models.meta_attribute import MetaAttribute
from openapi_server.models.meta_edge import MetaEdge
from openapi_server.models.meta_knowledge_graph import MetaKnowledgeGraph
from openapi_server.models.meta_node import MetaNode
from openapi_server.models.node import Node
from openapi_server.models.node_binding import NodeBinding
from openapi_server.models.operation_lookup import OperationLookup
from openapi_server.models.operations import Operations
from openapi_server.models.q_edge import QEdge
from openapi_server.models.q_node import QNode
from openapi_server.models.qualifier import Qualifier
from openapi_server.models.qualifier_constraint import QualifierConstraint
from openapi_server.models.query import Query
from openapi_server.models.query_graph import QueryGraph
from openapi_server.models.response import Response
from openapi_server.models.result import Result


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/any_type.py

class AnyType():
    """NOTE: This class should be auto generated by OpenAPI Generator (https://openapi-generator.tech).
        But it is mysteriously not, so it was hand-coded by Eric Deutsch
        It is a stub type that stands in for any OpenAPI type
    """

    def __init__(self):

        # If openapi_types is None, then util.deserialize_model just returns the data as is,
        # which is what we want for AnyType
        self.openapi_types = None



# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/async_query.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_level import LogLevel
from openapi_server.models.message import Message
from openapi_server.models.any_type import AnyType
import re
from openapi_server import util

from openapi_server.models.log_level import LogLevel  # noqa: E501
from openapi_server.models.message import Message  # noqa: E501
from openapi_server.models.any_type import AnyType  # noqa: E501
import re  # noqa: E501

class AsyncQuery(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, callback=None, message=None, log_level=None, workflow=None, submitter=None):  # noqa: E501
        """AsyncQuery - a model defined in OpenAPI

        :param callback: The callback of this AsyncQuery.  # noqa: E501
        :type callback: str
        :param message: The message of this AsyncQuery.  # noqa: E501
        :type message: Message
        :param log_level: The log_level of this AsyncQuery.  # noqa: E501
        :type log_level: LogLevel
        :param workflow: The workflow of this AsyncQuery.  # noqa: E501
        :type workflow: List[AnyType]
        :param submitter: The submitter of this AsyncQuery.  # noqa: E501
        :type submitter: str
        """
        self.openapi_types = {
            'callback': str,
            'message': Message,
            'log_level': LogLevel,
            'workflow': List[AnyType],
            'submitter': str
        }

        self.attribute_map = {
            'callback': 'callback',
            'message': 'message',
            'log_level': 'log_level',
            'workflow': 'workflow',
            'submitter': 'submitter'
        }

        self._callback = callback
        self._message = message
        self._log_level = log_level
        self._workflow = workflow
        self._submitter = submitter

    @classmethod
    def from_dict(cls, dikt) -> 'AsyncQuery':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The AsyncQuery of this AsyncQuery.  # noqa: E501
        :rtype: AsyncQuery
        """
        return util.deserialize_model(dikt, cls)

    @property
    def callback(self):
        """Gets the callback of this AsyncQuery.

        Upon completion, this server will send a POST request to the callback URL with `Content-Type: application/json` header and request body containing a JSON-encoded `Response` object. The server MAY POST `Response` objects before work is fully complete to provide interim results with a Response.status value of 'Running'. If a POST operation to the callback URL does not succeed, the server SHOULD retry the POST at least once.  # noqa: E501

        :return: The callback of this AsyncQuery.
        :rtype: str
        """
        return self._callback

    @callback.setter
    def callback(self, callback):
        """Sets the callback of this AsyncQuery.

        Upon completion, this server will send a POST request to the callback URL with `Content-Type: application/json` header and request body containing a JSON-encoded `Response` object. The server MAY POST `Response` objects before work is fully complete to provide interim results with a Response.status value of 'Running'. If a POST operation to the callback URL does not succeed, the server SHOULD retry the POST at least once.  # noqa: E501

        :param callback: The callback of this AsyncQuery.
        :type callback: str
        """
        if callback is None:
            raise ValueError("Invalid value for `callback`, must not be `None`")  # noqa: E501
        if callback is not None and not re.search(r'^https?:\/\/', callback):  # noqa: E501
            raise ValueError("Invalid value for `callback`, must be a follow pattern or equal to `/^https?:\/\//`")  # noqa: E501

        self._callback = callback

    @property
    def message(self):
        """Gets the message of this AsyncQuery.


        :return: The message of this AsyncQuery.
        :rtype: Message
        """
        return self._message

    @message.setter
    def message(self, message):
        """Sets the message of this AsyncQuery.


        :param message: The message of this AsyncQuery.
        :type message: Message
        """
        if message is None:
            raise ValueError("Invalid value for `message`, must not be `None`")  # noqa: E501

        self._message = message

    @property
    def log_level(self):
        """Gets the log_level of this AsyncQuery.

        The least critical level of logs to return  # noqa: E501

        :return: The log_level of this AsyncQuery.
        :rtype: LogLevel
        """
        return self._log_level

    @log_level.setter
    def log_level(self, log_level):
        """Sets the log_level of this AsyncQuery.

        The least critical level of logs to return  # noqa: E501

        :param log_level: The log_level of this AsyncQuery.
        :type log_level: LogLevel
        """

        self._log_level = log_level

    @property
    def workflow(self):
        """Gets the workflow of this AsyncQuery.

        List of workflow steps to be executed.  # noqa: E501

        :return: The workflow of this AsyncQuery.
        :rtype: List[AnyType]
        """
        return self._workflow

    @workflow.setter
    def workflow(self, workflow):
        """Sets the workflow of this AsyncQuery.

        List of workflow steps to be executed.  # noqa: E501

        :param workflow: The workflow of this AsyncQuery.
        :type workflow: List[AnyType]
        """

        self._workflow = workflow

    @property
    def submitter(self):
        """Gets the submitter of this AsyncQuery.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :return: The submitter of this AsyncQuery.
        :rtype: str
        """
        return self._submitter

    @submitter.setter
    def submitter(self, submitter):
        """Sets the submitter of this AsyncQuery.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :param submitter: The submitter of this AsyncQuery.
        :type submitter: str
        """

        self._submitter = submitter


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/async_query_response.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class AsyncQueryResponse(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, status=None, description=None, job_id=None):  # noqa: E501
        """AsyncQueryResponse - a model defined in OpenAPI

        :param status: The status of this AsyncQueryResponse.  # noqa: E501
        :type status: str
        :param description: The description of this AsyncQueryResponse.  # noqa: E501
        :type description: str
        :param job_id: The job_id of this AsyncQueryResponse.  # noqa: E501
        :type job_id: str
        """
        self.openapi_types = {
            'status': str,
            'description': str,
            'job_id': str
        }

        self.attribute_map = {
            'status': 'status',
            'description': 'description',
            'job_id': 'job_id'
        }

        self._status = status
        self._description = description
        self._job_id = job_id

    @classmethod
    def from_dict(cls, dikt) -> 'AsyncQueryResponse':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The AsyncQueryResponse of this AsyncQueryResponse.  # noqa: E501
        :rtype: AsyncQueryResponse
        """
        return util.deserialize_model(dikt, cls)

    @property
    def status(self):
        """Gets the status of this AsyncQueryResponse.

        One of a standardized set of short codes: e.g. Accepted, QueryNotTraversable, KPsNotAvailable  # noqa: E501

        :return: The status of this AsyncQueryResponse.
        :rtype: str
        """
        return self._status

    @status.setter
    def status(self, status):
        """Sets the status of this AsyncQueryResponse.

        One of a standardized set of short codes: e.g. Accepted, QueryNotTraversable, KPsNotAvailable  # noqa: E501

        :param status: The status of this AsyncQueryResponse.
        :type status: str
        """

        self._status = status

    @property
    def description(self):
        """Gets the description of this AsyncQueryResponse.

        A brief human-readable description of the result of the async_query submission.  # noqa: E501

        :return: The description of this AsyncQueryResponse.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this AsyncQueryResponse.

        A brief human-readable description of the result of the async_query submission.  # noqa: E501

        :param description: The description of this AsyncQueryResponse.
        :type description: str
        """

        self._description = description

    @property
    def job_id(self):
        """Gets the job_id of this AsyncQueryResponse.

        An identifier for the submitted job that can be used with /async_query_status to receive an update on the status of the job.  # noqa: E501

        :return: The job_id of this AsyncQueryResponse.
        :rtype: str
        """
        return self._job_id

    @job_id.setter
    def job_id(self, job_id):
        """Sets the job_id of this AsyncQueryResponse.

        An identifier for the submitted job that can be used with /async_query_status to receive an update on the status of the job.  # noqa: E501

        :param job_id: The job_id of this AsyncQueryResponse.
        :type job_id: str
        """
        if job_id is None:
            raise ValueError("Invalid value for `job_id`, must not be `None`")  # noqa: E501

        self._job_id = job_id


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/async_query_status_response.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_entry import LogEntry
from openapi_server import util

from openapi_server.models.log_entry import LogEntry  # noqa: E501

class AsyncQueryStatusResponse(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, status=None, description=None, logs=None, response_url=None):  # noqa: E501
        """AsyncQueryStatusResponse - a model defined in OpenAPI

        :param status: The status of this AsyncQueryStatusResponse.  # noqa: E501
        :type status: str
        :param description: The description of this AsyncQueryStatusResponse.  # noqa: E501
        :type description: str
        :param logs: The logs of this AsyncQueryStatusResponse.  # noqa: E501
        :type logs: List[LogEntry]
        :param response_url: The response_url of this AsyncQueryStatusResponse.  # noqa: E501
        :type response_url: str
        """
        self.openapi_types = {
            'status': str,
            'description': str,
            'logs': List[LogEntry],
            'response_url': str
        }

        self.attribute_map = {
            'status': 'status',
            'description': 'description',
            'logs': 'logs',
            'response_url': 'response_url'
        }

        self._status = status
        self._description = description
        self._logs = logs
        self._response_url = response_url

    @classmethod
    def from_dict(cls, dikt) -> 'AsyncQueryStatusResponse':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The AsyncQueryStatusResponse of this AsyncQueryStatusResponse.  # noqa: E501
        :rtype: AsyncQueryStatusResponse
        """
        return util.deserialize_model(dikt, cls)

    @property
    def status(self):
        """Gets the status of this AsyncQueryStatusResponse.

        One of a standardized set of short codes: Queued, Running, Completed, Failed  # noqa: E501

        :return: The status of this AsyncQueryStatusResponse.
        :rtype: str
        """
        return self._status

    @status.setter
    def status(self, status):
        """Sets the status of this AsyncQueryStatusResponse.

        One of a standardized set of short codes: Queued, Running, Completed, Failed  # noqa: E501

        :param status: The status of this AsyncQueryStatusResponse.
        :type status: str
        """
        if status is None:
            raise ValueError("Invalid value for `status`, must not be `None`")  # noqa: E501

        self._status = status

    @property
    def description(self):
        """Gets the description of this AsyncQueryStatusResponse.

        A brief human-readable description of the current state or summary of the problem if the status is Failed.  # noqa: E501

        :return: The description of this AsyncQueryStatusResponse.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this AsyncQueryStatusResponse.

        A brief human-readable description of the current state or summary of the problem if the status is Failed.  # noqa: E501

        :param description: The description of this AsyncQueryStatusResponse.
        :type description: str
        """
        if description is None:
            raise ValueError("Invalid value for `description`, must not be `None`")  # noqa: E501

        self._description = description

    @property
    def logs(self):
        """Gets the logs of this AsyncQueryStatusResponse.

        A list of LogEntry items, containing errors, warnings, debugging information, etc. List items MUST be in chronological order with earliest first. The most recent entry should be last. Its timestamp will be compared against the current time to see if there is still activity.  # noqa: E501

        :return: The logs of this AsyncQueryStatusResponse.
        :rtype: List[LogEntry]
        """
        return self._logs

    @logs.setter
    def logs(self, logs):
        """Sets the logs of this AsyncQueryStatusResponse.

        A list of LogEntry items, containing errors, warnings, debugging information, etc. List items MUST be in chronological order with earliest first. The most recent entry should be last. Its timestamp will be compared against the current time to see if there is still activity.  # noqa: E501

        :param logs: The logs of this AsyncQueryStatusResponse.
        :type logs: List[LogEntry]
        """
        if logs is None:
            raise ValueError("Invalid value for `logs`, must not be `None`")  # noqa: E501

        self._logs = logs

    @property
    def response_url(self):
        """Gets the response_url of this AsyncQueryStatusResponse.

        Optional URL that can be queried to restrieve the full TRAPI Response.  # noqa: E501

        :return: The response_url of this AsyncQueryStatusResponse.
        :rtype: str
        """
        return self._response_url

    @response_url.setter
    def response_url(self, response_url):
        """Sets the response_url of this AsyncQueryStatusResponse.

        Optional URL that can be queried to restrieve the full TRAPI Response.  # noqa: E501

        :param response_url: The response_url of this AsyncQueryStatusResponse.
        :type response_url: str
        """

        self._response_url = response_url


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/attribute.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class Attribute(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, attribute_type_id=None, original_attribute_name=None, value=None, value_type_id=None, attribute_source=None, value_url=None, description=None, attributes=None):  # noqa: E501
        """Attribute - a model defined in OpenAPI

        :param attribute_type_id: The attribute_type_id of this Attribute.  # noqa: E501
        :type attribute_type_id: str
        :param original_attribute_name: The original_attribute_name of this Attribute.  # noqa: E501
        :type original_attribute_name: str
        :param value: The value of this Attribute.  # noqa: E501
        :type value: object
        :param value_type_id: The value_type_id of this Attribute.  # noqa: E501
        :type value_type_id: str
        :param attribute_source: The attribute_source of this Attribute.  # noqa: E501
        :type attribute_source: str
        :param value_url: The value_url of this Attribute.  # noqa: E501
        :type value_url: str
        :param description: The description of this Attribute.  # noqa: E501
        :type description: str
        :param attributes: The attributes of this Attribute.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'attribute_type_id': str,
            'original_attribute_name': str,
            'value': object,
            'value_type_id': str,
            'attribute_source': str,
            'value_url': str,
            'description': str,
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'attribute_type_id': 'attribute_type_id',
            'original_attribute_name': 'original_attribute_name',
            'value': 'value',
            'value_type_id': 'value_type_id',
            'attribute_source': 'attribute_source',
            'value_url': 'value_url',
            'description': 'description',
            'attributes': 'attributes'
        }

        self._attribute_type_id = attribute_type_id
        self._original_attribute_name = original_attribute_name
        self._value = value
        self._value_type_id = value_type_id
        self._attribute_source = attribute_source
        self._value_url = value_url
        self._description = description
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'Attribute':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Attribute of this Attribute.  # noqa: E501
        :rtype: Attribute
        """
        return util.deserialize_model(dikt, cls)

    @property
    def attribute_type_id(self):
        """Gets the attribute_type_id of this Attribute.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The attribute_type_id of this Attribute.
        :rtype: str
        """
        return self._attribute_type_id

    @attribute_type_id.setter
    def attribute_type_id(self, attribute_type_id):
        """Sets the attribute_type_id of this Attribute.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param attribute_type_id: The attribute_type_id of this Attribute.
        :type attribute_type_id: str
        """
        if attribute_type_id is None:
            raise ValueError("Invalid value for `attribute_type_id`, must not be `None`")  # noqa: E501

        self._attribute_type_id = attribute_type_id

    @property
    def original_attribute_name(self):
        """Gets the original_attribute_name of this Attribute.

        The term used by the original source of an attribute to describe the meaning or significance of the value it captures. This may be a column name in a source tsv file, or a key in a source json document for the field in the data that held the attribute's value. Capturing this information  where possible lets us preserve what the original source said. Note that the data type is string' but the contents of the field could also be a CURIE of a third party ontology term.  # noqa: E501

        :return: The original_attribute_name of this Attribute.
        :rtype: str
        """
        return self._original_attribute_name

    @original_attribute_name.setter
    def original_attribute_name(self, original_attribute_name):
        """Sets the original_attribute_name of this Attribute.

        The term used by the original source of an attribute to describe the meaning or significance of the value it captures. This may be a column name in a source tsv file, or a key in a source json document for the field in the data that held the attribute's value. Capturing this information  where possible lets us preserve what the original source said. Note that the data type is string' but the contents of the field could also be a CURIE of a third party ontology term.  # noqa: E501

        :param original_attribute_name: The original_attribute_name of this Attribute.
        :type original_attribute_name: str
        """

        self._original_attribute_name = original_attribute_name

    @property
    def value(self):
        """Gets the value of this Attribute.

        Value of the attribute. May be any data type, including a list.  # noqa: E501

        :return: The value of this Attribute.
        :rtype: object
        """
        return self._value

    @value.setter
    def value(self, value):
        """Sets the value of this Attribute.

        Value of the attribute. May be any data type, including a list.  # noqa: E501

        :param value: The value of this Attribute.
        :type value: object
        """
        if value is None:
            raise ValueError("Invalid value for `value`, must not be `None`")  # noqa: E501

        self._value = value

    @property
    def value_type_id(self):
        """Gets the value_type_id of this Attribute.

        CURIE describing the semantic type of an  attribute's value. Use a Biolink class if possible, otherwise a term from an external ontology. If a suitable CURIE/identifier does not exist, enter a descriptive phrase here and submit the new type for consideration by the appropriate authority.  # noqa: E501

        :return: The value_type_id of this Attribute.
        :rtype: str
        """
        return self._value_type_id

    @value_type_id.setter
    def value_type_id(self, value_type_id):
        """Sets the value_type_id of this Attribute.

        CURIE describing the semantic type of an  attribute's value. Use a Biolink class if possible, otherwise a term from an external ontology. If a suitable CURIE/identifier does not exist, enter a descriptive phrase here and submit the new type for consideration by the appropriate authority.  # noqa: E501

        :param value_type_id: The value_type_id of this Attribute.
        :type value_type_id: str
        """

        self._value_type_id = value_type_id

    @property
    def attribute_source(self):
        """Gets the attribute_source of this Attribute.

        The source of the core assertion made by the key-value pair of an attribute object. Use a CURIE or namespace designator for this resource where possible.  # noqa: E501

        :return: The attribute_source of this Attribute.
        :rtype: str
        """
        return self._attribute_source

    @attribute_source.setter
    def attribute_source(self, attribute_source):
        """Sets the attribute_source of this Attribute.

        The source of the core assertion made by the key-value pair of an attribute object. Use a CURIE or namespace designator for this resource where possible.  # noqa: E501

        :param attribute_source: The attribute_source of this Attribute.
        :type attribute_source: str
        """

        self._attribute_source = attribute_source

    @property
    def value_url(self):
        """Gets the value_url of this Attribute.

        Human-consumable URL linking to a web document that provides additional information about an  attribute's value (not the node or the edge fom which it hangs).  # noqa: E501

        :return: The value_url of this Attribute.
        :rtype: str
        """
        return self._value_url

    @value_url.setter
    def value_url(self, value_url):
        """Sets the value_url of this Attribute.

        Human-consumable URL linking to a web document that provides additional information about an  attribute's value (not the node or the edge fom which it hangs).  # noqa: E501

        :param value_url: The value_url of this Attribute.
        :type value_url: str
        """

        self._value_url = value_url

    @property
    def description(self):
        """Gets the description of this Attribute.

        Human-readable description for the attribute and its value.  # noqa: E501

        :return: The description of this Attribute.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this Attribute.

        Human-readable description for the attribute and its value.  # noqa: E501

        :param description: The description of this Attribute.
        :type description: str
        """

        self._description = description

    @property
    def attributes(self):
        """Gets the attributes of this Attribute.

        A list of attributes providing further information about the parent attribute (for example to provide provenance information about the parent attribute).  # noqa: E501

        :return: The attributes of this Attribute.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this Attribute.

        A list of attributes providing further information about the parent attribute (for example to provide provenance information about the parent attribute).  # noqa: E501

        :param attributes: The attributes of this Attribute.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/base_model_.py

import pprint

import six
import typing

from openapi_server import util

T = typing.TypeVar('T')


class Model(object):
    # openapiTypes: The key is attribute name and the
    # value is attribute type.
    openapi_types = {}

    # attributeMap: The key is attribute name and the
    # value is json key in definition.
    attribute_map = {}

    @classmethod
    def from_dict(cls: typing.Type[T], dikt) -> T:
        """Returns the dict as a model"""
        return util.deserialize_model(dikt, cls)

    def to_dict(self):
        """Returns the model properties as a dict

        :rtype: dict
        """
        result = {}

        for attr, _ in six.iteritems(self.openapi_types):
            value = getattr(self, attr)
            if isinstance(value, list):
                result[attr] = list(map(
                    lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
                    value
                ))
            elif hasattr(value, "to_dict"):
                result[attr] = value.to_dict()
            elif isinstance(value, dict):

                #### This only can handle one level of lists or dicts between objects
                #result[attr] = dict(map(
                #    lambda item: (item[0], item[1].to_dict())
                #    if hasattr(item[1], "to_dict") else item,
                #    value.items()
                #))

                #### This is a little fancier in that it can handle two levels, a dict and then
                #### another dict or list between objects. Not the ultimate solution but
                #### perhaps adequate for now?
                result_dict = {}
                for dict_key, dict_value in value.items():
                    if isinstance(dict_value, list):
                        result_dict[dict_key] = list(map(
                            lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
                            dict_value
                        ))
                    elif isinstance(dict_value, dict):
                        result_dict[dict_key] = dict(map(
                            lambda dict_value_item: (dict_value_item[0], dict_value_item[1].to_dict())
                            if hasattr(dict_value_item[1], "to_dict") else dict_value_item,
                            dict_value.items()
                        ))
                    elif hasattr(dict_value, "to_dict"):
                        result_dict[dict_key] = dict_value.to_dict()
                    else:
                        result_dict[dict_key] = dict_value
                result[attr] = result_dict

            else:
                result[attr] = value

        return result

    def to_str(self):
        """Returns the string representation of the model

        :rtype: str
        """
        return pprint.pformat(self.to_dict())

    def __repr__(self):
        """For `print` and `pprint`"""
        return self.to_str()

    def __eq__(self, other):
        """Returns true if both objects are equal"""
        return self.__dict__ == other.__dict__

    def __ne__(self, other):
        """Returns true if both objects are not equal"""
        return not self == other


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/edge.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server.models.qualifier import Qualifier
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501
from openapi_server.models.qualifier import Qualifier  # noqa: E501

class Edge(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, predicate=None, subject=None, object=None, attributes=None, qualifiers=None):  # noqa: E501
        """Edge - a model defined in OpenAPI

        :param predicate: The predicate of this Edge.  # noqa: E501
        :type predicate: str
        :param subject: The subject of this Edge.  # noqa: E501
        :type subject: str
        :param object: The object of this Edge.  # noqa: E501
        :type object: str
        :param attributes: The attributes of this Edge.  # noqa: E501
        :type attributes: List[Attribute]
        :param qualifiers: The qualifiers of this Edge.  # noqa: E501
        :type qualifiers: List[Qualifier]
        """
        self.openapi_types = {
            'predicate': str,
            'subject': str,
            'object': str,
            'attributes': List[Attribute],
            'qualifiers': List[Qualifier]
        }

        self.attribute_map = {
            'predicate': 'predicate',
            'subject': 'subject',
            'object': 'object',
            'attributes': 'attributes',
            'qualifiers': 'qualifiers'
        }

        self._predicate = predicate
        self._subject = subject
        self._object = object
        self._attributes = attributes
        self._qualifiers = qualifiers

    @classmethod
    def from_dict(cls, dikt) -> 'Edge':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Edge of this Edge.  # noqa: E501
        :rtype: Edge
        """
        return util.deserialize_model(dikt, cls)

    @property
    def predicate(self):
        """Gets the predicate of this Edge.


        :return: The predicate of this Edge.
        :rtype: str
        """
        return self._predicate

    @predicate.setter
    def predicate(self, predicate):
        """Sets the predicate of this Edge.


        :param predicate: The predicate of this Edge.
        :type predicate: str
        """

        self._predicate = predicate

    @property
    def subject(self):
        """Gets the subject of this Edge.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The subject of this Edge.
        :rtype: str
        """
        return self._subject

    @subject.setter
    def subject(self, subject):
        """Sets the subject of this Edge.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param subject: The subject of this Edge.
        :type subject: str
        """
        if subject is None:
            raise ValueError("Invalid value for `subject`, must not be `None`")  # noqa: E501

        self._subject = subject

    @property
    def object(self):
        """Gets the object of this Edge.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The object of this Edge.
        :rtype: str
        """
        return self._object

    @object.setter
    def object(self, object):
        """Sets the object of this Edge.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param object: The object of this Edge.
        :type object: str
        """
        if object is None:
            raise ValueError("Invalid value for `object`, must not be `None`")  # noqa: E501

        self._object = object

    @property
    def attributes(self):
        """Gets the attributes of this Edge.

        A list of additional attributes for this edge  # noqa: E501

        :return: The attributes of this Edge.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this Edge.

        A list of additional attributes for this edge  # noqa: E501

        :param attributes: The attributes of this Edge.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes

    @property
    def qualifiers(self):
        """Gets the qualifiers of this Edge.

        A set of Qualifiers that act together to add nuance or detail to the statement expressed in an Edge.  # noqa: E501

        :return: The qualifiers of this Edge.
        :rtype: List[Qualifier]
        """
        return self._qualifiers

    @qualifiers.setter
    def qualifiers(self, qualifiers):
        """Sets the qualifiers of this Edge.

        A set of Qualifiers that act together to add nuance or detail to the statement expressed in an Edge.  # noqa: E501

        :param qualifiers: The qualifiers of this Edge.
        :type qualifiers: List[Qualifier]
        """

        self._qualifiers = qualifiers


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/edge_binding.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501

class EdgeBinding(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, attributes=None):  # noqa: E501
        """EdgeBinding - a model defined in OpenAPI

        :param id: The id of this EdgeBinding.  # noqa: E501
        :type id: str
        :param attributes: The attributes of this EdgeBinding.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'id': str,
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'id': 'id',
            'attributes': 'attributes'
        }

        self._id = id
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'EdgeBinding':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The EdgeBinding of this EdgeBinding.  # noqa: E501
        :rtype: EdgeBinding
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this EdgeBinding.

        The key identifier of a specific KnowledgeGraph Edge.  # noqa: E501

        :return: The id of this EdgeBinding.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this EdgeBinding.

        The key identifier of a specific KnowledgeGraph Edge.  # noqa: E501

        :param id: The id of this EdgeBinding.
        :type id: str
        """
        if id is None:
            raise ValueError("Invalid value for `id`, must not be `None`")  # noqa: E501

        self._id = id

    @property
    def attributes(self):
        """Gets the attributes of this EdgeBinding.

        A list of attributes providing further information about the edge binding. This is not intended for capturing edge attributes and should only be used for properties that vary from result to result.  # noqa: E501

        :return: The attributes of this EdgeBinding.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this EdgeBinding.

        A list of attributes providing further information about the edge binding. This is not intended for capturing edge attributes and should only be used for properties that vary from result to result.  # noqa: E501

        :param attributes: The attributes of this EdgeBinding.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/entity_query.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class EntityQuery(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, terms=None, format=None):  # noqa: E501
        """EntityQuery - a model defined in OpenAPI

        :param terms: The terms of this EntityQuery.  # noqa: E501
        :type terms: List[str]
        :param format: The format of this EntityQuery.  # noqa: E501
        :type format: str
        """
        self.openapi_types = {
            'terms': List[str],
            'format': str
        }

        self.attribute_map = {
            'terms': 'terms',
            'format': 'format'
        }

        self._terms = terms
        self._format = format

    @classmethod
    def from_dict(cls, dikt) -> 'EntityQuery':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The EntityQuery of this EntityQuery.  # noqa: E501
        :rtype: EntityQuery
        """
        return util.deserialize_model(dikt, cls)

    @property
    def terms(self):
        """Gets the terms of this EntityQuery.

        List of term strings to search for  # noqa: E501

        :return: The terms of this EntityQuery.
        :rtype: List[str]
        """
        return self._terms

    @terms.setter
    def terms(self, terms):
        """Sets the terms of this EntityQuery.

        List of term strings to search for  # noqa: E501

        :param terms: The terms of this EntityQuery.
        :type terms: List[str]
        """

        self._terms = terms

    @property
    def format(self):
        """Gets the format of this EntityQuery.

        Format of the response (full or compact)  # noqa: E501

        :return: The format of this EntityQuery.
        :rtype: str
        """
        return self._format

    @format.setter
    def format(self, format):
        """Sets the format of this EntityQuery.

        Format of the response (full or compact)  # noqa: E501

        :param format: The format of this EntityQuery.
        :type format: str
        """

        self._format = format


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/knowledge_graph.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.edge import Edge
from openapi_server.models.node import Node
from openapi_server import util

from openapi_server.models.edge import Edge  # noqa: E501
from openapi_server.models.node import Node  # noqa: E501

class KnowledgeGraph(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, nodes=None, edges=None):  # noqa: E501
        """KnowledgeGraph - a model defined in OpenAPI

        :param nodes: The nodes of this KnowledgeGraph.  # noqa: E501
        :type nodes: Dict[str, Node]
        :param edges: The edges of this KnowledgeGraph.  # noqa: E501
        :type edges: Dict[str, Edge]
        """
        self.openapi_types = {
            'nodes': Dict[str, Node],
            'edges': Dict[str, Edge]
        }

        self.attribute_map = {
            'nodes': 'nodes',
            'edges': 'edges'
        }

        self._nodes = nodes
        self._edges = edges

    @classmethod
    def from_dict(cls, dikt) -> 'KnowledgeGraph':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The KnowledgeGraph of this KnowledgeGraph.  # noqa: E501
        :rtype: KnowledgeGraph
        """
        return util.deserialize_model(dikt, cls)

    @property
    def nodes(self):
        """Gets the nodes of this KnowledgeGraph.

        Dictionary of Node instances used in the KnowledgeGraph, referenced elsewhere in the TRAPI output by the dictionary key.  # noqa: E501

        :return: The nodes of this KnowledgeGraph.
        :rtype: Dict[str, Node]
        """
        return self._nodes

    @nodes.setter
    def nodes(self, nodes):
        """Sets the nodes of this KnowledgeGraph.

        Dictionary of Node instances used in the KnowledgeGraph, referenced elsewhere in the TRAPI output by the dictionary key.  # noqa: E501

        :param nodes: The nodes of this KnowledgeGraph.
        :type nodes: Dict[str, Node]
        """
        if nodes is None:
            raise ValueError("Invalid value for `nodes`, must not be `None`")  # noqa: E501

        self._nodes = nodes

    @property
    def edges(self):
        """Gets the edges of this KnowledgeGraph.

        Dictionary of Edge instances used in the KnowledgeGraph, referenced elsewhere in the TRAPI output by the dictionary key.  # noqa: E501

        :return: The edges of this KnowledgeGraph.
        :rtype: Dict[str, Edge]
        """
        return self._edges

    @edges.setter
    def edges(self, edges):
        """Sets the edges of this KnowledgeGraph.

        Dictionary of Edge instances used in the KnowledgeGraph, referenced elsewhere in the TRAPI output by the dictionary key.  # noqa: E501

        :param edges: The edges of this KnowledgeGraph.
        :type edges: Dict[str, Edge]
        """
        if edges is None:
            raise ValueError("Invalid value for `edges`, must not be `None`")  # noqa: E501

        self._edges = edges


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/log_entry.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_level import LogLevel
from openapi_server import util

from openapi_server.models.log_level import LogLevel  # noqa: E501

class LogEntry(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, timestamp=None, level=None, code=None, message=None):  # noqa: E501
        """LogEntry - a model defined in OpenAPI

        :param timestamp: The timestamp of this LogEntry.  # noqa: E501
        :type timestamp: datetime
        :param level: The level of this LogEntry.  # noqa: E501
        :type level: LogLevel
        :param code: The code of this LogEntry.  # noqa: E501
        :type code: str
        :param message: The message of this LogEntry.  # noqa: E501
        :type message: str
        """
        self.openapi_types = {
            'timestamp': datetime,
            'level': LogLevel,
            'code': str,
            'message': str
        }

        self.attribute_map = {
            'timestamp': 'timestamp',
            'level': 'level',
            'code': 'code',
            'message': 'message'
        }

        self._timestamp = timestamp
        self._level = level
        self._code = code
        self._message = message

    @classmethod
    def from_dict(cls, dikt) -> 'LogEntry':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The LogEntry of this LogEntry.  # noqa: E501
        :rtype: LogEntry
        """
        return util.deserialize_model(dikt, cls)

    @property
    def timestamp(self):
        """Gets the timestamp of this LogEntry.

        Timestamp in ISO 8601 format  # noqa: E501

        :return: The timestamp of this LogEntry.
        :rtype: datetime
        """
        return self._timestamp

    @timestamp.setter
    def timestamp(self, timestamp):
        """Sets the timestamp of this LogEntry.

        Timestamp in ISO 8601 format  # noqa: E501

        :param timestamp: The timestamp of this LogEntry.
        :type timestamp: datetime
        """

        self._timestamp = timestamp

    @property
    def level(self):
        """Gets the level of this LogEntry.


        :return: The level of this LogEntry.
        :rtype: LogLevel
        """
        return self._level

    @level.setter
    def level(self, level):
        """Sets the level of this LogEntry.


        :param level: The level of this LogEntry.
        :type level: LogLevel
        """

        self._level = level

    @property
    def code(self):
        """Gets the code of this LogEntry.

        One of a standardized set of short codes e.g. QueryNotTraversable, KPNotAvailable, KPResponseMalformed  # noqa: E501

        :return: The code of this LogEntry.
        :rtype: str
        """
        return self._code

    @code.setter
    def code(self, code):
        """Sets the code of this LogEntry.

        One of a standardized set of short codes e.g. QueryNotTraversable, KPNotAvailable, KPResponseMalformed  # noqa: E501

        :param code: The code of this LogEntry.
        :type code: str
        """

        self._code = code

    @property
    def message(self):
        """Gets the message of this LogEntry.

        A human-readable log message  # noqa: E501

        :return: The message of this LogEntry.
        :rtype: str
        """
        return self._message

    @message.setter
    def message(self, message):
        """Sets the message of this LogEntry.

        A human-readable log message  # noqa: E501

        :param message: The message of this LogEntry.
        :type message: str
        """

        self._message = message


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/log_level.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class LogLevel(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    """
    allowed enum values
    """
    ERROR = "ERROR"
    WARNING = "WARNING"
    INFO = "INFO"
    DEBUG = "DEBUG"
    def __init__(self):  # noqa: E501
        """LogLevel - a model defined in OpenAPI

        """
        self.openapi_types = {
        }

        self.attribute_map = {
        }

    @classmethod
    def from_dict(cls, dikt) -> 'LogLevel':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The LogLevel of this LogLevel.  # noqa: E501
        :rtype: LogLevel
        """
        return util.deserialize_model(dikt, cls)


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/message.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.knowledge_graph import KnowledgeGraph
from openapi_server.models.query_graph import QueryGraph
from openapi_server.models.result import Result
from openapi_server import util

from openapi_server.models.knowledge_graph import KnowledgeGraph  # noqa: E501
from openapi_server.models.query_graph import QueryGraph  # noqa: E501
from openapi_server.models.result import Result  # noqa: E501

class Message(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, results=None, query_graph=None, knowledge_graph=None):  # noqa: E501
        """Message - a model defined in OpenAPI

        :param results: The results of this Message.  # noqa: E501
        :type results: List[Result]
        :param query_graph: The query_graph of this Message.  # noqa: E501
        :type query_graph: QueryGraph
        :param knowledge_graph: The knowledge_graph of this Message.  # noqa: E501
        :type knowledge_graph: KnowledgeGraph
        """
        self.openapi_types = {
            'results': List[Result],
            'query_graph': QueryGraph,
            'knowledge_graph': KnowledgeGraph
        }

        self.attribute_map = {
            'results': 'results',
            'query_graph': 'query_graph',
            'knowledge_graph': 'knowledge_graph'
        }

        self._results = results
        self._query_graph = query_graph
        self._knowledge_graph = knowledge_graph

    @classmethod
    def from_dict(cls, dikt) -> 'Message':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Message of this Message.  # noqa: E501
        :rtype: Message
        """
        return util.deserialize_model(dikt, cls)

    @property
    def results(self):
        """Gets the results of this Message.

        List of all returned Result objects for the query posed. The list SHOULD NOT be assumed to be ordered. The 'score' property,  if present, MAY be used to infer result rankings.  # noqa: E501

        :return: The results of this Message.
        :rtype: List[Result]
        """
        return self._results

    @results.setter
    def results(self, results):
        """Sets the results of this Message.

        List of all returned Result objects for the query posed. The list SHOULD NOT be assumed to be ordered. The 'score' property,  if present, MAY be used to infer result rankings.  # noqa: E501

        :param results: The results of this Message.
        :type results: List[Result]
        """

        self._results = results

    @property
    def query_graph(self):
        """Gets the query_graph of this Message.

        QueryGraph object that contains a serialization of a query in the form of a graph  # noqa: E501

        :return: The query_graph of this Message.
        :rtype: QueryGraph
        """
        return self._query_graph

    @query_graph.setter
    def query_graph(self, query_graph):
        """Sets the query_graph of this Message.

        QueryGraph object that contains a serialization of a query in the form of a graph  # noqa: E501

        :param query_graph: The query_graph of this Message.
        :type query_graph: QueryGraph
        """

        self._query_graph = query_graph

    @property
    def knowledge_graph(self):
        """Gets the knowledge_graph of this Message.

        KnowledgeGraph object that contains lists of nodes and edges in the thought graph corresponding to the message  # noqa: E501

        :return: The knowledge_graph of this Message.
        :rtype: KnowledgeGraph
        """
        return self._knowledge_graph

    @knowledge_graph.setter
    def knowledge_graph(self, knowledge_graph):
        """Sets the knowledge_graph of this Message.

        KnowledgeGraph object that contains lists of nodes and edges in the thought graph corresponding to the message  # noqa: E501

        :param knowledge_graph: The knowledge_graph of this Message.
        :type knowledge_graph: KnowledgeGraph
        """

        self._knowledge_graph = knowledge_graph


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/meta_attribute.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class MetaAttribute(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, attribute_type_id=None, attribute_source=None, original_attribute_names=None, constraint_use=False, constraint_name=None):  # noqa: E501
        """MetaAttribute - a model defined in OpenAPI

        :param attribute_type_id: The attribute_type_id of this MetaAttribute.  # noqa: E501
        :type attribute_type_id: str
        :param attribute_source: The attribute_source of this MetaAttribute.  # noqa: E501
        :type attribute_source: str
        :param original_attribute_names: The original_attribute_names of this MetaAttribute.  # noqa: E501
        :type original_attribute_names: List[str]
        :param constraint_use: The constraint_use of this MetaAttribute.  # noqa: E501
        :type constraint_use: bool
        :param constraint_name: The constraint_name of this MetaAttribute.  # noqa: E501
        :type constraint_name: str
        """
        self.openapi_types = {
            'attribute_type_id': str,
            'attribute_source': str,
            'original_attribute_names': List[str],
            'constraint_use': bool,
            'constraint_name': str
        }

        self.attribute_map = {
            'attribute_type_id': 'attribute_type_id',
            'attribute_source': 'attribute_source',
            'original_attribute_names': 'original_attribute_names',
            'constraint_use': 'constraint_use',
            'constraint_name': 'constraint_name'
        }

        self._attribute_type_id = attribute_type_id
        self._attribute_source = attribute_source
        self._original_attribute_names = original_attribute_names
        self._constraint_use = constraint_use
        self._constraint_name = constraint_name

    @classmethod
    def from_dict(cls, dikt) -> 'MetaAttribute':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MetaAttribute of this MetaAttribute.  # noqa: E501
        :rtype: MetaAttribute
        """
        return util.deserialize_model(dikt, cls)

    @property
    def attribute_type_id(self):
        """Gets the attribute_type_id of this MetaAttribute.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The attribute_type_id of this MetaAttribute.
        :rtype: str
        """
        return self._attribute_type_id

    @attribute_type_id.setter
    def attribute_type_id(self, attribute_type_id):
        """Sets the attribute_type_id of this MetaAttribute.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param attribute_type_id: The attribute_type_id of this MetaAttribute.
        :type attribute_type_id: str
        """
        if attribute_type_id is None:
            raise ValueError("Invalid value for `attribute_type_id`, must not be `None`")  # noqa: E501

        self._attribute_type_id = attribute_type_id

    @property
    def attribute_source(self):
        """Gets the attribute_source of this MetaAttribute.

        Source of an attribute provided by this TRAPI web service.  # noqa: E501

        :return: The attribute_source of this MetaAttribute.
        :rtype: str
        """
        return self._attribute_source

    @attribute_source.setter
    def attribute_source(self, attribute_source):
        """Sets the attribute_source of this MetaAttribute.

        Source of an attribute provided by this TRAPI web service.  # noqa: E501

        :param attribute_source: The attribute_source of this MetaAttribute.
        :type attribute_source: str
        """

        self._attribute_source = attribute_source

    @property
    def original_attribute_names(self):
        """Gets the original_attribute_names of this MetaAttribute.

        Names of an the attribute as provided by the source.  # noqa: E501

        :return: The original_attribute_names of this MetaAttribute.
        :rtype: List[str]
        """
        return self._original_attribute_names

    @original_attribute_names.setter
    def original_attribute_names(self, original_attribute_names):
        """Sets the original_attribute_names of this MetaAttribute.

        Names of an the attribute as provided by the source.  # noqa: E501

        :param original_attribute_names: The original_attribute_names of this MetaAttribute.
        :type original_attribute_names: List[str]
        """
        if original_attribute_names is not None and len(original_attribute_names) < 1:
            raise ValueError("Invalid value for `original_attribute_names`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._original_attribute_names = original_attribute_names

    @property
    def constraint_use(self):
        """Gets the constraint_use of this MetaAttribute.

        Indicates whether this attribute can be used as a query constraint.  # noqa: E501

        :return: The constraint_use of this MetaAttribute.
        :rtype: bool
        """
        return self._constraint_use

    @constraint_use.setter
    def constraint_use(self, constraint_use):
        """Sets the constraint_use of this MetaAttribute.

        Indicates whether this attribute can be used as a query constraint.  # noqa: E501

        :param constraint_use: The constraint_use of this MetaAttribute.
        :type constraint_use: bool
        """

        self._constraint_use = constraint_use

    @property
    def constraint_name(self):
        """Gets the constraint_name of this MetaAttribute.

        Human-readable name or label for the constraint concept. Required whenever constraint_use is true.  # noqa: E501

        :return: The constraint_name of this MetaAttribute.
        :rtype: str
        """
        return self._constraint_name

    @constraint_name.setter
    def constraint_name(self, constraint_name):
        """Sets the constraint_name of this MetaAttribute.

        Human-readable name or label for the constraint concept. Required whenever constraint_use is true.  # noqa: E501

        :param constraint_name: The constraint_name of this MetaAttribute.
        :type constraint_name: str
        """

        self._constraint_name = constraint_name


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/meta_edge.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.meta_attribute import MetaAttribute
import re
from openapi_server import util

from openapi_server.models.meta_attribute import MetaAttribute  # noqa: E501
import re  # noqa: E501

class MetaEdge(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, subject=None, predicate=None, object=None, knowledge_types=None, attributes=None):  # noqa: E501
        """MetaEdge - a model defined in OpenAPI

        :param subject: The subject of this MetaEdge.  # noqa: E501
        :type subject: str
        :param predicate: The predicate of this MetaEdge.  # noqa: E501
        :type predicate: str
        :param object: The object of this MetaEdge.  # noqa: E501
        :type object: str
        :param knowledge_types: The knowledge_types of this MetaEdge.  # noqa: E501
        :type knowledge_types: List[str]
        :param attributes: The attributes of this MetaEdge.  # noqa: E501
        :type attributes: List[MetaAttribute]
        """
        self.openapi_types = {
            'subject': str,
            'predicate': str,
            'object': str,
            'knowledge_types': List[str],
            'attributes': List[MetaAttribute]
        }

        self.attribute_map = {
            'subject': 'subject',
            'predicate': 'predicate',
            'object': 'object',
            'knowledge_types': 'knowledge_types',
            'attributes': 'attributes'
        }

        self._subject = subject
        self._predicate = predicate
        self._object = object
        self._knowledge_types = knowledge_types
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'MetaEdge':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MetaEdge of this MetaEdge.  # noqa: E501
        :rtype: MetaEdge
        """
        return util.deserialize_model(dikt, cls)

    @property
    def subject(self):
        """Gets the subject of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :return: The subject of this MetaEdge.
        :rtype: str
        """
        return self._subject

    @subject.setter
    def subject(self, subject):
        """Sets the subject of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :param subject: The subject of this MetaEdge.
        :type subject: str
        """
        if subject is None:
            raise ValueError("Invalid value for `subject`, must not be `None`")  # noqa: E501
        if subject is not None and not re.search(r'^biolink:[A-Z][a-zA-Z]*$', subject):  # noqa: E501
            raise ValueError("Invalid value for `subject`, must be a follow pattern or equal to `/^biolink:[A-Z][a-zA-Z]*$/`")  # noqa: E501

        self._subject = subject

    @property
    def predicate(self):
        """Gets the predicate of this MetaEdge.

        CURIE for a Biolink 'predicate' slot, taken from the Biolink slot ('is_a') hierarchy rooted in biolink:related_to (snake_case). This predicate defines the Biolink relationship between the subject and object nodes of a biolink:Association defining a knowledge graph edge.  # noqa: E501

        :return: The predicate of this MetaEdge.
        :rtype: str
        """
        return self._predicate

    @predicate.setter
    def predicate(self, predicate):
        """Sets the predicate of this MetaEdge.

        CURIE for a Biolink 'predicate' slot, taken from the Biolink slot ('is_a') hierarchy rooted in biolink:related_to (snake_case). This predicate defines the Biolink relationship between the subject and object nodes of a biolink:Association defining a knowledge graph edge.  # noqa: E501

        :param predicate: The predicate of this MetaEdge.
        :type predicate: str
        """
        if predicate is None:
            raise ValueError("Invalid value for `predicate`, must not be `None`")  # noqa: E501
        if predicate is not None and not re.search(r'^biolink:[a-z][a-z_]*$', predicate):  # noqa: E501
            raise ValueError("Invalid value for `predicate`, must be a follow pattern or equal to `/^biolink:[a-z][a-z_]*$/`")  # noqa: E501

        self._predicate = predicate

    @property
    def object(self):
        """Gets the object of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :return: The object of this MetaEdge.
        :rtype: str
        """
        return self._object

    @object.setter
    def object(self, object):
        """Sets the object of this MetaEdge.

        Compact URI (CURIE) for a Biolink class, biolink:NamedThing or a child thereof. The CURIE must use the prefix 'biolink:' followed by the PascalCase class name.  # noqa: E501

        :param object: The object of this MetaEdge.
        :type object: str
        """
        if object is None:
            raise ValueError("Invalid value for `object`, must not be `None`")  # noqa: E501
        if object is not None and not re.search(r'^biolink:[A-Z][a-zA-Z]*$', object):  # noqa: E501
            raise ValueError("Invalid value for `object`, must be a follow pattern or equal to `/^biolink:[A-Z][a-zA-Z]*$/`")  # noqa: E501

        self._object = object

    @property
    def knowledge_types(self):
        """Gets the knowledge_types of this MetaEdge.

        A list of knowledge_types that are supported by the service. If the knowledge_types is null, this means that only 'lookup' is supported. Currently allowed values are 'lookup' or 'inferred'.  # noqa: E501

        :return: The knowledge_types of this MetaEdge.
        :rtype: List[str]
        """
        return self._knowledge_types

    @knowledge_types.setter
    def knowledge_types(self, knowledge_types):
        """Sets the knowledge_types of this MetaEdge.

        A list of knowledge_types that are supported by the service. If the knowledge_types is null, this means that only 'lookup' is supported. Currently allowed values are 'lookup' or 'inferred'.  # noqa: E501

        :param knowledge_types: The knowledge_types of this MetaEdge.
        :type knowledge_types: List[str]
        """
        if knowledge_types is not None and len(knowledge_types) < 1:
            raise ValueError("Invalid value for `knowledge_types`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._knowledge_types = knowledge_types

    @property
    def attributes(self):
        """Gets the attributes of this MetaEdge.

        Edge attributes provided by this TRAPI web service.  # noqa: E501

        :return: The attributes of this MetaEdge.
        :rtype: List[MetaAttribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this MetaEdge.

        Edge attributes provided by this TRAPI web service.  # noqa: E501

        :param attributes: The attributes of this MetaEdge.
        :type attributes: List[MetaAttribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/meta_knowledge_graph.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.meta_edge import MetaEdge
from openapi_server.models.meta_node import MetaNode
from openapi_server import util

from openapi_server.models.meta_edge import MetaEdge  # noqa: E501
from openapi_server.models.meta_node import MetaNode  # noqa: E501

class MetaKnowledgeGraph(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, nodes=None, edges=None):  # noqa: E501
        """MetaKnowledgeGraph - a model defined in OpenAPI

        :param nodes: The nodes of this MetaKnowledgeGraph.  # noqa: E501
        :type nodes: Dict[str, MetaNode]
        :param edges: The edges of this MetaKnowledgeGraph.  # noqa: E501
        :type edges: List[MetaEdge]
        """
        self.openapi_types = {
            'nodes': Dict[str, MetaNode],
            'edges': List[MetaEdge]
        }

        self.attribute_map = {
            'nodes': 'nodes',
            'edges': 'edges'
        }

        self._nodes = nodes
        self._edges = edges

    @classmethod
    def from_dict(cls, dikt) -> 'MetaKnowledgeGraph':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MetaKnowledgeGraph of this MetaKnowledgeGraph.  # noqa: E501
        :rtype: MetaKnowledgeGraph
        """
        return util.deserialize_model(dikt, cls)

    @property
    def nodes(self):
        """Gets the nodes of this MetaKnowledgeGraph.

        Collection of the most specific node categories provided by this TRAPI web service, indexed by Biolink class CURIEs. A node category is only exposed here if there is node for which that is the most specific category available.  # noqa: E501

        :return: The nodes of this MetaKnowledgeGraph.
        :rtype: Dict[str, MetaNode]
        """
        return self._nodes

    @nodes.setter
    def nodes(self, nodes):
        """Sets the nodes of this MetaKnowledgeGraph.

        Collection of the most specific node categories provided by this TRAPI web service, indexed by Biolink class CURIEs. A node category is only exposed here if there is node for which that is the most specific category available.  # noqa: E501

        :param nodes: The nodes of this MetaKnowledgeGraph.
        :type nodes: Dict[str, MetaNode]
        """
        if nodes is None:
            raise ValueError("Invalid value for `nodes`, must not be `None`")  # noqa: E501

        self._nodes = nodes

    @property
    def edges(self):
        """Gets the edges of this MetaKnowledgeGraph.

        List of the most specific edges/predicates provided by this TRAPI web service. A predicate is only exposed here if there is an edge for which the predicate is the most specific available.  # noqa: E501

        :return: The edges of this MetaKnowledgeGraph.
        :rtype: List[MetaEdge]
        """
        return self._edges

    @edges.setter
    def edges(self, edges):
        """Sets the edges of this MetaKnowledgeGraph.

        List of the most specific edges/predicates provided by this TRAPI web service. A predicate is only exposed here if there is an edge for which the predicate is the most specific available.  # noqa: E501

        :param edges: The edges of this MetaKnowledgeGraph.
        :type edges: List[MetaEdge]
        """
        if edges is None:
            raise ValueError("Invalid value for `edges`, must not be `None`")  # noqa: E501

        self._edges = edges


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/meta_node.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.meta_attribute import MetaAttribute
from openapi_server import util

from openapi_server.models.meta_attribute import MetaAttribute  # noqa: E501

class MetaNode(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id_prefixes=None, attributes=None):  # noqa: E501
        """MetaNode - a model defined in OpenAPI

        :param id_prefixes: The id_prefixes of this MetaNode.  # noqa: E501
        :type id_prefixes: List[str]
        :param attributes: The attributes of this MetaNode.  # noqa: E501
        :type attributes: List[MetaAttribute]
        """
        self.openapi_types = {
            'id_prefixes': List[str],
            'attributes': List[MetaAttribute]
        }

        self.attribute_map = {
            'id_prefixes': 'id_prefixes',
            'attributes': 'attributes'
        }

        self._id_prefixes = id_prefixes
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'MetaNode':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The MetaNode of this MetaNode.  # noqa: E501
        :rtype: MetaNode
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id_prefixes(self):
        """Gets the id_prefixes of this MetaNode.

        List of CURIE prefixes for the node category that this TRAPI web service understands and accepts on the input.  # noqa: E501

        :return: The id_prefixes of this MetaNode.
        :rtype: List[str]
        """
        return self._id_prefixes

    @id_prefixes.setter
    def id_prefixes(self, id_prefixes):
        """Sets the id_prefixes of this MetaNode.

        List of CURIE prefixes for the node category that this TRAPI web service understands and accepts on the input.  # noqa: E501

        :param id_prefixes: The id_prefixes of this MetaNode.
        :type id_prefixes: List[str]
        """
        if id_prefixes is None:
            raise ValueError("Invalid value for `id_prefixes`, must not be `None`")  # noqa: E501
        if id_prefixes is not None and len(id_prefixes) < 1:
            raise ValueError("Invalid value for `id_prefixes`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._id_prefixes = id_prefixes

    @property
    def attributes(self):
        """Gets the attributes of this MetaNode.

        Node attributes provided by this TRAPI web service.  # noqa: E501

        :return: The attributes of this MetaNode.
        :rtype: List[MetaAttribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this MetaNode.

        Node attributes provided by this TRAPI web service.  # noqa: E501

        :param attributes: The attributes of this MetaNode.
        :type attributes: List[MetaAttribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/node.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501

class Node(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, name=None, categories=None, attributes=None):  # noqa: E501
        """Node - a model defined in OpenAPI

        :param name: The name of this Node.  # noqa: E501
        :type name: str
        :param categories: The categories of this Node.  # noqa: E501
        :type categories: List[str]
        :param attributes: The attributes of this Node.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'name': str,
            'categories': List[str],
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'name': 'name',
            'categories': 'categories',
            'attributes': 'attributes'
        }

        self._name = name
        self._categories = categories
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'Node':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Node of this Node.  # noqa: E501
        :rtype: Node
        """
        return util.deserialize_model(dikt, cls)

    @property
    def name(self):
        """Gets the name of this Node.

        Formal name of the entity  # noqa: E501

        :return: The name of this Node.
        :rtype: str
        """
        return self._name

    @name.setter
    def name(self, name):
        """Sets the name of this Node.

        Formal name of the entity  # noqa: E501

        :param name: The name of this Node.
        :type name: str
        """

        self._name = name

    @property
    def categories(self):
        """Gets the categories of this Node.


        :return: The categories of this Node.
        :rtype: List[str]
        """
        return self._categories

    @categories.setter
    def categories(self, categories):
        """Sets the categories of this Node.


        :param categories: The categories of this Node.
        :type categories: List[str]
        """

        self._categories = categories

    @property
    def attributes(self):
        """Gets the attributes of this Node.

        A list of attributes describing the node  # noqa: E501

        :return: The attributes of this Node.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this Node.

        A list of attributes describing the node  # noqa: E501

        :param attributes: The attributes of this Node.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/node_binding.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501

class NodeBinding(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, query_id=None, attributes=None):  # noqa: E501
        """NodeBinding - a model defined in OpenAPI

        :param id: The id of this NodeBinding.  # noqa: E501
        :type id: str
        :param query_id: The query_id of this NodeBinding.  # noqa: E501
        :type query_id: str
        :param attributes: The attributes of this NodeBinding.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'id': str,
            'query_id': str,
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'id': 'id',
            'query_id': 'query_id',
            'attributes': 'attributes'
        }

        self._id = id
        self._query_id = query_id
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'NodeBinding':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The NodeBinding of this NodeBinding.  # noqa: E501
        :rtype: NodeBinding
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this NodeBinding.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The id of this NodeBinding.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this NodeBinding.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param id: The id of this NodeBinding.
        :type id: str
        """
        if id is None:
            raise ValueError("Invalid value for `id`, must not be `None`")  # noqa: E501

        self._id = id

    @property
    def query_id(self):
        """Gets the query_id of this NodeBinding.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The query_id of this NodeBinding.
        :rtype: str
        """
        return self._query_id

    @query_id.setter
    def query_id(self, query_id):
        """Sets the query_id of this NodeBinding.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param query_id: The query_id of this NodeBinding.
        :type query_id: str
        """

        self._query_id = query_id

    @property
    def attributes(self):
        """Gets the attributes of this NodeBinding.

        A list of attributes providing further information about the node binding. This is not intended for capturing node attributes and should only be used for properties that vary from result to result.  # noqa: E501

        :return: The attributes of this NodeBinding.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this NodeBinding.

        A list of attributes providing further information about the node binding. This is not intended for capturing node attributes and should only be used for properties that vary from result to result.  # noqa: E501

        :param attributes: The attributes of this NodeBinding.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/operation_lookup.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationLookup(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationLookup - a model defined in OpenAPI

        :param id: The id of this OperationLookup.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationLookup.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationLookup':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationLookup of this OperationLookup.  # noqa: E501
        :rtype: OperationLookup
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationLookup.


        :return: The id of this OperationLookup.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationLookup.


        :param id: The id of this OperationLookup.
        :type id: str
        """
        allowed_values = ["lookup"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationLookup.


        :return: The parameters of this OperationLookup.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationLookup.


        :param parameters: The parameters of this OperationLookup.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/operation_lookup_and_score.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.one_ofobjectobject import OneOfobjectobject
from openapi_server import util

from openapi_server.models.one_ofobjectobject import OneOfobjectobject  # noqa: E501

class OperationLookupAndScore(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None, runner_parameters=None, unique=True):  # noqa: E501
        """OperationLookupAndScore - a model defined in OpenAPI

        :param id: The id of this OperationLookupAndScore.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationLookupAndScore.  # noqa: E501
        :type parameters: object
        :param runner_parameters: The runner_parameters of this OperationLookupAndScore.  # noqa: E501
        :type runner_parameters: OneOfobjectobject
        :param unique: The unique of this OperationLookupAndScore.  # noqa: E501
        :type unique: bool
        """
        self.openapi_types = {
            'id': str,
            'parameters': object,
            'runner_parameters': OneOfobjectobject,
            'unique': bool
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters',
            'runner_parameters': 'runner_parameters',
            'unique': 'unique'
        }

        self._id = id
        self._parameters = parameters
        self._runner_parameters = runner_parameters
        self._unique = unique

    @classmethod
    def from_dict(cls, dikt) -> 'OperationLookupAndScore':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationLookupAndScore of this OperationLookupAndScore.  # noqa: E501
        :rtype: OperationLookupAndScore
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationLookupAndScore.


        :return: The id of this OperationLookupAndScore.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationLookupAndScore.


        :param id: The id of this OperationLookupAndScore.
        :type id: str
        """
        allowed_values = ["lookup_and_score"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationLookupAndScore.


        :return: The parameters of this OperationLookupAndScore.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationLookupAndScore.


        :param parameters: The parameters of this OperationLookupAndScore.
        :type parameters: object
        """

        self._parameters = parameters

    @property
    def runner_parameters(self):
        """Gets the runner_parameters of this OperationLookupAndScore.


        :return: The runner_parameters of this OperationLookupAndScore.
        :rtype: OneOfobjectobject
        """
        return self._runner_parameters

    @runner_parameters.setter
    def runner_parameters(self, runner_parameters):
        """Sets the runner_parameters of this OperationLookupAndScore.


        :param runner_parameters: The runner_parameters of this OperationLookupAndScore.
        :type runner_parameters: OneOfobjectobject
        """

        self._runner_parameters = runner_parameters

    @property
    def unique(self):
        """Gets the unique of this OperationLookupAndScore.


        :return: The unique of this OperationLookupAndScore.
        :rtype: bool
        """
        return self._unique

    @unique.setter
    def unique(self, unique):
        """Sets the unique of this OperationLookupAndScore.


        :param unique: The unique of this OperationLookupAndScore.
        :type unique: bool
        """

        self._unique = unique


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/operation_overlay.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class OperationOverlay(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, id=None, parameters=None):  # noqa: E501
        """OperationOverlay - a model defined in OpenAPI

        :param id: The id of this OperationOverlay.  # noqa: E501
        :type id: str
        :param parameters: The parameters of this OperationOverlay.  # noqa: E501
        :type parameters: object
        """
        self.openapi_types = {
            'id': str,
            'parameters': object
        }

        self.attribute_map = {
            'id': 'id',
            'parameters': 'parameters'
        }

        self._id = id
        self._parameters = parameters

    @classmethod
    def from_dict(cls, dikt) -> 'OperationOverlay':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The OperationOverlay of this OperationOverlay.  # noqa: E501
        :rtype: OperationOverlay
        """
        return util.deserialize_model(dikt, cls)

    @property
    def id(self):
        """Gets the id of this OperationOverlay.


        :return: The id of this OperationOverlay.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this OperationOverlay.


        :param id: The id of this OperationOverlay.
        :type id: str
        """
        allowed_values = ["overlay"]  # noqa: E501
        if id not in allowed_values:
            raise ValueError(
                "Invalid value for `id` ({0}), must be one of {1}"
                .format(id, allowed_values)
            )

        self._id = id

    @property
    def parameters(self):
        """Gets the parameters of this OperationOverlay.


        :return: The parameters of this OperationOverlay.
        :rtype: object
        """
        return self._parameters

    @parameters.setter
    def parameters(self, parameters):
        """Sets the parameters of this OperationOverlay.


        :param parameters: The parameters of this OperationOverlay.
        :type parameters: object
        """

        self._parameters = parameters


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/operations.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.message import Message
from openapi_server import util

from openapi_server.models.message import Message  # noqa: E501

class Operations(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, message_uris=None, messages=None, actions=None, options=None):  # noqa: E501
        """Operations - a model defined in OpenAPI

        :param message_uris: The message_uris of this Operations.  # noqa: E501
        :type message_uris: List[str]
        :param messages: The messages of this Operations.  # noqa: E501
        :type messages: List[Message]
        :param actions: The actions of this Operations.  # noqa: E501
        :type actions: List[str]
        :param options: The options of this Operations.  # noqa: E501
        :type options: Dict[str, object]
        """
        self.openapi_types = {
            'message_uris': List[str],
            'messages': List[Message],
            'actions': List[str],
            'options': Dict[str, object]
        }

        self.attribute_map = {
            'message_uris': 'message_uris',
            'messages': 'messages',
            'actions': 'actions',
            'options': 'options'
        }

        self._message_uris = message_uris
        self._messages = messages
        self._actions = actions
        self._options = options

    @classmethod
    def from_dict(cls, dikt) -> 'Operations':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Operations of this Operations.  # noqa: E501
        :rtype: Operations
        """
        return util.deserialize_model(dikt, cls)

    @property
    def message_uris(self):
        """Gets the message_uris of this Operations.

        List of URIs for Message or Response objects to fetch and process  # noqa: E501

        :return: The message_uris of this Operations.
        :rtype: List[str]
        """
        return self._message_uris

    @message_uris.setter
    def message_uris(self, message_uris):
        """Sets the message_uris of this Operations.

        List of URIs for Message or Response objects to fetch and process  # noqa: E501

        :param message_uris: The message_uris of this Operations.
        :type message_uris: List[str]
        """

        self._message_uris = message_uris

    @property
    def messages(self):
        """Gets the messages of this Operations.

        List of Message objects to process  # noqa: E501

        :return: The messages of this Operations.
        :rtype: List[Message]
        """
        return self._messages

    @messages.setter
    def messages(self, messages):
        """Sets the messages of this Operations.

        List of Message objects to process  # noqa: E501

        :param messages: The messages of this Operations.
        :type messages: List[Message]
        """

        self._messages = messages

    @property
    def actions(self):
        """Gets the actions of this Operations.

        List of order-dependent operations to execute  # noqa: E501

        :return: The actions of this Operations.
        :rtype: List[str]
        """
        return self._actions

    @actions.setter
    def actions(self, actions):
        """Sets the actions of this Operations.

        List of order-dependent operations to execute  # noqa: E501

        :param actions: The actions of this Operations.
        :type actions: List[str]
        """

        self._actions = actions

    @property
    def options(self):
        """Gets the options of this Operations.

        Map of order independent options that apply during processing  # noqa: E501

        :return: The options of this Operations.
        :rtype: Dict[str, object]
        """
        return self._options

    @options.setter
    def options(self, options):
        """Sets the options of this Operations.

        Map of order independent options that apply during processing  # noqa: E501

        :param options: The options of this Operations.
        :type options: Dict[str, object]
        """

        self._options = options


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/q_edge.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute_constraint import AttributeConstraint
from openapi_server.models.qualifier_constraint import QualifierConstraint
from openapi_server import util

from openapi_server.models.attribute_constraint import AttributeConstraint  # noqa: E501
from openapi_server.models.qualifier_constraint import QualifierConstraint  # noqa: E501

class QEdge(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, knowledge_type=None, predicates=None, subject=None, object=None, attribute_constraints=[], qualifier_constraints=[], exclude=None, option_group_id=None):  # noqa: E501
        """QEdge - a model defined in OpenAPI

        :param knowledge_type: The knowledge_type of this QEdge.  # noqa: E501
        :type knowledge_type: str
        :param predicates: The predicates of this QEdge.  # noqa: E501
        :type predicates: List[str]
        :param subject: The subject of this QEdge.  # noqa: E501
        :type subject: str
        :param object: The object of this QEdge.  # noqa: E501
        :type object: str
        :param attribute_constraints: The attribute_constraints of this QEdge.  # noqa: E501
        :type attribute_constraints: List[AttributeConstraint]
        :param qualifier_constraints: The qualifier_constraints of this QEdge.  # noqa: E501
        :type qualifier_constraints: List[QualifierConstraint]
        :param exclude: The exclude of this QEdge.  # noqa: E501
        :type exclude: bool
        :param option_group_id: The option_group_id of this QEdge.  # noqa: E501
        :type option_group_id: str
        """
        self.openapi_types = {
            'knowledge_type': str,
            'predicates': List[str],
            'subject': str,
            'object': str,
            'attribute_constraints': List[AttributeConstraint],
            'qualifier_constraints': List[QualifierConstraint],
            'exclude': bool,
            'option_group_id': str
        }

        self.attribute_map = {
            'knowledge_type': 'knowledge_type',
            'predicates': 'predicates',
            'subject': 'subject',
            'object': 'object',
            'attribute_constraints': 'attribute_constraints',
            'qualifier_constraints': 'qualifier_constraints',
            'exclude': 'exclude',
            'option_group_id': 'option_group_id'
        }

        self._knowledge_type = knowledge_type
        self._predicates = predicates
        self._subject = subject
        self._object = object
        self._attribute_constraints = attribute_constraints
        self._qualifier_constraints = qualifier_constraints
        self._exclude = exclude
        self._option_group_id = option_group_id

    @classmethod
    def from_dict(cls, dikt) -> 'QEdge':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The QEdge of this QEdge.  # noqa: E501
        :rtype: QEdge
        """
        return util.deserialize_model(dikt, cls)

    @property
    def knowledge_type(self):
        """Gets the knowledge_type of this QEdge.

        Indicates the type of knowledge that the client wants from the server between the subject and object. If the value is 'lookup', then the client wants direct lookup information from knowledge sources. If the value is 'inferred', then the client wants the server to get creative and connect the subject and object in more speculative and non-direct-lookup ways. If this property is absent or null, it MUST be assumed to mean 'lookup'. This feature is currently experimental and may be further extended in the future.  # noqa: E501

        :return: The knowledge_type of this QEdge.
        :rtype: str
        """
        return self._knowledge_type

    @knowledge_type.setter
    def knowledge_type(self, knowledge_type):
        """Sets the knowledge_type of this QEdge.

        Indicates the type of knowledge that the client wants from the server between the subject and object. If the value is 'lookup', then the client wants direct lookup information from knowledge sources. If the value is 'inferred', then the client wants the server to get creative and connect the subject and object in more speculative and non-direct-lookup ways. If this property is absent or null, it MUST be assumed to mean 'lookup'. This feature is currently experimental and may be further extended in the future.  # noqa: E501

        :param knowledge_type: The knowledge_type of this QEdge.
        :type knowledge_type: str
        """

        self._knowledge_type = knowledge_type

    @property
    def predicates(self):
        """Gets the predicates of this QEdge.


        :return: The predicates of this QEdge.
        :rtype: List[str]
        """
        return self._predicates

    @predicates.setter
    def predicates(self, predicates):
        """Sets the predicates of this QEdge.


        :param predicates: The predicates of this QEdge.
        :type predicates: List[str]
        """
        if predicates is not None and len(predicates) < 1:
            raise ValueError("Invalid value for `predicates`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._predicates = predicates

    @property
    def subject(self):
        """Gets the subject of this QEdge.

        Corresponds to the map key identifier of the subject concept node anchoring the query filter pattern for the query relationship edge.  # noqa: E501

        :return: The subject of this QEdge.
        :rtype: str
        """
        return self._subject

    @subject.setter
    def subject(self, subject):
        """Sets the subject of this QEdge.

        Corresponds to the map key identifier of the subject concept node anchoring the query filter pattern for the query relationship edge.  # noqa: E501

        :param subject: The subject of this QEdge.
        :type subject: str
        """
        if subject is None:
            raise ValueError("Invalid value for `subject`, must not be `None`")  # noqa: E501

        self._subject = subject

    @property
    def object(self):
        """Gets the object of this QEdge.

        Corresponds to the map key identifier of the object concept node anchoring the query filter pattern for the query relationship edge.  # noqa: E501

        :return: The object of this QEdge.
        :rtype: str
        """
        return self._object

    @object.setter
    def object(self, object):
        """Sets the object of this QEdge.

        Corresponds to the map key identifier of the object concept node anchoring the query filter pattern for the query relationship edge.  # noqa: E501

        :param object: The object of this QEdge.
        :type object: str
        """
        if object is None:
            raise ValueError("Invalid value for `object`, must not be `None`")  # noqa: E501

        self._object = object

    @property
    def attribute_constraints(self):
        """Gets the attribute_constraints of this QEdge.

        A list of attribute contraints applied to a query edge. If there are multiple items, they must all be true (equivalent to AND)  # noqa: E501

        :return: The attribute_constraints of this QEdge.
        :rtype: List[AttributeConstraint]
        """
        return self._attribute_constraints

    @attribute_constraints.setter
    def attribute_constraints(self, attribute_constraints):
        """Sets the attribute_constraints of this QEdge.

        A list of attribute contraints applied to a query edge. If there are multiple items, they must all be true (equivalent to AND)  # noqa: E501

        :param attribute_constraints: The attribute_constraints of this QEdge.
        :type attribute_constraints: List[AttributeConstraint]
        """

        self._attribute_constraints = attribute_constraints

    @property
    def qualifier_constraints(self):
        """Gets the qualifier_constraints of this QEdge.

        A list of QualifierConstraints that provide nuance to the QEdge. If multiple QualifierConstraints are provided, there is an OR relationship between them. If the QEdge has multiple predicates or if the QNodes that correspond to the subject or object of this QEdge have multiple categories or multiple curies, then qualifier_constraints MUST NOT be specified because these complex use cases are not supported at this time.  # noqa: E501

        :return: The qualifier_constraints of this QEdge.
        :rtype: List[QualifierConstraint]
        """
        return self._qualifier_constraints

    @qualifier_constraints.setter
    def qualifier_constraints(self, qualifier_constraints):
        """Sets the qualifier_constraints of this QEdge.

        A list of QualifierConstraints that provide nuance to the QEdge. If multiple QualifierConstraints are provided, there is an OR relationship between them. If the QEdge has multiple predicates or if the QNodes that correspond to the subject or object of this QEdge have multiple categories or multiple curies, then qualifier_constraints MUST NOT be specified because these complex use cases are not supported at this time.  # noqa: E501

        :param qualifier_constraints: The qualifier_constraints of this QEdge.
        :type qualifier_constraints: List[QualifierConstraint]
        """

        self._qualifier_constraints = qualifier_constraints

    @property
    def exclude(self):
        """Gets the exclude of this QEdge.

        If set to true, then all subgraphs containing this edge are excluded from the final results. (optional)  # noqa: E501

        :return: The exclude of this QEdge.
        :rtype: bool
        """
        return self._exclude

    @exclude.setter
    def exclude(self, exclude):
        """Sets the exclude of this QEdge.

        If set to true, then all subgraphs containing this edge are excluded from the final results. (optional)  # noqa: E501

        :param exclude: The exclude of this QEdge.
        :type exclude: bool
        """

        self._exclude = exclude

    @property
    def option_group_id(self):
        """Gets the option_group_id of this QEdge.

        Optional string acting as a label on a set of nodes and/or edges indicating that they belong to a group that are to be evaluated as a group.   # noqa: E501

        :return: The option_group_id of this QEdge.
        :rtype: str
        """
        return self._option_group_id

    @option_group_id.setter
    def option_group_id(self, option_group_id):
        """Sets the option_group_id of this QEdge.

        Optional string acting as a label on a set of nodes and/or edges indicating that they belong to a group that are to be evaluated as a group.   # noqa: E501

        :param option_group_id: The option_group_id of this QEdge.
        :type option_group_id: str
        """

        self._option_group_id = option_group_id


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/q_node.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute_constraint import AttributeConstraint
from openapi_server import util

from openapi_server.models.attribute_constraint import AttributeConstraint  # noqa: E501

class QNode(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, ids=None, categories=None, is_set=False, constraints=[], option_group_id=None):  # noqa: E501
        """QNode - a model defined in OpenAPI

        :param ids: The ids of this QNode.  # noqa: E501
        :type ids: List[str]
        :param categories: The categories of this QNode.  # noqa: E501
        :type categories: List[str]
        :param is_set: The is_set of this QNode.  # noqa: E501
        :type is_set: bool
        :param constraints: The constraints of this QNode.  # noqa: E501
        :type constraints: List[AttributeConstraint]
        :param option_group_id: The option_group_id of this QNode.  # noqa: E501
        :type option_group_id: str
        """
        self.openapi_types = {
            'ids': List[str],
            'categories': List[str],
            'is_set': bool,
            'constraints': List[AttributeConstraint],
            'option_group_id': str
        }

        self.attribute_map = {
            'ids': 'ids',
            'categories': 'categories',
            'is_set': 'is_set',
            'constraints': 'constraints',
            'option_group_id': 'option_group_id'
        }

        self._ids = ids
        self._categories = categories
        self._is_set = is_set
        self._constraints = constraints
        self._option_group_id = option_group_id

    @classmethod
    def from_dict(cls, dikt) -> 'QNode':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The QNode of this QNode.  # noqa: E501
        :rtype: QNode
        """
        return util.deserialize_model(dikt, cls)

    @property
    def ids(self):
        """Gets the ids of this QNode.

        CURIE identifier for this node  # noqa: E501

        :return: The ids of this QNode.
        :rtype: List[str]
        """
        return self._ids

    @ids.setter
    def ids(self, ids):
        """Sets the ids of this QNode.

        CURIE identifier for this node  # noqa: E501

        :param ids: The ids of this QNode.
        :type ids: List[str]
        """
        if ids is not None and len(ids) < 1:
            raise ValueError("Invalid value for `ids`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._ids = ids

    @property
    def categories(self):
        """Gets the categories of this QNode.


        :return: The categories of this QNode.
        :rtype: List[str]
        """
        return self._categories

    @categories.setter
    def categories(self, categories):
        """Sets the categories of this QNode.


        :param categories: The categories of this QNode.
        :type categories: List[str]
        """
        if categories is not None and len(categories) < 1:
            raise ValueError("Invalid value for `categories`, number of items must be greater than or equal to `1`")  # noqa: E501

        self._categories = categories

    @property
    def is_set(self):
        """Gets the is_set of this QNode.

        Boolean that if set to true, indicates that this QNode MAY have multiple KnowledgeGraph Nodes bound to it within each Result. The nodes in a set should be considered as a set of independent nodes, rather than a set of dependent nodes, i.e., the answer would still be valid if the nodes in the set were instead returned individually. Multiple QNodes may have is_set=True. If a QNode (n1) with is_set=True is connected to a QNode (n2) with is_set=False, each n1 must be connected to n2. If a QNode (n1) with is_set=True is connected to a QNode (n2) with is_set=True, each n1 must be connected to at least one n2.  # noqa: E501

        :return: The is_set of this QNode.
        :rtype: bool
        """
        return self._is_set

    @is_set.setter
    def is_set(self, is_set):
        """Sets the is_set of this QNode.

        Boolean that if set to true, indicates that this QNode MAY have multiple KnowledgeGraph Nodes bound to it within each Result. The nodes in a set should be considered as a set of independent nodes, rather than a set of dependent nodes, i.e., the answer would still be valid if the nodes in the set were instead returned individually. Multiple QNodes may have is_set=True. If a QNode (n1) with is_set=True is connected to a QNode (n2) with is_set=False, each n1 must be connected to n2. If a QNode (n1) with is_set=True is connected to a QNode (n2) with is_set=True, each n1 must be connected to at least one n2.  # noqa: E501

        :param is_set: The is_set of this QNode.
        :type is_set: bool
        """

        self._is_set = is_set

    @property
    def constraints(self):
        """Gets the constraints of this QNode.

        A list of contraints applied to a query node. If there are multiple items, they must all be true (equivalent to AND)  # noqa: E501

        :return: The constraints of this QNode.
        :rtype: List[AttributeConstraint]
        """
        return self._constraints

    @constraints.setter
    def constraints(self, constraints):
        """Sets the constraints of this QNode.

        A list of contraints applied to a query node. If there are multiple items, they must all be true (equivalent to AND)  # noqa: E501

        :param constraints: The constraints of this QNode.
        :type constraints: List[AttributeConstraint]
        """

        self._constraints = constraints

    @property
    def option_group_id(self):
        """Gets the option_group_id of this QNode.

        Optional string acting as a label on a set of nodes and/or edges indicating that they belong to a group that are to be evaluated as a group.   # noqa: E501

        :return: The option_group_id of this QNode.
        :rtype: str
        """
        return self._option_group_id

    @option_group_id.setter
    def option_group_id(self, option_group_id):
        """Sets the option_group_id of this QNode.

        Optional string acting as a label on a set of nodes and/or edges indicating that they belong to a group that are to be evaluated as a group.   # noqa: E501

        :param option_group_id: The option_group_id of this QNode.
        :type option_group_id: str
        """

        self._option_group_id = option_group_id


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/qualifier.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server import util


class Qualifier(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, qualifier_type_id=None, qualifier_value=None):  # noqa: E501
        """Qualifier - a model defined in OpenAPI

        :param qualifier_type_id: The qualifier_type_id of this Qualifier.  # noqa: E501
        :type qualifier_type_id: str
        :param qualifier_value: The qualifier_value of this Qualifier.  # noqa: E501
        :type qualifier_value: str
        """
        self.openapi_types = {
            'qualifier_type_id': str,
            'qualifier_value': str
        }

        self.attribute_map = {
            'qualifier_type_id': 'qualifier_type_id',
            'qualifier_value': 'qualifier_value'
        }

        self._qualifier_type_id = qualifier_type_id
        self._qualifier_value = qualifier_value

    @classmethod
    def from_dict(cls, dikt) -> 'Qualifier':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Qualifier of this Qualifier.  # noqa: E501
        :rtype: Qualifier
        """
        return util.deserialize_model(dikt, cls)

    @property
    def qualifier_type_id(self):
        """Gets the qualifier_type_id of this Qualifier.

        The category of the qualifier, drawn from a hierarchy of qualifier slots in the Biolink model (e.g. subject_aspect, subject_direction, object_aspect, object_direction, etc).  # noqa: E501

        :return: The qualifier_type_id of this Qualifier.
        :rtype: str
        """
        return self._qualifier_type_id

    @qualifier_type_id.setter
    def qualifier_type_id(self, qualifier_type_id):
        """Sets the qualifier_type_id of this Qualifier.

        The category of the qualifier, drawn from a hierarchy of qualifier slots in the Biolink model (e.g. subject_aspect, subject_direction, object_aspect, object_direction, etc).  # noqa: E501

        :param qualifier_type_id: The qualifier_type_id of this Qualifier.
        :type qualifier_type_id: str
        """
        if qualifier_type_id is None:
            raise ValueError("Invalid value for `qualifier_type_id`, must not be `None`")  # noqa: E501

        self._qualifier_type_id = qualifier_type_id

    @property
    def qualifier_value(self):
        """Gets the qualifier_value of this Qualifier.

        The value associated with the type of the qualifier, drawn from a set of controlled values by the type as specified in the Biolink model (e.g. 'expression' or 'abundance' for the qualifier type 'subject_aspect', etc).  # noqa: E501

        :return: The qualifier_value of this Qualifier.
        :rtype: str
        """
        return self._qualifier_value

    @qualifier_value.setter
    def qualifier_value(self, qualifier_value):
        """Sets the qualifier_value of this Qualifier.

        The value associated with the type of the qualifier, drawn from a set of controlled values by the type as specified in the Biolink model (e.g. 'expression' or 'abundance' for the qualifier type 'subject_aspect', etc).  # noqa: E501

        :param qualifier_value: The qualifier_value of this Qualifier.
        :type qualifier_value: str
        """
        if qualifier_value is None:
            raise ValueError("Invalid value for `qualifier_value`, must not be `None`")  # noqa: E501

        self._qualifier_value = qualifier_value


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/qualifier_constraint.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.qualifier import Qualifier
from openapi_server import util

from openapi_server.models.qualifier import Qualifier  # noqa: E501

class QualifierConstraint(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, qualifier_set=None):  # noqa: E501
        """QualifierConstraint - a model defined in OpenAPI

        :param qualifier_set: The qualifier_set of this QualifierConstraint.  # noqa: E501
        :type qualifier_set: List[Qualifier]
        """
        self.openapi_types = {
            'qualifier_set': List[Qualifier]
        }

        self.attribute_map = {
            'qualifier_set': 'qualifier_set'
        }

        self._qualifier_set = qualifier_set

    @classmethod
    def from_dict(cls, dikt) -> 'QualifierConstraint':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The QualifierConstraint of this QualifierConstraint.  # noqa: E501
        :rtype: QualifierConstraint
        """
        return util.deserialize_model(dikt, cls)

    @property
    def qualifier_set(self):
        """Gets the qualifier_set of this QualifierConstraint.

        A set of Qualifiers that serves to add nuance to a query, by constraining allowed values held by Qualifiers on queried Edges.  # noqa: E501

        :return: The qualifier_set of this QualifierConstraint.
        :rtype: List[Qualifier]
        """
        return self._qualifier_set

    @qualifier_set.setter
    def qualifier_set(self, qualifier_set):
        """Sets the qualifier_set of this QualifierConstraint.

        A set of Qualifiers that serves to add nuance to a query, by constraining allowed values held by Qualifiers on queried Edges.  # noqa: E501

        :param qualifier_set: The qualifier_set of this QualifierConstraint.
        :type qualifier_set: List[Qualifier]
        """
        if qualifier_set is None:
            raise ValueError("Invalid value for `qualifier_set`, must not be `None`")  # noqa: E501

        self._qualifier_set = qualifier_set


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/query.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_level import LogLevel
from openapi_server.models.message import Message
from openapi_server.models.any_type import AnyType
from openapi_server.models.operations import Operations
from openapi_server import util

from openapi_server.models.log_level import LogLevel  # noqa: E501
from openapi_server.models.message import Message  # noqa: E501
from openapi_server.models.any_type import AnyType  # noqa: E501
from openapi_server.models.operations import Operations  # noqa: E501

class Query(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, message=None, log_level=None, workflow=None, submitter=None, bypass_cache=False, stream_progress=False, enforce_edge_directionality=False, return_minimal_metadata=False, max_results=100, page_size=100, page_number=1, operations=None):  # noqa: E501
        """Query - a model defined in OpenAPI

        :param message: The message of this Query.  # noqa: E501
        :type message: Message
        :param log_level: The log_level of this Query.  # noqa: E501
        :type log_level: LogLevel
        :param workflow: The workflow of this Query.  # noqa: E501
        :type workflow: List[AnyType]
        :param submitter: The submitter of this Query.  # noqa: E501
        :type submitter: str
        :param bypass_cache: The bypass_cache of this Query.  # noqa: E501
        :type bypass_cache: bool
        :param stream_progress: The stream_progress of this Query.  # noqa: E501
        :type stream_progress: bool
        :param enforce_edge_directionality: The enforce_edge_directionality of this Query.  # noqa: E501
        :type enforce_edge_directionality: bool
        :param return_minimal_metadata: The return_minimal_metadata of this Query.  # noqa: E501
        :type return_minimal_metadata: bool
        :param max_results: The max_results of this Query.  # noqa: E501
        :type max_results: int
        :param page_size: The page_size of this Query.  # noqa: E501
        :type page_size: int
        :param page_number: The page_number of this Query.  # noqa: E501
        :type page_number: int
        :param operations: The operations of this Query.  # noqa: E501
        :type operations: Operations
        """
        self.openapi_types = {
            'message': Message,
            'log_level': LogLevel,
            'workflow': List[AnyType],
            'submitter': str,
            'bypass_cache': bool,
            'stream_progress': bool,
            'enforce_edge_directionality': bool,
            'return_minimal_metadata': bool,
            'max_results': int,
            'page_size': int,
            'page_number': int,
            'operations': Operations
        }

        self.attribute_map = {
            'message': 'message',
            'log_level': 'log_level',
            'workflow': 'workflow',
            'submitter': 'submitter',
            'bypass_cache': 'bypass_cache',
            'stream_progress': 'stream_progress',
            'enforce_edge_directionality': 'enforce_edge_directionality',
            'return_minimal_metadata': 'return_minimal_metadata',
            'max_results': 'max_results',
            'page_size': 'page_size',
            'page_number': 'page_number',
            'operations': 'operations'
        }

        self._message = message
        self._log_level = log_level
        self._workflow = workflow
        self._submitter = submitter
        self._bypass_cache = bypass_cache
        self._stream_progress = stream_progress
        self._enforce_edge_directionality = enforce_edge_directionality
        self._return_minimal_metadata = return_minimal_metadata
        self._max_results = max_results
        self._page_size = page_size
        self._page_number = page_number
        self._operations = operations

    @classmethod
    def from_dict(cls, dikt) -> 'Query':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Query of this Query.  # noqa: E501
        :rtype: Query
        """
        return util.deserialize_model(dikt, cls)

    @property
    def message(self):
        """Gets the message of this Query.


        :return: The message of this Query.
        :rtype: Message
        """
        return self._message

    @message.setter
    def message(self, message):
        """Sets the message of this Query.


        :param message: The message of this Query.
        :type message: Message
        """
        if message is None:
            raise ValueError("Invalid value for `message`, must not be `None`")  # noqa: E501

        self._message = message

    @property
    def log_level(self):
        """Gets the log_level of this Query.

        The least critical level of logs to return  # noqa: E501

        :return: The log_level of this Query.
        :rtype: LogLevel
        """
        return self._log_level

    @log_level.setter
    def log_level(self, log_level):
        """Sets the log_level of this Query.

        The least critical level of logs to return  # noqa: E501

        :param log_level: The log_level of this Query.
        :type log_level: LogLevel
        """

        self._log_level = log_level

    @property
    def workflow(self):
        """Gets the workflow of this Query.

        List of workflow steps to be executed.  # noqa: E501

        :return: The workflow of this Query.
        :rtype: List[AnyType]
        """
        return self._workflow

    @workflow.setter
    def workflow(self, workflow):
        """Sets the workflow of this Query.

        List of workflow steps to be executed.  # noqa: E501

        :param workflow: The workflow of this Query.
        :type workflow: List[AnyType]
        """

        self._workflow = workflow

    @property
    def submitter(self):
        """Gets the submitter of this Query.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :return: The submitter of this Query.
        :rtype: str
        """
        return self._submitter

    @submitter.setter
    def submitter(self, submitter):
        """Sets the submitter of this Query.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :param submitter: The submitter of this Query.
        :type submitter: str
        """

        self._submitter = submitter

    @property
    def bypass_cache(self):
        """Gets the bypass_cache of this Query.

        Set to true in order to bypass any possible cached response and try to answer the query from scratch  # noqa: E501

        :return: The bypass_cache of this Query.
        :rtype: bool
        """
        return self._bypass_cache

    @bypass_cache.setter
    def bypass_cache(self, bypass_cache):
        """Sets the bypass_cache of this Query.

        Set to true in order to bypass any possible cached response and try to answer the query from scratch  # noqa: E501

        :param bypass_cache: The bypass_cache of this Query.
        :type bypass_cache: bool
        """

        self._bypass_cache = bypass_cache

    @property
    def stream_progress(self):
        """Gets the stream_progress of this Query.

        Set to true in order to receive a stream of LogEntry objects as the query is progressing  # noqa: E501

        :return: The stream_progress of this Query.
        :rtype: bool
        """
        return self._stream_progress

    @stream_progress.setter
    def stream_progress(self, stream_progress):
        """Sets the stream_progress of this Query.

        Set to true in order to receive a stream of LogEntry objects as the query is progressing  # noqa: E501

        :param stream_progress: The stream_progress of this Query.
        :type stream_progress: bool
        """

        self._stream_progress = stream_progress

    @property
    def enforce_edge_directionality(self):
        """Gets the enforce_edge_directionality of this Query.

        Set to true in order to enforce edge directionality during queries. By default, edge directionality can be reversed without penalty.  # noqa: E501

        :return: The enforce_edge_directionality of this Query.
        :rtype: bool
        """
        return self._enforce_edge_directionality

    @enforce_edge_directionality.setter
    def enforce_edge_directionality(self, enforce_edge_directionality):
        """Sets the enforce_edge_directionality of this Query.

        Set to true in order to enforce edge directionality during queries. By default, edge directionality can be reversed without penalty.  # noqa: E501

        :param enforce_edge_directionality: The enforce_edge_directionality of this Query.
        :type enforce_edge_directionality: bool
        """

        self._enforce_edge_directionality = enforce_edge_directionality

    @property
    def return_minimal_metadata(self):
        """Gets the return_minimal_metadata of this Query.

        Set to true in order to return only the most minimal metadata. By default, rich metadata for nodes and edges are supplied, which may cause a very large and slower Response.  # noqa: E501

        :return: The return_minimal_metadata of this Query.
        :rtype: bool
        """
        return self._return_minimal_metadata

    @return_minimal_metadata.setter
    def return_minimal_metadata(self, return_minimal_metadata):
        """Sets the return_minimal_metadata of this Query.

        Set to true in order to return only the most minimal metadata. By default, rich metadata for nodes and edges are supplied, which may cause a very large and slower Response.  # noqa: E501

        :param return_minimal_metadata: The return_minimal_metadata of this Query.
        :type return_minimal_metadata: bool
        """

        self._return_minimal_metadata = return_minimal_metadata

    @property
    def max_results(self):
        """Gets the max_results of this Query.

        Maximum number of individual results to return  # noqa: E501

        :return: The max_results of this Query.
        :rtype: int
        """
        return self._max_results

    @max_results.setter
    def max_results(self, max_results):
        """Sets the max_results of this Query.

        Maximum number of individual results to return  # noqa: E501

        :param max_results: The max_results of this Query.
        :type max_results: int
        """

        self._max_results = max_results

    @property
    def page_size(self):
        """Gets the page_size of this Query.

        Split the results into pages with this number of results each  # noqa: E501

        :return: The page_size of this Query.
        :rtype: int
        """
        return self._page_size

    @page_size.setter
    def page_size(self, page_size):
        """Sets the page_size of this Query.

        Split the results into pages with this number of results each  # noqa: E501

        :param page_size: The page_size of this Query.
        :type page_size: int
        """

        self._page_size = page_size

    @property
    def page_number(self):
        """Gets the page_number of this Query.

        Page number of results when the number of results exceeds the page_size, with page 1 as the first page  # noqa: E501

        :return: The page_number of this Query.
        :rtype: int
        """
        return self._page_number

    @page_number.setter
    def page_number(self, page_number):
        """Sets the page_number of this Query.

        Page number of results when the number of results exceeds the page_size, with page 1 as the first page  # noqa: E501

        :param page_number: The page_number of this Query.
        :type page_number: int
        """

        self._page_number = page_number

    @property
    def operations(self):
        """Gets the operations of this Query.

        Container for one or more Message objects or identifiers for one or more Messages along with a processing plan and options for how those messages should be processed and returned  # noqa: E501

        :return: The operations of this Query.
        :rtype: Operations
        """
        return self._operations

    @operations.setter
    def operations(self, operations):
        """Sets the operations of this Query.

        Container for one or more Message objects or identifiers for one or more Messages along with a processing plan and options for how those messages should be processed and returned  # noqa: E501

        :param operations: The operations of this Query.
        :type operations: Operations
        """

        self._operations = operations


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/query_graph.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.q_edge import QEdge
from openapi_server.models.q_node import QNode
from openapi_server import util

from openapi_server.models.q_edge import QEdge  # noqa: E501
from openapi_server.models.q_node import QNode  # noqa: E501

class QueryGraph(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, nodes=None, edges=None):  # noqa: E501
        """QueryGraph - a model defined in OpenAPI

        :param nodes: The nodes of this QueryGraph.  # noqa: E501
        :type nodes: Dict[str, QNode]
        :param edges: The edges of this QueryGraph.  # noqa: E501
        :type edges: Dict[str, QEdge]
        """
        self.openapi_types = {
            'nodes': Dict[str, QNode],
            'edges': Dict[str, QEdge]
        }

        self.attribute_map = {
            'nodes': 'nodes',
            'edges': 'edges'
        }

        self._nodes = nodes
        self._edges = edges

    @classmethod
    def from_dict(cls, dikt) -> 'QueryGraph':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The QueryGraph of this QueryGraph.  # noqa: E501
        :rtype: QueryGraph
        """
        return util.deserialize_model(dikt, cls)

    @property
    def nodes(self):
        """Gets the nodes of this QueryGraph.

        The node specifications. The keys of this map are unique node identifiers and the corresponding values include the constraints on bound nodes.  # noqa: E501

        :return: The nodes of this QueryGraph.
        :rtype: Dict[str, QNode]
        """
        return self._nodes

    @nodes.setter
    def nodes(self, nodes):
        """Sets the nodes of this QueryGraph.

        The node specifications. The keys of this map are unique node identifiers and the corresponding values include the constraints on bound nodes.  # noqa: E501

        :param nodes: The nodes of this QueryGraph.
        :type nodes: Dict[str, QNode]
        """
        if nodes is None:
            raise ValueError("Invalid value for `nodes`, must not be `None`")  # noqa: E501

        self._nodes = nodes

    @property
    def edges(self):
        """Gets the edges of this QueryGraph.

        The edge specifications. The keys of this map are unique edge identifiers and the corresponding values include the constraints on bound edges, in addition to specifying the subject and object QNodes.  # noqa: E501

        :return: The edges of this QueryGraph.
        :rtype: Dict[str, QEdge]
        """
        return self._edges

    @edges.setter
    def edges(self, edges):
        """Sets the edges of this QueryGraph.

        The edge specifications. The keys of this map are unique edge identifiers and the corresponding values include the constraints on bound edges, in addition to specifying the subject and object QNodes.  # noqa: E501

        :param edges: The edges of this QueryGraph.
        :type edges: Dict[str, QEdge]
        """
        if edges is None:
            raise ValueError("Invalid value for `edges`, must not be `None`")  # noqa: E501

        self._edges = edges


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/response.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.log_entry import LogEntry
from openapi_server.models.message import Message
from openapi_server.models.any_type import AnyType
from openapi_server.models.operations import Operations
from openapi_server import util

from openapi_server.models.log_entry import LogEntry  # noqa: E501
from openapi_server.models.message import Message  # noqa: E501
from openapi_server.models.any_type import AnyType  # noqa: E501
from openapi_server.models.operations import Operations  # noqa: E501

class Response(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, message=None, status=None, description=None, logs=None, workflow=None, submitter=None, operations=None, reasoner_id=None, tool_version=None, schema_version=None, datetime=None, table_column_names=None, original_question=None, restated_question=None, query_options=None, context=None, type=None, id=None, validation_result=None, total_results_count=None, info=None):  # noqa: E501
        """Response - a model defined in OpenAPI

        :param message: The message of this Response.  # noqa: E501
        :type message: Message
        :param status: The status of this Response.  # noqa: E501
        :type status: str
        :param description: The description of this Response.  # noqa: E501
        :type description: str
        :param logs: The logs of this Response.  # noqa: E501
        :type logs: List[LogEntry]
        :param workflow: The workflow of this Response.  # noqa: E501
        :type workflow: List[AnyType]
        :param submitter: The submitter of this Response.  # noqa: E501
        :type submitter: str
        :param operations: The operations of this Response.  # noqa: E501
        :type operations: Operations
        :param reasoner_id: The reasoner_id of this Response.  # noqa: E501
        :type reasoner_id: str
        :param tool_version: The tool_version of this Response.  # noqa: E501
        :type tool_version: str
        :param schema_version: The schema_version of this Response.  # noqa: E501
        :type schema_version: str
        :param datetime: The datetime of this Response.  # noqa: E501
        :type datetime: str
        :param table_column_names: The table_column_names of this Response.  # noqa: E501
        :type table_column_names: List[str]
        :param original_question: The original_question of this Response.  # noqa: E501
        :type original_question: str
        :param restated_question: The restated_question of this Response.  # noqa: E501
        :type restated_question: str
        :param query_options: The query_options of this Response.  # noqa: E501
        :type query_options: object
        :param context: The context of this Response.  # noqa: E501
        :type context: str
        :param type: The type of this Response.  # noqa: E501
        :type type: str
        :param id: The id of this Response.  # noqa: E501
        :type id: str
        :param validation_result: The validation_result of this Response.  # noqa: E501
        :type validation_result: object
        :param total_results_count: The total_results_count of this Response.  # noqa: E501
        :type total_results_count: int
        :param info: The info of this Response.  # noqa: E501
        :type info: str
        """
        self.openapi_types = {
            'message': Message,
            'status': str,
            'description': str,
            'logs': List[LogEntry],
            'workflow': List[AnyType],
            'submitter': str,
            'operations': Operations,
            'reasoner_id': str,
            'tool_version': str,
            'schema_version': str,
            'datetime': str,
            'table_column_names': List[str],
            'original_question': str,
            'restated_question': str,
            'query_options': object,
            'context': str,
            'type': str,
            'id': str,
            'validation_result': object,
            'total_results_count': int,
            'info': str
        }

        self.attribute_map = {
            'message': 'message',
            'status': 'status',
            'description': 'description',
            'logs': 'logs',
            'workflow': 'workflow',
            'submitter': 'submitter',
            'operations': 'operations',
            'reasoner_id': 'reasoner_id',
            'tool_version': 'tool_version',
            'schema_version': 'schema_version',
            'datetime': 'datetime',
            'table_column_names': 'table_column_names',
            'original_question': 'original_question',
            'restated_question': 'restated_question',
            'query_options': 'query_options',
            'context': 'context',
            'type': 'type',
            'id': 'id',
            'validation_result': 'validation_result',
            'total_results_count': 'total_results_count',
            'info': 'info'
        }

        self._message = message
        self._status = status
        self._description = description
        self._logs = logs
        self._workflow = workflow
        self._submitter = submitter
        self._operations = operations
        self._reasoner_id = reasoner_id
        self._tool_version = tool_version
        self._schema_version = schema_version
        self._datetime = datetime
        self._table_column_names = table_column_names
        self._original_question = original_question
        self._restated_question = restated_question
        self._query_options = query_options
        self._context = context
        self._type = type
        self._id = id
        self._validation_result = validation_result
        self._total_results_count = total_results_count
        self._info = info

    @classmethod
    def from_dict(cls, dikt) -> 'Response':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Response of this Response.  # noqa: E501
        :rtype: Response
        """
        return util.deserialize_model(dikt, cls)

    @property
    def message(self):
        """Gets the message of this Response.


        :return: The message of this Response.
        :rtype: Message
        """
        return self._message

    @message.setter
    def message(self, message):
        """Sets the message of this Response.


        :param message: The message of this Response.
        :type message: Message
        """
        if message is None:
            raise ValueError("Invalid value for `message`, must not be `None`")  # noqa: E501

        self._message = message

    @property
    def status(self):
        """Gets the status of this Response.

        One of a standardized set of short codes, e.g. Success, QueryNotTraversable, KPsNotAvailable  # noqa: E501

        :return: The status of this Response.
        :rtype: str
        """
        return self._status

    @status.setter
    def status(self, status):
        """Sets the status of this Response.

        One of a standardized set of short codes, e.g. Success, QueryNotTraversable, KPsNotAvailable  # noqa: E501

        :param status: The status of this Response.
        :type status: str
        """

        self._status = status

    @property
    def description(self):
        """Gets the description of this Response.

        A brief human-readable description of the outcome  # noqa: E501

        :return: The description of this Response.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this Response.

        A brief human-readable description of the outcome  # noqa: E501

        :param description: The description of this Response.
        :type description: str
        """

        self._description = description

    @property
    def logs(self):
        """Gets the logs of this Response.

        Log entries containing errors, warnings, debugging information, etc  # noqa: E501

        :return: The logs of this Response.
        :rtype: List[LogEntry]
        """
        return self._logs

    @logs.setter
    def logs(self, logs):
        """Sets the logs of this Response.

        Log entries containing errors, warnings, debugging information, etc  # noqa: E501

        :param logs: The logs of this Response.
        :type logs: List[LogEntry]
        """

        self._logs = logs

    @property
    def workflow(self):
        """Gets the workflow of this Response.

        A list of operations that were executed.  # noqa: E501

        :return: The workflow of this Response.
        :rtype: List[AnyType]
        """
        return self._workflow

    @workflow.setter
    def workflow(self, workflow):
        """Sets the workflow of this Response.

        A list of operations that were executed.  # noqa: E501

        :param workflow: The workflow of this Response.
        :type workflow: List[AnyType]
        """

        self._workflow = workflow

    @property
    def submitter(self):
        """Gets the submitter of this Response.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :return: The submitter of this Response.
        :rtype: str
        """
        return self._submitter

    @submitter.setter
    def submitter(self, submitter):
        """Sets the submitter of this Response.

        Any string for self-identifying the submitter of a query. The purpose of this optional field is to aid in the tracking of the source of queries for development and issue resolution.  # noqa: E501

        :param submitter: The submitter of this Response.
        :type submitter: str
        """

        self._submitter = submitter

    @property
    def operations(self):
        """Gets the operations of this Response.

        Container for one or more Message objects or identifiers for one or more Messages along with the processing plan and options for how those messages were processed and returned  # noqa: E501

        :return: The operations of this Response.
        :rtype: Operations
        """
        return self._operations

    @operations.setter
    def operations(self, operations):
        """Sets the operations of this Response.

        Container for one or more Message objects or identifiers for one or more Messages along with the processing plan and options for how those messages were processed and returned  # noqa: E501

        :param operations: The operations of this Response.
        :type operations: Operations
        """

        self._operations = operations

    @property
    def reasoner_id(self):
        """Gets the reasoner_id of this Response.

        Identifier string of the reasoner that provided this response (one of ARAX, Robokop, etc.)  # noqa: E501

        :return: The reasoner_id of this Response.
        :rtype: str
        """
        return self._reasoner_id

    @reasoner_id.setter
    def reasoner_id(self, reasoner_id):
        """Sets the reasoner_id of this Response.

        Identifier string of the reasoner that provided this response (one of ARAX, Robokop, etc.)  # noqa: E501

        :param reasoner_id: The reasoner_id of this Response.
        :type reasoner_id: str
        """

        self._reasoner_id = reasoner_id

    @property
    def tool_version(self):
        """Gets the tool_version of this Response.

        Version label of the tool that generated this response  # noqa: E501

        :return: The tool_version of this Response.
        :rtype: str
        """
        return self._tool_version

    @tool_version.setter
    def tool_version(self, tool_version):
        """Sets the tool_version of this Response.

        Version label of the tool that generated this response  # noqa: E501

        :param tool_version: The tool_version of this Response.
        :type tool_version: str
        """

        self._tool_version = tool_version

    @property
    def schema_version(self):
        """Gets the schema_version of this Response.

        Version label of this TRAPI schema  # noqa: E501

        :return: The schema_version of this Response.
        :rtype: str
        """
        return self._schema_version

    @schema_version.setter
    def schema_version(self, schema_version):
        """Sets the schema_version of this Response.

        Version label of this TRAPI schema  # noqa: E501

        :param schema_version: The schema_version of this Response.
        :type schema_version: str
        """

        self._schema_version = schema_version

    @property
    def datetime(self):
        """Gets the datetime of this Response.

        Datetime string for the time that this response was generated  # noqa: E501

        :return: The datetime of this Response.
        :rtype: str
        """
        return self._datetime

    @datetime.setter
    def datetime(self, datetime):
        """Sets the datetime of this Response.

        Datetime string for the time that this response was generated  # noqa: E501

        :param datetime: The datetime of this Response.
        :type datetime: str
        """

        self._datetime = datetime

    @property
    def table_column_names(self):
        """Gets the table_column_names of this Response.

        List of column names that corresponds to the row_data for each result  # noqa: E501

        :return: The table_column_names of this Response.
        :rtype: List[str]
        """
        return self._table_column_names

    @table_column_names.setter
    def table_column_names(self, table_column_names):
        """Sets the table_column_names of this Response.

        List of column names that corresponds to the row_data for each result  # noqa: E501

        :param table_column_names: The table_column_names of this Response.
        :type table_column_names: List[str]
        """

        self._table_column_names = table_column_names

    @property
    def original_question(self):
        """Gets the original_question of this Response.

        The original question text typed in by the user  # noqa: E501

        :return: The original_question of this Response.
        :rtype: str
        """
        return self._original_question

    @original_question.setter
    def original_question(self, original_question):
        """Sets the original_question of this Response.

        The original question text typed in by the user  # noqa: E501

        :param original_question: The original_question of this Response.
        :type original_question: str
        """

        self._original_question = original_question

    @property
    def restated_question(self):
        """Gets the restated_question of this Response.

        A precise restatement of the question, as understood by the Translator, for which the answer applies. The user should verify that the restated question matches the intent of their original question (it might not).  # noqa: E501

        :return: The restated_question of this Response.
        :rtype: str
        """
        return self._restated_question

    @restated_question.setter
    def restated_question(self, restated_question):
        """Sets the restated_question of this Response.

        A precise restatement of the question, as understood by the Translator, for which the answer applies. The user should verify that the restated question matches the intent of their original question (it might not).  # noqa: E501

        :param restated_question: The restated_question of this Response.
        :type restated_question: str
        """

        self._restated_question = restated_question

    @property
    def query_options(self):
        """Gets the query_options of this Response.

        Dict of options that can be sent with the query. Options are tool specific and not stipulated here  # noqa: E501

        :return: The query_options of this Response.
        :rtype: object
        """
        return self._query_options

    @query_options.setter
    def query_options(self, query_options):
        """Sets the query_options of this Response.

        Dict of options that can be sent with the query. Options are tool specific and not stipulated here  # noqa: E501

        :param query_options: The query_options of this Response.
        :type query_options: object
        """

        self._query_options = query_options

    @property
    def context(self):
        """Gets the context of this Response.

        JSON-LD context URI  # noqa: E501

        :return: The context of this Response.
        :rtype: str
        """
        return self._context

    @context.setter
    def context(self, context):
        """Sets the context of this Response.

        JSON-LD context URI  # noqa: E501

        :param context: The context of this Response.
        :type context: str
        """

        self._context = context

    @property
    def type(self):
        """Gets the type of this Response.

        Entity type of this response  # noqa: E501

        :return: The type of this Response.
        :rtype: str
        """
        return self._type

    @type.setter
    def type(self, type):
        """Sets the type of this Response.

        Entity type of this response  # noqa: E501

        :param type: The type of this Response.
        :type type: str
        """

        self._type = type

    @property
    def id(self):
        """Gets the id of this Response.

        URI for this response  # noqa: E501

        :return: The id of this Response.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this Response.

        URI for this response  # noqa: E501

        :param id: The id of this Response.
        :type id: str
        """

        self._id = id

    @property
    def validation_result(self):
        """Gets the validation_result of this Response.

        Validation results and other summary stats computed for this Response.  # noqa: E501

        :return: The validation_result of this Response.
        :rtype: object
        """
        return self._validation_result

    @validation_result.setter
    def validation_result(self, validation_result):
        """Sets the validation_result of this Response.

        Validation results and other summary stats computed for this Response.  # noqa: E501

        :param validation_result: The validation_result of this Response.
        :type validation_result: object
        """

        self._validation_result = validation_result

    @property
    def total_results_count(self):
        """Gets the total_results_count of this Response.

        The total number of results that were generated prior to any filtering.  # noqa: E501

        :return: The total_results_count of this Response.
        :rtype: int
        """
        return self._total_results_count

    @total_results_count.setter
    def total_results_count(self, total_results_count):
        """Sets the total_results_count of this Response.

        The total number of results that were generated prior to any filtering.  # noqa: E501

        :param total_results_count: The total_results_count of this Response.
        :type total_results_count: int
        """

        self._total_results_count = total_results_count

    @property
    def info(self):
        """Gets the info of this Response.

        A placholder for including some additional information  # noqa: E501

        :return: The info of this Response.
        :rtype: str
        """
        return self._info

    @info.setter
    def info(self, info):
        """Sets the info of this Response.

        A placholder for including some additional information  # noqa: E501

        :param info: The info of this Response.
        :type info: str
        """

        self._info = info



# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/result.py

# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.edge_binding import EdgeBinding
from openapi_server.models.node_binding import NodeBinding
from openapi_server.models.any_type import AnyType
from openapi_server import util

from openapi_server.models.edge_binding import EdgeBinding  # noqa: E501
from openapi_server.models.node_binding import NodeBinding  # noqa: E501
from openapi_server.models.any_type import AnyType  # noqa: E501

class Result(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, node_bindings=None, edge_bindings=None, id=None, description=None, essence=None, essence_category=None, row_data=None, score=None, score_name=None, score_direction=None, confidence=None, result_group=None, result_group_similarity_score=None, reasoner_id=None):  # noqa: E501
        """Result - a model defined in OpenAPI

        :param node_bindings: The node_bindings of this Result.  # noqa: E501
        :type node_bindings: Dict[str, List[NodeBinding]]
        :param edge_bindings: The edge_bindings of this Result.  # noqa: E501
        :type edge_bindings: Dict[str, List[EdgeBinding]]
        :param id: The id of this Result.  # noqa: E501
        :type id: str
        :param description: The description of this Result.  # noqa: E501
        :type description: str
        :param essence: The essence of this Result.  # noqa: E501
        :type essence: str
        :param essence_category: The essence_category of this Result.  # noqa: E501
        :type essence_category: str
        :param row_data: The row_data of this Result.  # noqa: E501
        :type row_data: List[AnyType]
        :param score: The score of this Result.  # noqa: E501
        :type score: float
        :param score_name: The score_name of this Result.  # noqa: E501
        :type score_name: str
        :param score_direction: The score_direction of this Result.  # noqa: E501
        :type score_direction: str
        :param confidence: The confidence of this Result.  # noqa: E501
        :type confidence: float
        :param result_group: The result_group of this Result.  # noqa: E501
        :type result_group: int
        :param result_group_similarity_score: The result_group_similarity_score of this Result.  # noqa: E501
        :type result_group_similarity_score: float
        :param reasoner_id: The reasoner_id of this Result.  # noqa: E501
        :type reasoner_id: str
        """
        self.openapi_types = {
            'node_bindings': Dict[str, List[NodeBinding]],
            'edge_bindings': Dict[str, List[EdgeBinding]],
            'id': str,
            'description': str,
            'essence': str,
            'essence_category': str,
            'row_data': List[AnyType],
            'score': float,
            'score_name': str,
            'score_direction': str,
            'confidence': float,
            'result_group': int,
            'result_group_similarity_score': float,
            'reasoner_id': str
        }

        self.attribute_map = {
            'node_bindings': 'node_bindings',
            'edge_bindings': 'edge_bindings',
            'id': 'id',
            'description': 'description',
            'essence': 'essence',
            'essence_category': 'essence_category',
            'row_data': 'row_data',
            'score': 'score',
            'score_name': 'score_name',
            'score_direction': 'score_direction',
            'confidence': 'confidence',
            'result_group': 'result_group',
            'result_group_similarity_score': 'result_group_similarity_score',
            'reasoner_id': 'reasoner_id'
        }

        self._node_bindings = node_bindings
        self._edge_bindings = edge_bindings
        self._id = id
        self._description = description
        self._essence = essence
        self._essence_category = essence_category
        self._row_data = row_data
        self._score = score
        self._score_name = score_name
        self._score_direction = score_direction
        self._confidence = confidence
        self._result_group = result_group
        self._result_group_similarity_score = result_group_similarity_score
        self._reasoner_id = reasoner_id

    @classmethod
    def from_dict(cls, dikt) -> 'Result':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Result of this Result.  # noqa: E501
        :rtype: Result
        """
        return util.deserialize_model(dikt, cls)

    @property
    def node_bindings(self):
        """Gets the node_bindings of this Result.

        The dictionary of Input Query Graph to Result Knowledge Graph node bindings where the dictionary keys are the key identifiers of the Query Graph nodes and the associated values of those keys are instances of NodeBinding schema type (see below). This value is an array of NodeBindings since a given query node may have multiple knowledge graph Node bindings in the result.  # noqa: E501

        :return: The node_bindings of this Result.
        :rtype: Dict[str, List[NodeBinding]]
        """
        return self._node_bindings

    @node_bindings.setter
    def node_bindings(self, node_bindings):
        """Sets the node_bindings of this Result.

        The dictionary of Input Query Graph to Result Knowledge Graph node bindings where the dictionary keys are the key identifiers of the Query Graph nodes and the associated values of those keys are instances of NodeBinding schema type (see below). This value is an array of NodeBindings since a given query node may have multiple knowledge graph Node bindings in the result.  # noqa: E501

        :param node_bindings: The node_bindings of this Result.
        :type node_bindings: Dict[str, List[NodeBinding]]
        """
        if node_bindings is None:
            raise ValueError("Invalid value for `node_bindings`, must not be `None`")  # noqa: E501

        self._node_bindings = node_bindings

    @property
    def edge_bindings(self):
        """Gets the edge_bindings of this Result.

        The dictionary of Input Query Graph to Result Knowledge Graph edge bindings where the dictionary keys are the key identifiers of the Query Graph edges and the associated values of those keys are instances of EdgeBinding schema type (see below). This value is an array of EdgeBindings since a given query edge may resolve to multiple knowledge graph edges in the result.  # noqa: E501

        :return: The edge_bindings of this Result.
        :rtype: Dict[str, List[EdgeBinding]]
        """
        return self._edge_bindings

    @edge_bindings.setter
    def edge_bindings(self, edge_bindings):
        """Sets the edge_bindings of this Result.

        The dictionary of Input Query Graph to Result Knowledge Graph edge bindings where the dictionary keys are the key identifiers of the Query Graph edges and the associated values of those keys are instances of EdgeBinding schema type (see below). This value is an array of EdgeBindings since a given query edge may resolve to multiple knowledge graph edges in the result.  # noqa: E501

        :param edge_bindings: The edge_bindings of this Result.
        :type edge_bindings: Dict[str, List[EdgeBinding]]
        """
        if edge_bindings is None:
            raise ValueError("Invalid value for `edge_bindings`, must not be `None`")  # noqa: E501

        self._edge_bindings = edge_bindings

    @property
    def id(self):
        """Gets the id of this Result.

        URI for this result  # noqa: E501

        :return: The id of this Result.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this Result.

        URI for this result  # noqa: E501

        :param id: The id of this Result.
        :type id: str
        """

        self._id = id

    @property
    def description(self):
        """Gets the description of this Result.

        A free text description of this result answer from the reasoner  # noqa: E501

        :return: The description of this Result.
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this Result.

        A free text description of this result answer from the reasoner  # noqa: E501

        :param description: The description of this Result.
        :type description: str
        """

        self._description = description

    @property
    def essence(self):
        """Gets the essence of this Result.

        A single string that is the terse essence of the result (useful for simple answers)  # noqa: E501

        :return: The essence of this Result.
        :rtype: str
        """
        return self._essence

    @essence.setter
    def essence(self, essence):
        """Sets the essence of this Result.

        A single string that is the terse essence of the result (useful for simple answers)  # noqa: E501

        :param essence: The essence of this Result.
        :type essence: str
        """

        self._essence = essence

    @property
    def essence_category(self):
        """Gets the essence_category of this Result.

        A Translator BioLink bioentity category of the essence  # noqa: E501

        :return: The essence_category of this Result.
        :rtype: str
        """
        return self._essence_category

    @essence_category.setter
    def essence_category(self, essence_category):
        """Sets the essence_category of this Result.

        A Translator BioLink bioentity category of the essence  # noqa: E501

        :param essence_category: The essence_category of this Result.
        :type essence_category: str
        """

        self._essence_category = essence_category

    @property
    def row_data(self):
        """Gets the row_data of this Result.

        An arbitrary list of values that captures the essence of the result that can be turned into a tabular result across all answers (each result is a row) for a user that wants simplified tabular output  # noqa: E501

        :return: The row_data of this Result.
        :rtype: List[AnyType]
        """
        return self._row_data

    @row_data.setter
    def row_data(self, row_data):
        """Sets the row_data of this Result.

        An arbitrary list of values that captures the essence of the result that can be turned into a tabular result across all answers (each result is a row) for a user that wants simplified tabular output  # noqa: E501

        :param row_data: The row_data of this Result.
        :type row_data: List[AnyType]
        """

        self._row_data = row_data

    @property
    def score(self):
        """Gets the score of this Result.

        A numerical score associated with this result indicating the relevance or confidence of this result relative to others in the returned set. Higher MUST be better.  # noqa: E501

        :return: The score of this Result.
        :rtype: float
        """
        return self._score

    @score.setter
    def score(self, score):
        """Sets the score of this Result.

        A numerical score associated with this result indicating the relevance or confidence of this result relative to others in the returned set. Higher MUST be better.  # noqa: E501

        :param score: The score of this Result.
        :type score: float
        """

        self._score = score

    @property
    def score_name(self):
        """Gets the score_name of this Result.

        Name for the score  # noqa: E501

        :return: The score_name of this Result.
        :rtype: str
        """
        return self._score_name

    @score_name.setter
    def score_name(self, score_name):
        """Sets the score_name of this Result.

        Name for the score  # noqa: E501

        :param score_name: The score_name of this Result.
        :type score_name: str
        """

        self._score_name = score_name

    @property
    def score_direction(self):
        """Gets the score_direction of this Result.

        Sorting indicator for the score: one of higher_is_better or lower_is_better  # noqa: E501

        :return: The score_direction of this Result.
        :rtype: str
        """
        return self._score_direction

    @score_direction.setter
    def score_direction(self, score_direction):
        """Sets the score_direction of this Result.

        Sorting indicator for the score: one of higher_is_better or lower_is_better  # noqa: E501

        :param score_direction: The score_direction of this Result.
        :type score_direction: str
        """

        self._score_direction = score_direction

    @property
    def confidence(self):
        """Gets the confidence of this Result.

        Confidence metric for this result, a value between (inclusive)  0.0 (no confidence) and 1.0 (highest confidence)  # noqa: E501

        :return: The confidence of this Result.
        :rtype: float
        """
        return self._confidence

    @confidence.setter
    def confidence(self, confidence):
        """Sets the confidence of this Result.

        Confidence metric for this result, a value between (inclusive)  0.0 (no confidence) and 1.0 (highest confidence)  # noqa: E501

        :param confidence: The confidence of this Result.
        :type confidence: float
        """

        self._confidence = confidence

    @property
    def result_group(self):
        """Gets the result_group of this Result.

        An integer group number for results for use in cases where several results should be grouped together. Also useful to control sorting ascending.  # noqa: E501

        :return: The result_group of this Result.
        :rtype: int
        """
        return self._result_group

    @result_group.setter
    def result_group(self, result_group):
        """Sets the result_group of this Result.

        An integer group number for results for use in cases where several results should be grouped together. Also useful to control sorting ascending.  # noqa: E501

        :param result_group: The result_group of this Result.
        :type result_group: int
        """

        self._result_group = result_group

    @property
    def result_group_similarity_score(self):
        """Gets the result_group_similarity_score of this Result.

        A score that denotes the similarity of this result to other members of the result_group  # noqa: E501

        :return: The result_group_similarity_score of this Result.
        :rtype: float
        """
        return self._result_group_similarity_score

    @result_group_similarity_score.setter
    def result_group_similarity_score(self, result_group_similarity_score):
        """Sets the result_group_similarity_score of this Result.

        A score that denotes the similarity of this result to other members of the result_group  # noqa: E501

        :param result_group_similarity_score: The result_group_similarity_score of this Result.
        :type result_group_similarity_score: float
        """

        self._result_group_similarity_score = result_group_similarity_score

    @property
    def reasoner_id(self):
        """Gets the reasoner_id of this Result.

        Identifier string of the reasoner that provided this result (e.g., ARAX, Robokop, etc.)  # noqa: E501

        :return: The reasoner_id of this Result.
        :rtype: str
        """
        return self._reasoner_id

    @reasoner_id.setter
    def reasoner_id(self, reasoner_id):
        """Sets the reasoner_id of this Result.

        Identifier string of the reasoner that provided this result (e.g., ARAX, Robokop, etc.)  # noqa: E501

        :param reasoner_id: The reasoner_id of this Result.
        :type reasoner_id: str
        """

        self._reasoner_id = reasoner_id


# code/UI/OpenAPI/python-flask-server/KG2/openapi_server/models/analysis.py
# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model_ import Model
from openapi_server.models.attribute import Attribute
from openapi_server.models.edge_binding import EdgeBinding
from openapi_server import util

from openapi_server.models.attribute import Attribute  # noqa: E501
from openapi_server.models.edge_binding import EdgeBinding  # noqa: E501

class Analysis(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, reasoner_id=None, score=None, edge_bindings=None, support_graphs=None, scoring_method=None, attributes=None):  # noqa: E501
        """Analysis - a model defined in OpenAPI

        :param reasoner_id: The reasoner_id of this Analysis.  # noqa: E501
        :type reasoner_id: str
        :param score: The score of this Analysis.  # noqa: E501
        :type score: float
        :param edge_bindings: The edge_bindings of this Analysis.  # noqa: E501
        :type edge_bindings: Dict[str, List[EdgeBinding]]
        :param support_graphs: The support_graphs of this Analysis.  # noqa: E501
        :type support_graphs: List[str]
        :param scoring_method: The scoring_method of this Analysis.  # noqa: E501
        :type scoring_method: str
        :param attributes: The attributes of this Analysis.  # noqa: E501
        :type attributes: List[Attribute]
        """
        self.openapi_types = {
            'reasoner_id': str,
            'score': float,
            'edge_bindings': Dict[str, List[EdgeBinding]],
            'support_graphs': List[str],
            'scoring_method': str,
            'attributes': List[Attribute]
        }

        self.attribute_map = {
            'reasoner_id': 'reasoner_id',
            'score': 'score',
            'edge_bindings': 'edge_bindings',
            'support_graphs': 'support_graphs',
            'scoring_method': 'scoring_method',
            'attributes': 'attributes'
        }

        self._reasoner_id = reasoner_id
        self._score = score
        self._edge_bindings = edge_bindings
        self._support_graphs = support_graphs
        self._scoring_method = scoring_method
        self._attributes = attributes

    @classmethod
    def from_dict(cls, dikt) -> 'Analysis':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Analysis of this Analysis.  # noqa: E501
        :rtype: Analysis
        """
        return util.deserialize_model(dikt, cls)

    @property
    def reasoner_id(self):
        """Gets the reasoner_id of this Analysis.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :return: The reasoner_id of this Analysis.
        :rtype: str
        """
        return self._reasoner_id

    @reasoner_id.setter
    def reasoner_id(self, reasoner_id):
        """Sets the reasoner_id of this Analysis.

        A Compact URI, consisting of a prefix and a reference separated by a colon, such as UniProtKB:P00738. Via an external context definition, the CURIE prefix and colon may be replaced by a URI prefix, such as http://identifiers.org/uniprot/, to form a full URI.  # noqa: E501

        :param reasoner_id: The reasoner_id of this Analysis.
        :type reasoner_id: str
        """
        if reasoner_id is None:
            raise ValueError("Invalid value for `reasoner_id`, must not be `None`")  # noqa: E501

        self._reasoner_id = reasoner_id

    @property
    def score(self):
        """Gets the score of this Analysis.

        A numerical score associated with this result indicating the relevance or confidence of this result relative to others in the returned set. Higher MUST be better.  # noqa: E501

        :return: The score of this Analysis.
        :rtype: float
        """
        return self._score

    @score.setter
    def score(self, score):
        """Sets the score of this Analysis.

        A numerical score associated with this result indicating the relevance or confidence of this result relative to others in the returned set. Higher MUST be better.  # noqa: E501

        :param score: The score of this Analysis.
        :type score: float
        """

        self._score = score

    @property
    def edge_bindings(self):
        """Gets the edge_bindings of this Analysis.

        The dictionary of input Query Graph to Knowledge Graph edge bindings where the dictionary keys are the key identifiers of the Query Graph edges and the associated values of those keys are instances of EdgeBinding schema type (see below). This value is an array of EdgeBindings since a given query edge may resolve to multiple Knowledge Graph Edges.  # noqa: E501

        :return: The edge_bindings of this Analysis.
        :rtype: Dict[str, List[EdgeBinding]]
        """
        return self._edge_bindings

    @edge_bindings.setter
    def edge_bindings(self, edge_bindings):
        """Sets the edge_bindings of this Analysis.

        The dictionary of input Query Graph to Knowledge Graph edge bindings where the dictionary keys are the key identifiers of the Query Graph edges and the associated values of those keys are instances of EdgeBinding schema type (see below). This value is an array of EdgeBindings since a given query edge may resolve to multiple Knowledge Graph Edges.  # noqa: E501

        :param edge_bindings: The edge_bindings of this Analysis.
        :type edge_bindings: Dict[str, List[EdgeBinding]]
        """
        if edge_bindings is None:
            raise ValueError("Invalid value for `edge_bindings`, must not be `None`")  # noqa: E501

        self._edge_bindings = edge_bindings

    @property
    def support_graphs(self):
        """Gets the support_graphs of this Analysis.

        This is a list of references to Auxiliary Graph instances that supported the analysis of a Result as performed by the reasoning service. Each item in the list is the key of a single Auxiliary Graph.  # noqa: E501

        :return: The support_graphs of this Analysis.
        :rtype: List[str]
        """
        return self._support_graphs

    @support_graphs.setter
    def support_graphs(self, support_graphs):
        """Sets the support_graphs of this Analysis.

        This is a list of references to Auxiliary Graph instances that supported the analysis of a Result as performed by the reasoning service. Each item in the list is the key of a single Auxiliary Graph.  # noqa: E501

        :param support_graphs: The support_graphs of this Analysis.
        :type support_graphs: List[str]
        """

        self._support_graphs = support_graphs

    @property
    def scoring_method(self):
        """Gets the scoring_method of this Analysis.

        An identifier and link to an explanation for the method used to generate the score  # noqa: E501

        :return: The scoring_method of this Analysis.
        :rtype: str
        """
        return self._scoring_method

    @scoring_method.setter
    def scoring_method(self, scoring_method):
        """Sets the scoring_method of this Analysis.

        An identifier and link to an explanation for the method used to generate the score  # noqa: E501

        :param scoring_method: The scoring_method of this Analysis.
        :type scoring_method: str
        """

        self._scoring_method = scoring_method

    @property
    def attributes(self):
        """Gets the attributes of this Analysis.

        The attributes of this particular Analysis.  # noqa: E501

        :return: The attributes of this Analysis.
        :rtype: List[Attribute]
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this Analysis.

        The attributes of this particular Analysis.  # noqa: E501

        :param attributes: The attributes of this Analysis.
        :type attributes: List[Attribute]
        """

        self._attributes = attributes
