#!/usr/bin/env python3
"""WARNING: DO NOT MODIFY THIS FILE
IT IS AUTOMATICALLY GENERATED BY scigraph.py
AND WILL BE OVERWRITTEN
Swagger Version: 2.0, API Version: 1.0.1
generated for http://localhost:9000/scigraph/swagger.json
by scigraph.py
"""
import re
import copy
import inspect
import builtins
from urllib.parse import parse_qs
import requests
from ast import literal_eval
from json import dumps
from urllib import parse

BASEPATH = 'https://scicrunch.org/api/1/scigraph'

exten_mapping = {'application/graphml+xml': 'graphml+xml', 'application/graphson': 'graphson', 'application/javascript': 'javascript', 'application/json': 'json', 'application/xgmml': 'xgmml', 'application/xml': 'xml', 'image/jpeg': 'jpeg', 'image/png': 'png', 'text/csv': 'csv', 'text/gml': 'gml', 'text/html': 'html', 'text/plain': 'plain', 'text/plain; charset=utf-8': 'plain; charset=utf-8', 'text/tab-separated-values': 'tab-separated-values'}

class restService:
    """ Base class for SciGraph rest services. """

    _api_key = None

    def __init__(self, cache=False, safe_cache=False, key=None):
        self._session = requests.Session()
        adapter = requests.adapters.HTTPAdapter(pool_connections=1000, pool_maxsize=1000)
        self._session.mount('http://', adapter)

        if cache:
            #print('WARNING: cache enabled, if you mutate the contents of return values you will mutate the cache!')
            self._cache = dict()
            if safe_cache:
                self._get = self._safe_cache_get
            else:
                self._get = self._cache_get

        else:
            self._get = self._normal_get

        if key is not None:
            self.api_key = key
            raise DeprecationWarning('this way of passing keys will be deprecated soon')

    @property
    def api_key(self):
        return self._api_key

    @api_key.setter
    def api_key(self, value):
        self._api_key = value

    def __del__(self):
        self._session.close()

    def _safe_url(self, url):
        return url.replace(self.api_key, '[secure]') if self.api_key else url

    @property
    def _last_url(self):
        return self._safe_url(self.__last_url)

    def _normal_get(self, method, url, params=None, output=None):
        s = self._session
        if self.api_key is not None:
            params['key'] = self.api_key
        if method == 'POST':
            req = requests.Request(method=method, url=url, data=params)
        else:
            req = requests.Request(method=method, url=url, params=params)
        if output:
            req.headers['Accept'] = output
        prep = req.prepare()
        if self._verbose: print(self._safe_url(prep.url))
        try:
            resp = s.send(prep)
            self.__last_url = resp.url
        except requests.exceptions.ConnectionError as e:
            host_port = prep.url.split(prep.path_url)[0]
            raise ConnectionError(f'Could not connect to {host_port}. '
                                  'Are SciGraph services running?') from e
        if resp.status_code == 401:
            raise ConnectionError(f'{resp.reason}. '
                                  f'Did you set {self.__class__.__name__}.api_key'
                                  ' = my_api_key?')
        elif not resp.ok:
            return None
        elif resp.headers['content-type'] == 'application/json':
            return resp.json()
        elif resp.headers['content-type'].startswith('text/plain'):
            return resp.text
        else:
            return resp

    def _cache_get(self, method, url, params=None, output=None):
        if params:
            pkey = '?' + '&'.join(['%s=%s' % (k,v) for k,v in sorted(params.items()) if v is not None])
        else:
            pkey = ''
        key = url + pkey + ' ' + method + ' ' + str(output)
        if  key in self._cache:
            if self._verbose:
                print('cache hit', key)
            self.__last_url, resp = self._cache[key]
        else:
            resp = self._normal_get(method, url, params, output)
            self._cache[key] = self.__last_url, resp

        return resp

    def _safe_cache_get(self, *args, **kwargs):
        """ If cached values might be used in a context where they
            could be mutated, then safe_cache = True should be set
            and this wrapper will protect the output """
        return copy.deepcopy(self._cache_get(*args, **kwargs))  # prevent mutation of the cache

    def _make_rest(self, default=None, **kwargs):
        kwargs = {k:v for k, v in kwargs.items() if v}
        param_rest = '&'.join(['%s={%s}' % (arg, arg) for arg in kwargs if arg != default])
        param_rest = param_rest if param_rest else ''
        return param_rest


class Analyzer(restService):
    """ Analysis services """

    def __init__(self, basePath=None, verbose=False, cache=False, safe_cache=False, key=None):
        if basePath is None:
            basePath = BASEPATH
        self._basePath = basePath
        self._verbose = verbose
        super().__init__(cache=cache, safe_cache=safe_cache, key=key)

    def enrich(self, sample, ontologyClass, path, callback=None, output='application/json'):
        """ Class Enrichment Service from: /analyzer/enrichment

            Arguments:
            sample: A list of CURIEs for nodes whose attributes are to be tested for
                    enrichment. For example, a list of genes.
            ontologyClass: CURIE for parent ontology class for the attribute to be tested.
                           For example, GO biological process
            path: A path expression that connects sample nodes to attribute class nodes
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
        """

        kwargs = {'sample': sample, 'ontologyClass': ontologyClass, 'path': path, 'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/analyzer/enrichment').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def enrichPost(self, output='application/json'):
        """  from: /analyzer/enrichment

            Arguments:

            outputs:
                application/json
        """

        kwargs = {}
        # type caste not needed
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/analyzer/enrichment').format(**kwargs)
        requests_params = kwargs
        output = self._get('POST', url, requests_params, output)
        return output if output else []


class Annotations(restService):
    """ Annotation services """

    def __init__(self, basePath=None, verbose=False, cache=False, safe_cache=False, key=None):
        if basePath is None:
            basePath = BASEPATH
        self._basePath = basePath
        self._verbose = verbose
        super().__init__(cache=cache, safe_cache=safe_cache, key=key)

    def annotate(self, content, includeCat=None, excludeCat=None, minLength=None, longestOnly=None, includeAbbrev=None, includeAcronym=None, includeNumbers=None, output='text/plain; charset=utf-8'):
        """ Annotate text from: /annotations

            Arguments:
            content: The content to annotate
            includeCat: A set of categories to include
            excludeCat: A set of categories to exclude
            minLength: The minimum number of characters in annotated entities
            longestOnly: Should only the longest entity be returned for an overlapping group
            includeAbbrev: Should abbreviations be included
            includeAcronym: Should acronyms be included
            includeNumbers: Should numbers be included
            outputs:
                text/plain; charset=utf-8
        """

        kwargs = {'content': content, 'includeCat': includeCat, 'excludeCat': excludeCat, 'minLength': minLength, 'longestOnly': longestOnly, 'includeAbbrev': includeAbbrev, 'includeAcronym': includeAcronym, 'includeNumbers': includeNumbers}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/annotations').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def annotatePost(self, content, includeCat=None, excludeCat=None, minLength=None, longestOnly=None, includeAbbrev=None, includeAcronym=None, includeNumbers=None, ignoreTag=None, stylesheet=None, scripts=None, targetId=None, targetClass=None, output='application/json'):
        """ Annotate text from: /annotations

            Arguments:
            content: The content to annotate
            includeCat: A set of categories to include
            excludeCat: A set of categories to exclude
            minLength: The minimum number of characters in annotated entities
            longestOnly: Should only the longest entity be returned for an overlapping group
            includeAbbrev: Should abbreviations be included
            includeAcronym: Should acronyms be included
            includeNumbers: Should numbers be included
            ignoreTag: HTML tags that should not be annotated
            stylesheet: CSS stylesheets to add to the HEAD
            scripts: JavaScripts that should to add to the HEAD
            targetId: A set of element IDs to annotate
            targetClass: A set of CSS class names to annotate
            outputs:
                application/json
        """

        kwargs = {'content': content, 'includeCat': includeCat, 'excludeCat': excludeCat, 'minLength': minLength, 'longestOnly': longestOnly, 'includeAbbrev': includeAbbrev, 'includeAcronym': includeAcronym, 'includeNumbers': includeNumbers, 'ignoreTag': ignoreTag, 'stylesheet': stylesheet, 'scripts': scripts, 'targetId': targetId, 'targetClass': targetClass}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/annotations').format(**kwargs)
        requests_params = kwargs
        output = self._get('POST', url, requests_params, output)
        return output if output else None

    def getEntitiesAndContent(self, content, includeCat=None, excludeCat=None, minLength=None, longestOnly=None, includeAbbrev=None, includeAcronym=None, includeNumbers=None, output='application/json'):
        """ Get embedded annotations as well as a separate list from: /annotations/complete

            Arguments:
            content: The content to annotate
            includeCat: A set of categories to include
            excludeCat: A set of categories to exclude
            minLength: The minimum number of characters in annotated entities
            longestOnly: Should only the longest entity be returned for an overlapping group
            includeAbbrev: Should abbreviations be included
            includeAcronym: Should acronyms be included
            includeNumbers: Should numbers be included
            outputs:
                application/json
        """

        kwargs = {'content': content, 'includeCat': includeCat, 'excludeCat': excludeCat, 'minLength': minLength, 'longestOnly': longestOnly, 'includeAbbrev': includeAbbrev, 'includeAcronym': includeAcronym, 'includeNumbers': includeNumbers}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/annotations/complete').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def postEntitiesAndContent(self, content, includeCat=None, excludeCat=None, minLength=None, longestOnly=None, includeAbbrev=None, includeAcronym=None, includeNumbers=None, output='application/json'):
        """ Get embedded annotations as well as a separate list from: /annotations/complete

            Arguments:
            content: The content to annotate
            includeCat: A set of categories to include
            excludeCat: A set of categories to exclude
            minLength: The minimum number of characters in annotated entities
            longestOnly: Should only the longest entity be returned for an overlapping group
            includeAbbrev: Should abbreviations be included
            includeAcronym: Should acronyms be included
            includeNumbers: Should numbers be included
            outputs:
                application/json
        """

        kwargs = {'content': content, 'includeCat': includeCat, 'excludeCat': excludeCat, 'minLength': minLength, 'longestOnly': longestOnly, 'includeAbbrev': includeAbbrev, 'includeAcronym': includeAcronym, 'includeNumbers': includeNumbers}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/annotations/complete').format(**kwargs)
        requests_params = kwargs
        output = self._get('POST', url, requests_params, output)
        return output if output else []

    def getEntities(self, content, includeCat=None, excludeCat=None, minLength=None, longestOnly=None, includeAbbrev=None, includeAcronym=None, includeNumbers=None, output='application/json'):
        """ Get entities from text from: /annotations/entities

            Arguments:
            content: The content to annotate
            includeCat: A set of categories to include
            excludeCat: A set of categories to exclude
            minLength: The minimum number of characters in annotated entities
            longestOnly: Should only the longest entity be returned for an overlapping group
            includeAbbrev: Should abbreviations be included
            includeAcronym: Should acronyms be included
            includeNumbers: Should numbers be included
            outputs:
                application/json
        """

        kwargs = {'content': content, 'includeCat': includeCat, 'excludeCat': excludeCat, 'minLength': minLength, 'longestOnly': longestOnly, 'includeAbbrev': includeAbbrev, 'includeAcronym': includeAcronym, 'includeNumbers': includeNumbers}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/annotations/entities').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def postEntities(self, content, includeCat=None, excludeCat=None, minLength=None, longestOnly=None, includeAbbrev=None, includeAcronym=None, includeNumbers=None, output='application/json'):
        """ Get entities from text from: /annotations/entities

            Arguments:
            content: The content to annotate
            includeCat: A set of categories to include
            excludeCat: A set of categories to exclude
            minLength: The minimum number of characters in annotated entities
            longestOnly: Should only the longest entity be returned for an overlapping group
            includeAbbrev: Should abbreviations be included
            includeAcronym: Should acronyms be included
            includeNumbers: Should numbers be included
            outputs:
                application/json
        """

        kwargs = {'content': content, 'includeCat': includeCat, 'excludeCat': excludeCat, 'minLength': minLength, 'longestOnly': longestOnly, 'includeAbbrev': includeAbbrev, 'includeAcronym': includeAcronym, 'includeNumbers': includeNumbers}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/annotations/entities').format(**kwargs)
        requests_params = kwargs
        output = self._get('POST', url, requests_params, output)
        return output if output else []

    def annotateUrl(self, url, includeCat=None, excludeCat=None, minLength=None, longestOnly=None, includeAbbrev=None, includeAcronym=None, includeNumbers=None, ignoreTag=None, stylesheet=None, scripts=None, targetId=None, targetClass=None, output='text/html'):
        """ Annotate a URL from: /annotations/url

            Arguments:
            url:
            includeCat: A set of categories to include
            excludeCat: A set of categories to exclude
            minLength: The minimum number of characters in annotated entities
            longestOnly: Should only the longest entity be returned for an overlapping group
            includeAbbrev: Should abbreviations be included
            includeAcronym: Should acronyms be included
            includeNumbers: Should numbers be included
            ignoreTag: HTML tags that should not be annotated
            stylesheet: CSS stylesheets to add to the HEAD
            scripts: JavaScripts that should to add to the HEAD
            targetId: A set of element IDs to annotate
            targetClass: A set of CSS class names to annotate
            outputs:
                text/html
        """

        kwargs = {'url': url, 'includeCat': includeCat, 'excludeCat': excludeCat, 'minLength': minLength, 'longestOnly': longestOnly, 'includeAbbrev': includeAbbrev, 'includeAcronym': includeAcronym, 'includeNumbers': includeNumbers, 'ignoreTag': ignoreTag, 'stylesheet': stylesheet, 'scripts': scripts, 'targetId': targetId, 'targetClass': targetClass}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/annotations/url').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None


class CypherBase(restService):
    """ Cypher utility services """

    def __init__(self, basePath=None, verbose=False, cache=False, safe_cache=False, key=None):
        if basePath is None:
            basePath = BASEPATH
        self._basePath = basePath
        self._verbose = verbose
        super().__init__(cache=cache, safe_cache=safe_cache, key=key)

    def getCuries(self, callback=None, output='application/json'):
        """ Get the curie map from: /cypher/curies

            Arguments:
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
        """

        kwargs = {'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/cypher/curies').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else {}

    def execute(self, cypherQuery, limit, output='text/plain'):
        """ Execute an arbitrary Cypher query. from: /cypher/execute

            Arguments:
            cypherQuery: The cypher query to execute
            limit: Limit
            outputs:
                text/plain
                application/json
        """

        kwargs = {'cypherQuery': cypherQuery, 'limit': limit}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/cypher/execute').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def resolve(self, cypherQuery, output='text/plain'):
        """ Cypher query resolver from: /cypher/resolve

            Arguments:
            cypherQuery: The cypher query to resolve
            outputs:
                text/plain
        """

        kwargs = {'cypherQuery': cypherQuery}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/cypher/resolve').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None


class Cypher(CypherBase):
    @staticmethod
    def fix_quotes(string, s1=':["', s2='"],'):
        out = []
        def subsplit(sstr, s=s2):
            #print(s)
            if s == '",' and sstr.endswith('"}'):  # special case for end of record
                s = '"}'
            if s:
                string, *rest = sstr.rsplit(s, 1)
            else:
                string = sstr
                rest = '',

            if rest:
                #print('>>>>', string)
                #print('>>>>', rest)
                r, = rest
                if s == '"],':
                    fixed_string = Cypher.fix_quotes(string, '","', '') + s + r
                else:
                    fixed_string = string.replace('"', r'\"') + s + r

                return fixed_string

        for sub1 in string.split(s1):
            ss = subsplit(sub1)
            if ss is None:
                if s1 == ':["':
                    out.append(Cypher.fix_quotes(sub1, ':"', '",'))
                else:
                    out.append(sub1)
            else:
                out.append(ss)

        return s1.join(out)

    def fix_cypher(self, record):
        rep = re.sub(r'({|, )(\S+)(: "|: \[)', r'\1"\2"\3',
                     self.fix_quotes(record.strip()).
                     split(']', 1)[1] .
                     replace(':"', ': "') .
                     replace(':[', ': [') .
                     replace('",', '", ') .
                     replace('"],', '"], ') .
                     replace('\n', '\\n') .
                     replace('xml:lang="en"', r'xml:lang=\"en\"')
                    )
        try:
            value = {self.qname(k):v for k, v in literal_eval(rep).items()}
        except (ValueError, SyntaxError) as e:
            print(repr(record))
            print(repr(rep))
            raise e

        return value

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._setCuries()

    def _setCuries(self):
        try:
            self._curies = self.getCuries()
        except ConnectionError:
            self._curies = {}

        self._inv = {v:k for k, v in self._curies.items()}

    @property
    def api_key(self):
        # note that using properties means that
        # if you want to use properties at all in
        # a subClass hierarchy you have to reimplement
        # them every single time to be aware if the
        # parent class value chanes
        if isinstance(restService.api_key, str):
            return restService.api_key
        else:
            return self._api_key

    @api_key.setter
    def api_key(self, value):
        old_key = self.api_key
        self._api_key = value
        if old_key is None and value is not None:
            self._setCuries()

    def qname(self, iri):
        for prefix, curie in self._inv.items():
            if iri.startswith(prefix):
                return iri.replace(prefix, curie + ':')
        else:
            return iri

    def execute(self, query, limit, output='text/plain'):
        if output == 'text/plain':
            out = super().execute(query, limit, output)
            rows = []
            if out:
                for raw in out.split('|')[3:-1]:
                    record = raw.strip()
                    if record:
                        d = self.fix_cypher(record)
                        rows.append(d)

            return rows

        else:
            return super().execute(query, limit, output)


class DynamicBase(restService):
    """ Dynamic Cypher resources """

    def __init__(self, basePath=None, verbose=False, cache=False, safe_cache=False, key=None):
        if basePath is None:
            basePath = BASEPATH
        self._basePath = basePath
        self._verbose = verbose
        super().__init__(cache=cache, safe_cache=safe_cache, key=key)

    def multiquery_relationship_id(self, relationship, id, output='application/json'):
        """ actually include parent class properties like owl is supposed to ... from: /dynamic/multiquery/{relationship}/{id}

            Arguments:
            relationship: ontology id of the relationship to traverse
            id: ontology id of the starting point

            Query:
            MATCH path = (start:Class{iri: "${id}"})
            -[:subClassOf|${relationship}*]
            ->(end)
            RETURN path
            
            /* // apparently broken
            MATCH (start:Class{iri: "${id}"})
            -[:subClassOf*0..20]
            -(intermediate)
            -[${relationship}*]
            ->(end)
            RETURN end
            */
            /*
            MATCH (start:Class{iri: "${id}"})
            -[:subClassOf*]
            ->(superClass)
            WITH superClass
            MATCH (superClass)-[:${relationship}*]->(superpart)
            RETURN superpart
            UNION
            MATCH (start:Class{iri: "${id}"})
            -[:${relationship}*]->(superpart)
            RETURN superpart
            */

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if id and id.startswith('http:'):
            id = parse.quote(id, safe='')
        kwargs = {'relationship': relationship, 'id': id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('id', **kwargs)
        url = self._basePath + ('/dynamic/multiquery/{relationship}/{id}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'id'}
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def neurons_connectedRegions(self, start_id=None, target_predicate=None, output='application/json'):
        """ Get connected anatomical regions by starting location and target relationship from: /dynamic/neurons/connectedRegions

            Arguments:
            start_id: The starting location (eg UBERON:0001759)
            target_predicate: The predicate for the type of connectivity (eg
                              ilxtr:hasPresynapticTerminalsIn)

            Query:
            MATCH (blank)-
            [entrytype:ilxtr:hasSomaLocatedIn|ilxtr:hasAxonLocatedIn|ilxtr:hasDendriteLocatedIn|ilxtr:hasPresynapticTerminalsIn]
            ->(location:Class{iri: '${start_id}'})
            WITH entrytype, blank
            MATCH (phenotype)<-[:${target_predicate}]-(blank)
            // WHERE NOT (phenotype.iri =~ ".*_:.*")
            RETURN phenotype

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        kwargs = {'start_id': start_id, 'target_predicate': target_predicate}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/neurons/connectedRegions').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def neurons_connectivity(self, start_id=None, output='application/json'):
        """ Get connected anatomical regions by neuron type from: /dynamic/neurons/connectivity

            Arguments:
            start_id: The starting location (eg UBERON:0001759)

            Query:
            MATCH (blank)-
            [entrytype:ilxtr:hasSomaLocatedIn|ilxtr:hasAxonLocatedIn|ilxtr:hasDendriteLocatedIn|ilxtr:hasPresynapticTerminalsIn]
            ->(location:Class{iri: '${start_id}'})
            WITH location, entrytype, blank
            MATCH (phenotype)<-[predicate]-(blank)<-[:equivalentClass]-(neuron)
            WHERE NOT (phenotype.iri =~ ".*_:.*")
            // RETURN phenotype, (phenotype)-[predicate]-(neuron) as e
            // WITH location, predicate, phenotype, neuron
            RETURN location, entrytype, neuron, predicate, phenotype

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        kwargs = {'start_id': start_id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/neurons/connectivity').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_artifactLabels_artifact_id(self, artifact_id, output='application/json'):
        """ Get the graph of all parcellation labels for a single artifact WARNING this can return no results from: /dynamic/prod/sparc/artifactLabels/{artifact-id}

            Arguments:
            artifact_id: ontology id of the parcellation artifact

            Query:
            MATCH path = (label)
            -[:subClassOf]->(root)
            -[:ilxtr:isDefinedBy]->(a)<-[:subClassOf*0..2]
            -(artifact:Class{iri: "${artifact-id}"})
            WHERE label.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if artifact_id and artifact_id.startswith('http:'):
            artifact_id = parse.quote(artifact_id, safe='')
        kwargs = {'artifact_id': artifact_id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/artifactLabels/{artifact_id}').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_artifactRoots_artifact_id(self, artifact_id, output='application/json'):
        """ Get the graph of all parcellation label roots for a single artifact WARNING this can return no results from: /dynamic/prod/sparc/artifactRoots/{artifact-id}

            Arguments:
            artifact_id: ontology id of the parcellation artifact

            Query:
            MATCH path = (root)
            -[:ilxtr:isDefinedBy]->(a)<-[:subClassOf*0..2]
            -(artifact:Class{iri: "${artifact-id}"})
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if artifact_id and artifact_id.startswith('http:'):
            artifact_id = parse.quote(artifact_id, safe='')
        kwargs = {'artifact_id': artifact_id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/artifactRoots/{artifact_id}').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_organList(self, output='application/json'):
        """ Get the list of all FMA organ identifiers relevant to SPARC from: /dynamic/prod/sparc/organList

            Arguments:


            Query:
            MATCH (n)
            WHERE n.iri IN [
                    "http://purl.org/sig/ont/fma/fma7195",
                    "http://purl.org/sig/ont/fma/fma7088",
                    "http://purl.org/sig/ont/fma/fma7197",
                    "http://purl.org/sig/ont/fma/fma7198",
                    "http://purl.org/sig/ont/fma/fma7203",
                    "http://purl.org/sig/ont/fma/fma7148",
                    "http://purl.org/sig/ont/fma/fma7196",
                    "http://purl.org/sig/ont/fma/fma14543",
                    "http://purl.org/sig/ont/fma/fma7201",
                    "http://purl.org/sig/ont/fma/fma7200",
                    "http://purl.org/sig/ont/fma/fma7199",
                    "http://purl.org/sig/ont/fma/fma15900",
                    "http://purl.org/sig/ont/fma/fma45659",
                    "http://purl.org/sig/ont/fma/fma7157",
                    "http://purl.org/sig/ont/fma/fma9903",
                    "http://purl.org/sig/ont/fma/fma9906",
                    "http://purl.org/sig/ont/fma/fma7647",
                    "http://purl.org/sig/ont/fma/fma50801"]
            RETURN n

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        kwargs = {}
        # type caste not needed
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/organList').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_organParts_id(self, id, output='application/json'):
        """ Get the parts list for an organ including nerves and blood vessles from: /dynamic/prod/sparc/organParts/{id}

            Arguments:
            id: ontology id of the organ

            Query:
            // NOTE: continuous with seems like it is what we want, but it causes
            // MASSIVE memory usage in creatTree :/
            // all parts of and directly connected to organ or parts of organ
            MATCH path = (start:Class{iri: "${id}"})
            -[:fma:regional_part|fma:constitutional_part|fma:related_part*0..20]->(part)
            -[:fma:arterial_supply|fma:nerve_supply|fma:venous_drainage|fma:continuous_with*0..1]->(sup)
            -[:fma:constitutional_part|fma:branch_of|fma:tributary_of|fma:branch*0..1]->(a_bit_more)
            RETURN path
            UNION
            // return the major artery for any arteries supplying the organ directly
            MATCH path = (start:Class{iri: "${id}"})
            -[:fma:arterial_supply|fma:venous_drainage]->(vessel)
            -[:fma:branch_of|fma:tributary_of]->(more_vessel)
            -[:fma:branch_of|fma:tributary_of|fma:regional_part_of]->(even_more_vessel)
            RETURN path
            //
            //
            //
            //
            //
            //
            /*
            MATCH path = (start:Class{iri: "${id}"})
            -[:fma:regional_part|fma:constitutional_part|fma:related_part*0..20]->(part)
            WHERE NOT (part)-[:fma:regional_part|fma:constitutional_part|fma:related_part]->()
            RETURN path
            UNION
            MATCH path = (start:Class{iri: "${id}"})
            -[:fma:regional_part|fma:constitutional_part|fma:related_part*0..20]->(part)
            -[:fma:connected_to*1]->(thing)
            RETURN path
            UNION
            MATCH ppath = (start:Class{iri: "${id}"})
            -[:fma:regional_part|fma:constitutional_part|fma:related_part*0..20]->(part)
            // can't exclude here otherwise we miss nerves of the containing parts
            // WHERE NOT (part)-[:fma:regional_part|fma:constitutional_part|fma:related_part]->()
            WITH start, ppath, part
            MATCH path = (part)
            -[:fma:nerve_supply*1]->(nerve)
            -[:fma:branch_of*0..20]->(more_nerve)
            WHERE NOT (more_nerve)-[:fma:branch_of]->()
            // WHERE more_nerve <> part
            RETURN path
            UNION
            MATCH ppath = (start:Class{iri: "${id}"})
            -[:fma:regional_part|fma:constitutional_part|fma:related_part*0..20]->(part)
            // can't exclude here otherwise we miss nerves of the containing parts
            // WHERE NOT (part)-[:fma:regional_part|fma:constitutional_part|fma:related_part]->()
            WITH start, ppath, part
            MATCH path = (part)
            -[:fma:arterial_supply*1]->(artery)
            RETURN path
            UNION
            MATCH path = (start:Class{iri: "${id}"})
            -[:fma:arterial_supply]->(artery)
            -[:fma:branch_of]->(more_artery)
            RETURN path
            // UNION
            // MATCH ppath = (start:Class{iri: "${id}"})
            // -[:fma:regional_part|fma:constitutional_part|fma:related_part*0..20]->(part)
            // -[:fma:arterial_supply*1]->(artery)
            // WITH start, ppath, artery
            // MATCH path = (artery)
            // -[:fma:branch_of*1]->(more_artery)
            // WHERE NOT more_artery IN nodes(ppath)
            
            // works but slow as fuck
            // WHERE NOT (start)
            // -[:fma:regional_part|fma:constitutional_part|fma:related_part*0..20]->(more_artery)
            
            // WHERE NOT more_artery IN nodes(ppath)
            // WHERE NOT more_artery IN nodes(path)[0..-2]
            // WHERE more_artery <> part AND more_artery <> artery
            // WHERE length([n IN nodes(path) WHERE more_artery = n]) < 2
            // WHERE more_artery IS NOT NULL
            // WHERE NONE (p in collect(part) WHERE more_artery.id = p.id)
            // WHERE NOT more_artery IN collect(part)
            // RETURN path
            */

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if id and id.startswith('http:'):
            id = parse.quote(id, safe='')
        kwargs = {'id': id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('id', **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/organParts/{id}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'id'}
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_parcellationArtifacts(self, output='application/json'):
        """ Get the graph of all parcellation artifacts for all species from: /dynamic/prod/sparc/parcellationArtifacts

            Arguments:


            Query:
            MATCH path = (artifact)
            -[:subClassOf*0..2]->(parent)
            -[:ilxtr:isDefinedInTaxon]->(species)
            WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        kwargs = {}
        # type caste not needed
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/parcellationArtifacts').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_parcellationArtifacts_species_id(self, species_id, output='application/json'):
        """ Get the graph of all parcellation artifacts for a single species from: /dynamic/prod/sparc/parcellationArtifacts/{species-id}

            Arguments:
            species_id: ontology id of the species

            Query:
            MATCH (parent)
            -[:ilxtr:isDefinedInTaxon]->(species:Class{iri: "${species-id}"})
            WITH parent
            MATCH path = (artifact)
            -[:subClassOf*0..2]->(parent)
            WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if species_id and species_id.startswith('http:'):
            species_id = parse.quote(species_id, safe='')
        kwargs = {'species_id': species_id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/parcellationArtifacts/{species_id}').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_parcellationGraph(self, output='application/json'):
        """ Get the graph of all parcellation labels for all species from: /dynamic/prod/sparc/parcellationGraph

            Arguments:


            Query:
            MATCH path = (artifact)
            -[:subClassOf*0..2]->(parent)
            -[:ilxtr:isDefinedInTaxon]->(species)
            WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            return path
            UNION
            MATCH path = (maybe)
            -[relation*0..1]-(label)
            -[:subClassOf]->(root)
            -[:ilxtr:isDefinedBy]->(artifact)
            -[:subClassOf*0..2]->(parent)
            -[:ilxtr:isDefinedInTaxon]->(species)
            WHERE NONE (r in relation WHERE type(r) IN ["isDefinedBy", "subClassOf", "filler"])
                  AND NOT (label.iri =~ ".*_:.*") AND NOT (maybe.iri =~ ".*_:.*")
                  AND label.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        kwargs = {}
        # type caste not needed
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/parcellationGraph').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_parcellationRoots(self, output='application/json'):
        """ Get the graph of all parcellation label roots for all species from: /dynamic/prod/sparc/parcellationRoots

            Arguments:


            Query:
            MATCH path = (artifact)
            -[:subClassOf*0..2]->(parent)
            -[:ilxtr:isDefinedInTaxon]->(species)
            WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            return path
            UNION
            MATCH path = (root)
            -[:ilxtr:isDefinedBy]->(artifact)
            -[:subClassOf*0..2]->(parent)
            -[:ilxtr:isDefinedInTaxon]->(species)
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        kwargs = {}
        # type caste not needed
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/parcellationRoots').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_parcellationRoots_species_id(self, species_id, output='application/json'):
        """ Get the graph of all parcellation label roots for a single species from: /dynamic/prod/sparc/parcellationRoots/{species-id}

            Arguments:
            species_id: ontology id of the species

            Query:
            MATCH (parent)
            -[:ilxtr:isDefinedInTaxon]->(species:Class{iri: "${species-id}"})
            WITH parent
            MATCH path = (artifact)
            -[:subClassOf*0..2]->(parent)
            WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            return path
            UNION
            MATCH (parent)
            -[:ilxtr:isDefinedInTaxon]->(species:Class{iri: "${species-id}"})
            WITH parent
            MATCH path = (root)
            -[:ilxtr:isDefinedBy]->(artifact)
            -[:subClassOf*0..2]->(parent)
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if species_id and species_id.startswith('http:'):
            species_id = parse.quote(species_id, safe='')
        kwargs = {'species_id': species_id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/parcellationRoots/{species_id}').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_parcellationRoots_species_id_region_id(self, species_id, region_id, output='application/json'):
        """ Get the graph of all parcellation label roots for a single species and anatomical region from: /dynamic/prod/sparc/parcellationRoots/{species-id}/{region-id}

            Arguments:
            species_id: ontology id of the species
            region_id: ontology id of the anatomical region

            Query:
            MATCH
            (region:Class{iri: "${region-id}"})
            <-[:ilxtr:isDefinedInRegion]-
            (parent)
            -[:ilxtr:isDefinedInTaxon]->
            (species:Class{iri: "${species-id}"})
            WITH parent
            MATCH path = (artifact)
            -[:subClassOf*0..2]->(parent)
            WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            RETURN path
            UNION
            MATCH
            (region:Class{iri: "${region-id}"})
            <-[:ilxtr:isDefinedInRegion]-
            (parent)
            -[:ilxtr:isDefinedInTaxon]->
            (species:Class{iri: "${species-id}"})
            WITH parent
            MATCH path = (root)
            -[:ilxtr:isDefinedBy]->(artifact)
            -[:subClassOf*0..2]->(parent)
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if species_id and species_id.startswith('http:'):
            species_id = parse.quote(species_id, safe='')
        if region_id and region_id.startswith('http:'):
            region_id = parse.quote(region_id, safe='')
        kwargs = {'species_id': species_id, 'region_id': region_id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/parcellationRoots/{species_id}/{region_id}').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_parcellationRootsFMA_species_id_fma_id(self, species_id, fma_id, output='application/json'):
        """ Get the graph of all parcellation label roots for a single species and anatomical region from: /dynamic/prod/sparc/parcellationRootsFMA/{species-id}/{fma-id}

            Arguments:
            species_id: ontology id of the species
            fma_id: ontology id of the anatomical region

            Query:
            MATCH (fma:Class{iri: "${fma-id}"})
            WITH "FMA:" + toString(fma.`http://purl.org/sig/ont/fma/FMAID`) AS curie
            MATCH (region)
            -[:subClassOf*]->(start:Class{iri: "http://purl.obolibrary.org/obo/UBERON_0001062"})
            WHERE any(x IN
                      region.`http://www.geneontology.org/formats/oboInOwl#hasDbXref`
                      WHERE x =~ curie)
            WITH region
            MATCH
            (region)
            <-[:ilxtr:isDefinedInRegion]-
            (parent)
            -[:ilxtr:isDefinedInTaxon]->
            (species:Class{iri: "${species-id}"})
            WITH parent
            MATCH path = (artifact)
            -[:subClassOf*0..2]->(parent)
            WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            RETURN path
            UNION
            MATCH (fma:Class{iri: "${fma-id}"})
            WITH "FMA:" + toString(fma.`http://purl.org/sig/ont/fma/FMAID`) AS curie
            MATCH (region)
            -[:subClassOf*]->(start:Class{iri: "http://purl.obolibrary.org/obo/UBERON_0001062"})
            WHERE any(x IN
                      region.`http://www.geneontology.org/formats/oboInOwl#hasDbXref`
                      WHERE x =~ curie)
            WITH region
            MATCH
            (region)
            <-[:ilxtr:isDefinedInRegion]-
            (parent)
            -[:ilxtr:isDefinedInTaxon]->
            (species:Class{iri: "${species-id}"})
            WITH parent
            MATCH path = (root)
            -[:ilxtr:isDefinedBy]->(artifact)
            -[:subClassOf*0..2]->(parent)
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if species_id and species_id.startswith('http:'):
            species_id = parse.quote(species_id, safe='')
        if fma_id and fma_id.startswith('http:'):
            fma_id = parse.quote(fma_id, safe='')
        kwargs = {'species_id': species_id, 'fma_id': fma_id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/parcellationRootsFMA/{species_id}/{fma_id}').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_rootLabels_root_id(self, root_id, output='application/json'):
        """ Get the list of all parcellation labels for a single label root from: /dynamic/prod/sparc/rootLabels/{root-id}

            Arguments:
            root_id: ontology id of the parcellation label root

            Query:
            MATCH (label)-[:subClassOf]->(root:Class{iri: "${root-id}"})
            WITH label
            MATCH path = (label)-[relation*0..1]-(maybe)
            WHERE NONE (r in relation WHERE type(r) IN ["isDefinedBy", "subClassOf", "filler"])
                  AND NOT (label.iri =~ ".*_:.*") AND NOT (maybe.iri =~ ".*_:.*")
                  AND label.iri <> "http://www.w3.org/2002/07/owl#Nothing"
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if root_id and root_id.startswith('http:'):
            root_id = parse.quote(root_id, safe='')
        kwargs = {'root_id': root_id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/rootLabels/{root_id}').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def prod_sparc_speciesList(self, output='application/json'):
        """ Get the list of all NCBITaxon species identifiers relevant to SPARC from: /dynamic/prod/sparc/speciesList

            Arguments:


            Query:
            MATCH (n)
            WHERE n.iri IN [
                    "http://purl.obolibrary.org/obo/NCBITaxon_9378",
                    "http://purl.obolibrary.org/obo/NCBITaxon_9606",
                    "http://purl.obolibrary.org/obo/NCBITaxon_9685",
                    "http://purl.obolibrary.org/obo/NCBITaxon_9823",
                    "http://purl.obolibrary.org/obo/NCBITaxon_10090",
                    "http://purl.obolibrary.org/obo/NCBITaxon_10116"]
            RETURN n

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        kwargs = {}
        # type caste not needed
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/prod/sparc/speciesList').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def shortestSimple(self, start_id=None, end_id=None, max_depth=None, relationship=None, output='application/json'):
        """ Get the shortest path between two IDs from: /dynamic/shortestSimple

            Arguments:
            start_id: The starting node (ex UBERON:0005751)
            end_id: The ending node (ex UBERON:0001255)
            max_depth: the maximum depth to traverse
            relationship: The property to traverse (ex subClassOf or subClassOf|partOf|isA)

            Query:
            MATCH (start:Class{iri: '${start_id}'})
            WITH start
            MATCH (end:Class{iri: '${end_id}'})
            WITH start, end
            MATCH path = shortestPath((start)-[:${relationship}*..${max_depth}]->(end))
            RETURN path

            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        kwargs = {'start_id': start_id, 'end_id': end_id, 'max_depth': max_depth, 'relationship': relationship}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/dynamic/shortestSimple').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None


class Dynamic(DynamicBase):

    @staticmethod
    def _path_to_id(path):
        return (path.strip('/')
                .replace('dynamic/', '')
                .replace('{', '')
                .replace('}', '')
                .replace('/', '_')
                .replace('-', '_'))

    def _path_function_arg(self, path):
        if '?' in path:
            path, query = path.split('?', 1)
            kwargs = parse_qs(query)
        else:
            kwargs = {}

        if '.' in path:
            raise ValueError('extensions not supported directly please use output=mimetype')

        if ':' in path:  # curie FIXME way more potential arguments here ...
            key = lambda s: len(s)
            args = []
            puts = []
            while ':' in path:
                path, arg = path.rsplit('/', 1)
                args.append(arg)
                base = self._path_to_id(path)
                putative = self._path_to_id(path + '/{')
                if ':' not in putative:
                    puts.append(putative)

            args.reverse()  # args are parsed backwards

            cands = sorted([p for p in dir(self) if p.startswith(puts[0])], key=key)
            if len(cands) > 1:
                effs = [getattr(self, self._path_to_id(c)) for c in cands]
                specs = [inspect.getargspec(f) for f in effs]
                lens = [len(s.args) - 1 - len(s.defaults) for s in specs]
                largs = len(args)
                new_cands = []
                for c, l in zip(cands, lens):
                    if l == largs:
                        new_cands.append(c)

                if len(new_cands) > 1:
                    raise TypeError('sigh')

                cands = new_cands

            fname = cands[0]
        else:
            arg = None
            args = []

            fname = self._path_to_id(path)

        if not hasattr(self, fname):
            raise TypeError(f'{self._basePath} does not have endpoint {path} -> {fname!r}')

        return getattr(self, fname), args, kwargs

    def dispatch(self, path, output='application/json', **kwargs):
        f, args, query_kwargs = self._path_function_arg(path)
        kwargs.update(query_kwargs)
        return f(*args, output=output, **kwargs) if args else f(output=output, **kwargs)


class GraphBase(restService):
    """ Graph services """

    def __init__(self, basePath=None, verbose=False, cache=False, safe_cache=False, key=None):
        if basePath is None:
            basePath = BASEPATH
        self._basePath = basePath
        self._verbose = verbose
        super().__init__(cache=cache, safe_cache=safe_cache, key=key)

    def getEdges(self, type, entail=None, limit=None, skip=None, callback=None, output='application/json'):
        """ Get nodes connected by an edge type from: /graph/edges/{type}

            Arguments:
            type: The type of the edge
            entail: Should subproperties and equivalent properties be included
            limit: The number of edges to be returned
            skip: The number of edges to skip
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        kwargs = {'type': type, 'entail': entail, 'limit': limit, 'skip': skip, 'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('type', **kwargs)
        url = self._basePath + ('/graph/edges/{type}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'type'}
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def getNeighborsFromMultipleRoots(self, id, depth=None, blankNodes=None, relationshipType=None, direction=None, entail=None, project=None, callback=None, output='application/json'):
        """ Get neighbors from: /graph/neighbors

            Arguments:
            id: This ID should be either a CURIE or an IRI
            depth: How far to traverse neighbors
            blankNodes: Traverse blank nodes
            relationshipType: Which relationship to traverse
            direction: Which direction to traverse: INCOMING, OUTGOING, BOTH (default).
                       Only used if relationshipType is specified.
            entail: Should subproperties and equivalent properties be included
            project: Which properties to project. Defaults to '*'.
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if id and id.startswith('http:'):
            id = parse.quote(id, safe='')
        kwargs = {'id': id, 'depth': depth, 'blankNodes': blankNodes, 'relationshipType': relationshipType, 'direction': direction, 'entail': entail, 'project': project, 'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/graph/neighbors').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def getNeighbors(self, id, depth=None, blankNodes=None, relationshipType=None, direction=None, entail=None, project=None, callback=None, output='application/json'):
        """ Get neighbors from: /graph/neighbors/{id}

            Arguments:
            id: This ID should be either a CURIE or an IRI
            depth: How far to traverse neighbors
            blankNodes: Traverse blank nodes
            relationshipType: Which relationship to traverse
            direction: Which direction to traverse: INCOMING, OUTGOING, BOTH (default).
                       Only used if relationshipType is specified.
            entail: Should subproperties and equivalent properties be included
            project: Which properties to project. Defaults to '*'.
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if id and id.startswith('http:'):
            id = parse.quote(id, safe='')
        kwargs = {'id': id, 'depth': depth, 'blankNodes': blankNodes, 'relationshipType': relationshipType, 'direction': direction, 'entail': entail, 'project': project, 'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('id', **kwargs)
        url = self._basePath + ('/graph/neighbors/{id}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'id'}
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def getProperties(self, callback=None, output='application/json'):
        """ Get all property keys from: /graph/properties

            Arguments:
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
        """

        kwargs = {'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/graph/properties').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def reachableFrom(self, id, hint=None, relationships=None, lbls=None, callback=None, output='application/json'):
        """ Get all the nodes reachable from a starting point, traversing the provided edges. from: /graph/reachablefrom/{id}

            Arguments:
            id: The type of the edge
            hint: A label hint to find the start node.
            relationships: A list of relationships to traverse, in order. Supports cypher
                           operations such as relA|relB or relA*.
            lbls: A list of node labels to filter.
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if id and id.startswith('http:'):
            id = parse.quote(id, safe='')
        kwargs = {'id': id, 'hint': hint, 'relationships': relationships, 'lbls': lbls, 'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('id', **kwargs)
        url = self._basePath + ('/graph/reachablefrom/{id}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'id'}
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def getRelationships(self, callback=None, output='application/json'):
        """ Get all relationship types from: /graph/relationship_types

            Arguments:
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
        """

        kwargs = {'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/graph/relationship_types').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def getNode(self, id, project=None, callback=None, output='application/json'):
        """ Get all properties of a node from: /graph/{id}

            Arguments:
            id: This ID should be either a CURIE or an IRI
            project: Which properties to project. Defaults to '*'.
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
                application/graphson
                application/xml
                application/graphml+xml
                application/xgmml
                text/gml
                text/csv
                text/tab-separated-values
                image/jpeg
                image/png
        """

        if id and id.startswith('http:'):
            id = parse.quote(id, safe='')
        kwargs = {'id': id, 'project': project, 'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('id', **kwargs)
        url = self._basePath + ('/graph/{id}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'id'}
        output = self._get('GET', url, requests_params, output)
        return output if output else None


class Graph(GraphBase):
    @staticmethod
    def ordered(start, edges, predicate=None, inverse=False):
        """ Depth first edges from a SciGraph response. """
        s, o = 'sub', 'obj'
        if inverse:
            s, o = o, s

        edges = list(edges)
        for edge in tuple(edges):
            if predicate is not None and edge['pred'] != predicate:
                print('scoop!')
                continue

            if edge[s] == start:
                yield edge
                edges.remove(edge)
                yield from Graph.ordered(edge[o], edges, predicate=predicate)


class Lexical(restService):
    """ Lexical services """

    def __init__(self, basePath=None, verbose=False, cache=False, safe_cache=False, key=None):
        if basePath is None:
            basePath = BASEPATH
        self._basePath = basePath
        self._verbose = verbose
        super().__init__(cache=cache, safe_cache=safe_cache, key=key)

    def getChunks(self, text, output='application/json'):
        """ Extract entities from text. from: /lexical/chunks

            Arguments:
            text: The text from which to extract chunks
            outputs:
                application/json
        """

        kwargs = {'text': text}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/lexical/chunks').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def getEntities(self, text, output='application/json'):
        """ Extract entities from text. from: /lexical/entities

            Arguments:
            text: The text from which to extract entities
            outputs:
                application/json
        """

        kwargs = {'text': text}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/lexical/entities').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def getPos(self, text, output='application/json'):
        """ Tag parts of speech. from: /lexical/pos

            Arguments:
            text: The text to tag
            outputs:
                application/json
        """

        kwargs = {'text': text}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/lexical/pos').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def getSentences(self, text, output='application/json'):
        """ Split text into sentences. from: /lexical/sentences

            Arguments:
            text: The text to split
            outputs:
                application/json
        """

        kwargs = {'text': text}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/lexical/sentences').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []


class Refine(restService):
    """ OpenRefine Reconciliation Services """

    def __init__(self, basePath=None, verbose=False, cache=False, safe_cache=False, key=None):
        if basePath is None:
            basePath = BASEPATH
        self._basePath = basePath
        self._verbose = verbose
        super().__init__(cache=cache, safe_cache=safe_cache, key=key)

    def getPreview(self, id, output='application/json'):
        """  from: /refine/preview/{id}

            Arguments:
            id:
            outputs:
                application/json
                application/javascript
        """

        if id and id.startswith('http:'):
            id = parse.quote(id, safe='')
        kwargs = {'id': id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('id', **kwargs)
        url = self._basePath + ('/refine/preview/{id}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'id'}
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def suggestFromTerm(self, query=None, queries=None, callback=None, output='application/json'):
        """ Reconcile terms from: /refine/reconcile

            Arguments:
            query: A call to a reconciliation service API for a single query looks
                   like either of these:<ul><li>
                   http://foo.com/bar/reconcile?query=...string...</li>
                   <li>http://foo.com/bar/reconcile?query={...json object
                   literal...}</li></ul>If the query parameter is a
                   string, then it's an abbreviation of <em>
                   query={"query":...string...}</em>.<em>NOTE:</em>
                    We encourage all API consumers to consider the single query
                   mode <b>DEPRECATED</b>.Refine currently only uses the
                   multiple query mode, but other consumers of the API may use the
                   single query option since it was included in the spec.
            queries: A call to a standard reconciliation service API for multiple
                     queries looks like this:<ul><li>
                     http://foo.com/bar/reconcile?queries={...json object
                     literal...}</li></ul>The json object literal has zero
                     or more key/value pairs with arbitrary keys where the value is in
                     the same format as a single query, e.g.<ul><li>
                     http://foo.com/bar/reconcile?queries={ "q0" : { "query" : "foo"
                     }, "q1" : { "query" : "bar" } }</li></ul>"q0" and
                     "q1" can be arbitrary strings.
            callback: Name of the JSONP callback ('fn' by default). Supplying this
                      parameter or requesting a javascript media type will cause a
                      JSONP response to be rendered.
            outputs:
                application/json
                application/javascript
        """

        kwargs = {'query': query, 'queries': queries, 'callback': callback}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/refine/reconcile').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def suggestFromTerm_POST(self, query=None, queries=None, output='application/json'):
        """ Reconcile terms from: /refine/reconcile

            Arguments:
            query: A call to a reconciliation service API for a single query looks
                   like either of these:<ul><li>
                   http://foo.com/bar/reconcile?query=...string...</li>
                   <li>http://foo.com/bar/reconcile?query={...json object
                   literal...}</li></ul>If the query parameter is a
                   string, then it's an abbreviation of <em>
                   query={"query":...string...}</em>.<em>NOTE:</em>
                    We encourage all API consumers to consider the single query
                   mode <b>DEPRECATED</b>.Refine currently only uses the
                   multiple query mode, but other consumers of the API may use the
                   single query option since it was included in the spec.
            queries: A call to a standard reconciliation service API for multiple
                     queries looks like this:<ul><li>
                     http://foo.com/bar/reconcile?queries={...json object
                     literal...}</li></ul>The json object literal has zero
                     or more key/value pairs with arbitrary keys where the value is in
                     the same format as a single query, e.g.<ul><li>
                     http://foo.com/bar/reconcile?queries={ "q0" : { "query" : "foo"
                     }, "q1" : { "query" : "bar" } }</li></ul>"q0" and
                     "q1" can be arbitrary strings.
            outputs:
                application/json
                application/javascript
        """

        kwargs = {'query': query, 'queries': queries}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/refine/reconcile').format(**kwargs)
        requests_params = kwargs
        output = self._get('POST', url, requests_params, output)
        return output if output else None

    def getView(self, id, output='application/json'):
        """  from: /refine/view/{id}

            Arguments:
            id:
            outputs:
                application/json
                application/javascript
        """

        if id and id.startswith('http:'):
            id = parse.quote(id, safe='')
        kwargs = {'id': id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('id', **kwargs)
        url = self._basePath + ('/refine/view/{id}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'id'}
        output = self._get('GET', url, requests_params, output)
        return output if output else None


class Vocabulary(restService):
    """ Vocabulary services """

    def __init__(self, basePath=None, verbose=False, cache=False, safe_cache=False, key=None):
        if basePath is None:
            basePath = BASEPATH
        self._basePath = basePath
        self._verbose = verbose
        super().__init__(cache=cache, safe_cache=safe_cache, key=key)

    def findByPrefix(self, term, limit=None, searchSynonyms=None, searchAbbreviations=None, searchAcronyms=None, includeDeprecated=None, category=None, prefix=None, output='application/json'):
        """ Find a concept by its prefix from: /vocabulary/autocomplete/{term}

            Arguments:
            term: Term prefix to find
            limit: Maximum result count
            searchSynonyms: Should synonyms be matched
            searchAbbreviations: Should abbreviations be matched
            searchAcronyms: Should acronyms be matched
            includeDeprecated: Should deprecated classes be included
            category: Categories to search (defaults to all)
            prefix: CURIE prefixes to search (defaults to all)
            outputs:
                application/json
        """

        kwargs = {'term': term, 'limit': limit, 'searchSynonyms': searchSynonyms, 'searchAbbreviations': searchAbbreviations, 'searchAcronyms': searchAcronyms, 'includeDeprecated': includeDeprecated, 'category': category, 'prefix': prefix}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('term', **kwargs)
        url = self._basePath + ('/vocabulary/autocomplete/{term}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'term'}
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def getCategories(self, output='application/json'):
        """ Get all categories from: /vocabulary/categories

            Arguments:

            outputs:
                application/json
        """

        kwargs = {}
        # type caste not needed
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/vocabulary/categories').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def findById(self, id, output='application/json'):
        """ Find a concept by its ID from: /vocabulary/id/{id}

            Arguments:
            id: ID to find
            outputs:
                application/json
        """

        if id and id.startswith('http:'):
            id = parse.quote(id, safe='')
        kwargs = {'id': id}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('id', **kwargs)
        url = self._basePath + ('/vocabulary/id/{id}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'id'}
        output = self._get('GET', url, requests_params, output)
        return output if output else None

    def getCuriePrefixes(self, output='application/json'):
        """ Get all CURIE prefixes from: /vocabulary/prefixes

            Arguments:

            outputs:
                application/json
        """

        kwargs = {}
        # type caste not needed
        param_rest = self._make_rest(None, **kwargs)
        url = self._basePath + ('/vocabulary/prefixes').format(**kwargs)
        requests_params = kwargs
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def searchByTerm(self, term, limit=None, searchSynonyms=None, searchAbbreviations=None, searchAcronyms=None, category=None, prefix=None, output='application/json'):
        """ Find a concept from a term fragment from: /vocabulary/search/{term}

            Arguments:
            term: Term to find
            limit: Maximum result count
            searchSynonyms: Should synonyms be matched
            searchAbbreviations: Should abbreviations be matched
            searchAcronyms: Should acronyms be matched
            category: Categories to search (defaults to all)
            prefix: CURIE prefixes to search (defaults to all)
            outputs:
                application/json
        """

        kwargs = {'term': term, 'limit': limit, 'searchSynonyms': searchSynonyms, 'searchAbbreviations': searchAbbreviations, 'searchAcronyms': searchAcronyms, 'category': category, 'prefix': prefix}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('term', **kwargs)
        url = self._basePath + ('/vocabulary/search/{term}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'term'}
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def suggestFromTerm(self, term, limit=None, output='application/json'):
        """ Suggest terms from: /vocabulary/suggestions/{term}

            Arguments:
            term: Mispelled term
            limit: Maximum result count
            outputs:
                application/json
        """

        kwargs = {'term': term, 'limit': limit}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('term', **kwargs)
        url = self._basePath + ('/vocabulary/suggestions/{term}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'term'}
        output = self._get('GET', url, requests_params, output)
        return output if output else []

    def findByTerm(self, term, limit=None, searchSynonyms=None, searchAbbreviations=None, searchAcronyms=None, category=None, prefix=None, output='application/json'):
        """ Find a concept from a term from: /vocabulary/term/{term}

            Arguments:
            term: Term to find
            limit: Maximum result count
            searchSynonyms: Should synonyms be matched
            searchAbbreviations: Should abbreviations be matched
            searchAcronyms: Should acronyms be matched
            category: Categories to search (defaults to all)
            prefix: CURIE prefixes to search (defaults to all)
            outputs:
                application/json
        """

        kwargs = {'term': term, 'limit': limit, 'searchSynonyms': searchSynonyms, 'searchAbbreviations': searchAbbreviations, 'searchAcronyms': searchAcronyms, 'category': category, 'prefix': prefix}
        kwargs = {k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}
        param_rest = self._make_rest('term', **kwargs)
        url = self._basePath + ('/vocabulary/term/{term}').format(**kwargs)
        requests_params = {k:v for k, v in kwargs.items() if k != 'term'}
        output = self._get('GET', url, requests_params, output)
        return output if output else []

