├── examples ├── __init__.py ├── v2 │ ├── __init__.py │ ├── conditions.py │ ├── lab_tests.py │ ├── risk_factors.py │ ├── parse.py │ ├── symptoms.py │ ├── rationale.py │ ├── red_flags.py │ ├── suggest.py │ ├── triage.py │ ├── explain.py │ ├── search.py │ └── diagnosis.py ├── v3 │ ├── __init__.py │ ├── diagnosis.py │ ├── triage.py │ ├── explain.py │ ├── specialist_recommender.py │ ├── conditions.py │ ├── lab_tests.py │ ├── risk_factors.py │ ├── symptoms.py │ ├── concepts.py │ ├── suggest.py │ ├── parse.py │ └── search.py └── config.py ├── .gitattributes ├── requirements.txt ├── setup.cfg ├── infermedica_api ├── connectors │ ├── v3 │ │ ├── __init__.py │ │ ├── basic.py │ │ └── standard.py │ ├── v2 │ │ ├── __init__.py │ │ ├── models │ │ │ ├── __init__.py │ │ │ ├── rationale.py │ │ │ ├── parse.py │ │ │ ├── red_flag.py │ │ │ ├── symptom.py │ │ │ ├── condition.py │ │ │ ├── lab_test.py │ │ │ ├── risk_factor.py │ │ │ ├── explain.py │ │ │ ├── base.py │ │ │ └── diagnosis.py │ │ ├── basic.py │ │ ├── model.py │ │ └── standard.py │ ├── __init__.py │ └── common.py ├── exceptions.py ├── webservice.py └── __init__.py ├── .pre-commit-config.yaml ├── .gitignore ├── .editorconfig ├── LICENSE ├── setup.py └── README.md /examples/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/v2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/v3/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.32.2 2 | black>=24.4.2 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | 4 | [bdist_wheel] 5 | universal=1 6 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v3/__init__.py: -------------------------------------------------------------------------------- 1 | from .basic import BasicAPIv3Connector 2 | from .standard import APIv3Connector, ConceptType 3 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/__init__.py: -------------------------------------------------------------------------------- 1 | from .basic import BasicAPIv2Connector 2 | from .standard import APIv2Connector 3 | from .model import ModelAPIv2Connector 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/psf/black 3 | rev: 24.4.2 4 | hooks: 5 | - id: black 6 | language_version: python3 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | 6 | # JetBrains 7 | .idea/ 8 | 9 | # OSX 10 | .DS_Store 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | eggs/ 19 | .eggs/ 20 | *.egg-info/ 21 | .installed.cfg 22 | *.egg 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 4 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /examples/v3/diagnosis.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | # Prepare the diagnosis request object 10 | request = config.get_example_request_data() 11 | 12 | # call diagnosis 13 | request = api.diagnosis(**request) 14 | 15 | print(request) 16 | -------------------------------------------------------------------------------- /examples/v3/triage.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | # Prepare the diagnosis request object 10 | request = config.get_example_request_data() 11 | 12 | # call triage method 13 | request = api.triage(**request) 14 | 15 | # and see the results 16 | print("\n\n", request) 17 | -------------------------------------------------------------------------------- /examples/v3/explain.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | # Prepare the diagnosis request object 10 | request = config.get_example_request_data() 11 | 12 | # call the explain method 13 | request = api.explain(**request, target_id="c_49") 14 | 15 | # and see the results 16 | print("\n\n", request) 17 | -------------------------------------------------------------------------------- /examples/v3/specialist_recommender.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | # Prepare the diagnosis request object 10 | request = config.get_example_request_data() 11 | 12 | # call triage method 13 | request = api.specialist_recommender(**request) 14 | 15 | # and see the results 16 | print("\n\n", request) 17 | -------------------------------------------------------------------------------- /examples/v2/conditions.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 8 | 9 | print("Conditions list:") 10 | print(api.condition_list(), end="\n\n") 11 | 12 | print("Condition details:") 13 | print(api.condition_details("c_221"), end="\n\n") 14 | 15 | print("Non-existent condition details:") 16 | print(api.condition_details("fail_test")) 17 | -------------------------------------------------------------------------------- /examples/v2/lab_tests.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 8 | 9 | print("Laboratory tests list:") 10 | print(api.lab_test_list(), end="\n\n") 11 | 12 | print("\n\nLaboratory test details:") 13 | print(api.lab_test_details("lt_81"), end="\n\n") 14 | 15 | print("Non-existent laboratory test details:") 16 | print(api.lab_test_details("fail_test")) 17 | -------------------------------------------------------------------------------- /examples/v2/risk_factors.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 8 | 9 | print("Risk factors list:") 10 | print(api.risk_factor_list(), end="\n\n") 11 | 12 | print("\n\nRisk factor details:") 13 | print(api.risk_factor_details("p_37"), end="\n\n") 14 | 15 | print("Non-existent risk factor details:") 16 | print(api.risk_factor_details("fail_test")) 17 | -------------------------------------------------------------------------------- /examples/v3/conditions.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | age = 35 10 | 11 | print("Conditions list:") 12 | print(api.condition_list(age=age), end="\n\n") 13 | 14 | print("Condition details:") 15 | print(api.condition_details("c_221", age=age), end="\n\n") 16 | 17 | print("Non-existent condition details:") 18 | print(api.condition_details("fail_test", age=age)) 19 | -------------------------------------------------------------------------------- /examples/v3/lab_tests.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | age = 52 10 | 11 | print("Laboratory tests list:") 12 | print(api.lab_test_list(age=age), end="\n\n") 13 | 14 | print("\n\nLaboratory test details:") 15 | print(api.lab_test_details("lt_81", age=age), end="\n\n") 16 | 17 | print("Non-existent laboratory test details:") 18 | print(api.lab_test_details("fail_test", age=age)) 19 | -------------------------------------------------------------------------------- /examples/v3/risk_factors.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | age = 45 10 | 11 | print("Risk factors list:") 12 | print(api.risk_factor_list(age=age), end="\n\n") 13 | 14 | print("\n\nRisk factor details:") 15 | print(api.risk_factor_details("p_37", age=age), end="\n\n") 16 | 17 | print("Non-existent risk factor details:") 18 | print(api.risk_factor_details("fail_test", age=age)) 19 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .condition import Condition, ConditionList 2 | from .diagnosis import ( 3 | Diagnosis, 4 | DiagnosisQuestion, 5 | ConditionResult, 6 | ConditionResultList, 7 | ) 8 | from .lab_test import LabTest, LabTestList 9 | from .risk_factor import RiskFactor, RiskFactorList 10 | from .symptom import Symptom, SymptomList 11 | from .explain import ExplainResults, ExplainResult 12 | from .parse import ParseResults, ParseMention 13 | from .rationale import RationaleResult 14 | from .red_flag import RedFlag, RedFlagList 15 | -------------------------------------------------------------------------------- /examples/v2/parse.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | 7 | if __name__ == "__main__": 8 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 9 | 10 | print("Parse simple text:") 11 | response = api.parse("i feel stomach pain but no coughing today") 12 | print(response, end="\n\n") 13 | 14 | print("Parse simple text and include tokens information:") 15 | response = api.parse( 16 | "i feel stomach pain but no coughing today", include_tokens=True 17 | ) 18 | print(response, end="\n\n") 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Infermedica 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /examples/v2/symptoms.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 8 | 9 | print("Symptoms list:") 10 | print(api.symptom_list(), end="\n\n") 11 | 12 | print("Symptom details:") 13 | print(api.symptom_details("s_56"), end="\n\n") 14 | 15 | print("Symptom details (with children):") 16 | print(api.symptom_details("s_551"), end="\n\n") 17 | 18 | print("Non-existent symptom details:") 19 | print(api.symptom_details("fail_test")) 20 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/rationale.py: -------------------------------------------------------------------------------- 1 | from .base import BaseModel 2 | 3 | 4 | class RationaleResult(BaseModel): 5 | """Model class for handling rationale result object.""" 6 | 7 | @staticmethod 8 | def from_json(json): 9 | """ 10 | Constructs RationaleResult object from given dict and returns it. 11 | 12 | :param json: Dict with a rationale result value. 13 | :type json: dict 14 | 15 | :returns: RationaleResult object 16 | :rtype: :class:`infermedica_api.models.RationaleResult` 17 | """ 18 | return RationaleResult(**json) 19 | -------------------------------------------------------------------------------- /infermedica_api/connectors/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.connectors 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains API Connector classes and related utils. 8 | """ 9 | from typing import Union 10 | 11 | from .common import SearchConceptType 12 | from .v2 import BasicAPIv2Connector, APIv2Connector, ModelAPIv2Connector 13 | from .v3 import BasicAPIv3Connector, APIv3Connector, ConceptType 14 | 15 | APIConnectorType = Union[ 16 | BasicAPIv2Connector, 17 | APIv2Connector, 18 | ModelAPIv2Connector, 19 | BasicAPIv3Connector, 20 | APIv3Connector, 21 | ] 22 | -------------------------------------------------------------------------------- /examples/v3/symptoms.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | age = 28 10 | age_unit = "year" 11 | 12 | print("Symptoms list:") 13 | print(api.symptom_list(age=age, age_unit=age_unit), end="\n\n") 14 | 15 | print("Symptom details:") 16 | print(api.symptom_details("s_56", age=age, age_unit=age_unit), end="\n\n") 17 | 18 | print("Symptom details (with children):") 19 | print(api.symptom_details("s_551", age=age, age_unit=age_unit), end="\n\n") 20 | 21 | print("Non-existent symptom details:") 22 | print(api.symptom_details("fail_test", age=age, age_unit=age_unit)) 23 | -------------------------------------------------------------------------------- /examples/v3/concepts.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | print("Concepts list:") 10 | print(api.concept_list(), end="\n\n") 11 | 12 | print("Condition and risk factor concepts list:") 13 | print( 14 | api.concept_list( 15 | types=[ 16 | infermedica_api.ConceptType.CONDITION, 17 | infermedica_api.ConceptType.RISK_FACTOR, 18 | ] 19 | ), 20 | end="\n\n", 21 | ) 22 | 23 | print("Concepts details:") 24 | print(api.concept_details("s_13"), end="\n\n") 25 | 26 | print("Non-existent concepts details:") 27 | print(api.concept_details("fail_test")) 28 | -------------------------------------------------------------------------------- /examples/v3/suggest.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | # Prepare the diagnosis request object 10 | request = config.get_example_request_data() 11 | 12 | # call triage method 13 | response = api.suggest(**request) 14 | print("\n\n", response) 15 | 16 | # Set different suggest_method 17 | request["suggest_method"] = "risk_factors" 18 | 19 | # call triage method 20 | response = api.suggest(**request) 21 | print("\n\n", response) 22 | 23 | # Set different suggest_method 24 | request["suggest_method"] = "red_flags" 25 | 26 | # call triage method 27 | response = api.suggest(**request) 28 | print("\n\n", response) 29 | -------------------------------------------------------------------------------- /examples/v2/rationale.py: -------------------------------------------------------------------------------- 1 | from infermedica_api.connectors.v2.models import Diagnosis 2 | from .. import config 3 | 4 | config.setup_examples() 5 | import infermedica_api 6 | 7 | if __name__ == "__main__": 8 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 9 | 10 | # To prepare a diagnosis request it needs to have a sufficient amount of evidence 11 | # The most appropriate way to get a request for rationale method is to 12 | # use the one which has been created while interacting with diagnosis. 13 | # For example purposes, a new one is created. 14 | request = Diagnosis(sex="male", age=65) 15 | 16 | request.add_symptom("s_10", "present") 17 | request.add_symptom("s_608", "present") 18 | 19 | # call the rationale method 20 | request = api.rationale(request) 21 | 22 | # and see the results 23 | print("\n\n", request) 24 | -------------------------------------------------------------------------------- /examples/v2/red_flags.py: -------------------------------------------------------------------------------- 1 | from infermedica_api.connectors.v2.models import Diagnosis 2 | from .. import config 3 | 4 | config.setup_examples() 5 | import infermedica_api 6 | 7 | if __name__ == "__main__": 8 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 9 | 10 | # To prepare a diagnosis request it needs to have a sufficient amount of evidence 11 | # The most appropriate way to get a request for red_flags method is to 12 | # use the one which has been created while interacting with diagnosis. 13 | # For example purposes, a new one is created. 14 | request = Diagnosis(sex="female", age=35) 15 | 16 | request.add_symptom("s_10", "present") 17 | request.add_symptom("s_608", "present") 18 | 19 | # call the red_flags method 20 | request = api.red_flags(request) 21 | 22 | # and see the results 23 | print("\n\n", request) 24 | -------------------------------------------------------------------------------- /examples/v3/parse.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | age = 38 10 | 11 | print("Parse simple text:") 12 | response = api.parse("i feel stomach pain but no coughing today", age=age) 13 | print(response, end="\n\n") 14 | 15 | print("Parse simple text and include tokens information:") 16 | response = api.parse( 17 | "i feel stomach pain but no coughing today", age=age, include_tokens=True 18 | ) 19 | print(response, end="\n\n") 20 | 21 | print("Parse simple sex specific text:") 22 | age = 25 23 | response = api.parse("painful period", age=age, sex="female") 24 | print(response, end="\n\n") 25 | response = api.parse("painful period", age=age, sex="male") 26 | print(response, end="\n\n") 27 | -------------------------------------------------------------------------------- /examples/v2/suggest.py: -------------------------------------------------------------------------------- 1 | from infermedica_api.connectors.v2.models import Diagnosis 2 | from .. import config 3 | 4 | config.setup_examples() 5 | import infermedica_api 6 | 7 | if __name__ == "__main__": 8 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 9 | 10 | # Prepare the diagnosis request object 11 | request = Diagnosis(sex="male", age=30) 12 | request.add_symptom("s_1193", "present") 13 | request.add_symptom("s_488", "present") 14 | request.add_symptom("s_418", "absent") 15 | 16 | # Alternatively prepare a dict based request object 17 | # request = { 18 | # 'sex': 'male', 19 | # 'age': 30, 20 | # 'evidence': [ 21 | # {'id': 's_1193', 'choice_id': 'present'}, 22 | # {'id': 's_488', 'choice_id': 'present'}, 23 | # {'id': 's_418', 'choice_id': 'absent'} 24 | # ] 25 | # } 26 | 27 | # call triage method 28 | request = api.suggest(request) 29 | 30 | # and see the results 31 | print("\n\n", request) 32 | -------------------------------------------------------------------------------- /examples/v2/triage.py: -------------------------------------------------------------------------------- 1 | from infermedica_api.connectors.v2.models import Diagnosis 2 | from .. import config 3 | 4 | config.setup_examples() 5 | import infermedica_api 6 | 7 | if __name__ == "__main__": 8 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 9 | 10 | # Prepare the diagnosis request object 11 | request = Diagnosis(sex="male", age=30) 12 | request.add_symptom("s_1193", "present") 13 | request.add_symptom("s_488", "present") 14 | request.add_symptom("s_418", "absent") 15 | 16 | # Alternatively prepare a dict based request object 17 | # request = { 18 | # 'sex': 'male', 19 | # 'age': 30, 20 | # 'evidence': [ 21 | # {'id': 's_1193', 'choice_id': 'present'}, 22 | # {'id': 's_488', 'choice_id': 'present'}, 23 | # {'id': 's_418', 'choice_id': 'absent'} 24 | # ] 25 | # } 26 | 27 | # call triage method 28 | request = api.triage(request) 29 | 30 | # and see the results 31 | print("\n\n", request) 32 | -------------------------------------------------------------------------------- /examples/v2/explain.py: -------------------------------------------------------------------------------- 1 | from infermedica_api.connectors.v2.models import Diagnosis 2 | from .. import config 3 | 4 | config.setup_examples() 5 | import infermedica_api 6 | 7 | if __name__ == "__main__": 8 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 9 | 10 | # To prepare a diagnosis request it needs to have a sufficient amount of evidence 11 | # The most appropriate way to get a request for explain method is to 12 | # use the one which has been created while interacting with diagnosis. 13 | # For example purposes, a new one is created. 14 | request = Diagnosis(sex="female", age=35) 15 | 16 | request.add_symptom("s_10", "present") 17 | request.add_symptom("s_608", "present") 18 | request.add_symptom("s_1394", "absent") 19 | request.add_symptom("s_216", "present") 20 | request.add_symptom("s_9", "present") 21 | request.add_symptom("s_188", "present") 22 | 23 | # call the explain method 24 | request = api.explain(request, target_id="c_62") 25 | 26 | # and see the results 27 | print("\n\n", request) 28 | -------------------------------------------------------------------------------- /examples/v2/search.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 8 | 9 | print("Look for evidence containing the phrase headache:") 10 | print(api.search("headache"), end="\n\n") 11 | 12 | print("Look for evidence containing the phrase breast, female specific symptoms:") 13 | print(api.search("breast", sex="female"), end="\n\n") 14 | 15 | print( 16 | "Look for evidence containing the phrase breast, female specific symptoms, with a limit of 5 results:" 17 | ) 18 | print(api.search("breast", sex="female", max_results=5), end="\n\n") 19 | 20 | print("Look for symptoms and risk factors containing the phrase trauma:") 21 | print( 22 | api.search( 23 | "trauma", 24 | types=[ 25 | infermedica_api.SearchConceptType.SYMPTOM, 26 | infermedica_api.SearchConceptType.RISK_FACTOR, 27 | ], 28 | ), 29 | end="\n\n", 30 | ) 31 | -------------------------------------------------------------------------------- /examples/v3/search.py: -------------------------------------------------------------------------------- 1 | from .. import config 2 | 3 | config.setup_examples() 4 | import infermedica_api 5 | 6 | if __name__ == "__main__": 7 | api: infermedica_api.APIv3Connector = infermedica_api.get_api() 8 | 9 | age = 38 10 | 11 | print("Look for evidence containing the phrase headache:") 12 | print(api.search("headache", age=age), end="\n\n") 13 | print("Look for evidence containing the phrase headache, send Interview-Id:") 14 | print(api.search("headache", age=age, interview_id="aaaa-bbbb-cccc-dddd"), end="\n\n") 15 | 16 | print("Look for evidence containing the phrase breast, female specific symptoms:") 17 | print(api.search("breast", age=age, sex="female"), end="\n\n") 18 | 19 | print( 20 | "Look for evidence containing the phrase breast, female specific symptoms, with a limit of 5 results:" 21 | ) 22 | print(api.search("breast", age=age, sex="female", max_results=5), end="\n\n") 23 | 24 | print("Look for symptoms and risk factors containing the phrase trauma:") 25 | print( 26 | api.search( 27 | "trauma", 28 | age=age, 29 | types=[ 30 | infermedica_api.SearchConceptType.SYMPTOM, 31 | infermedica_api.SearchConceptType.RISK_FACTOR, 32 | ], 33 | ), 34 | end="\n\n", 35 | ) 36 | -------------------------------------------------------------------------------- /examples/v2/diagnosis.py: -------------------------------------------------------------------------------- 1 | from infermedica_api.connectors.v2.models import Diagnosis 2 | from .. import config 3 | 4 | config.setup_examples() 5 | import infermedica_api 6 | 7 | if __name__ == "__main__": 8 | api: infermedica_api.ModelAPIv2Connector = infermedica_api.get_api("v2") 9 | 10 | request = Diagnosis(sex="female", age=35) 11 | 12 | request.add_symptom("s_21", "present", source="initial") 13 | request.add_symptom("s_98", "present", source="initial") 14 | request.add_symptom("s_418", "present", source="suggest") 15 | request.add_symptom("s_18", "present", source="predefined") 16 | request.add_symptom("s_10", "present", source="red_flags") 17 | request.add_symptom("s_107", "absent") 18 | 19 | # call diagnosis 20 | request = api.diagnosis(request) 21 | 22 | print(request) 23 | 24 | # ask patient the questions returned by diagnosis and update request with patient answers 25 | request.add_symptom("s_99", "present") 26 | request.add_symptom("s_8", "absent") 27 | request.add_symptom("s_25", "present") 28 | # ... and so on until you decided that enough question have been asked 29 | # or you have sufficient results in request.conditions 30 | 31 | # call diagnosis again with updated request 32 | request = api.diagnosis(request) 33 | 34 | # repeat the process 35 | print("\n\n", request) 36 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/parse.py: -------------------------------------------------------------------------------- 1 | from .base import BaseModel 2 | 3 | 4 | class ParseMention(BaseModel): 5 | """Model class for API parse mention object.""" 6 | 7 | @staticmethod 8 | def from_json(json): 9 | """ 10 | Constructs ParseMention object from given dict and returns it. 11 | 12 | :param json: Dict with mention values 13 | :type json: dict 14 | 15 | :returns: ParseMention details object 16 | :rtype: :class:`infermedica_api.models.ParseMention` 17 | """ 18 | return ParseMention(**json) 19 | 20 | 21 | class ParseResults(BaseModel): 22 | """Model class for API parse response object.""" 23 | 24 | def __init__(self, **kwargs): 25 | super().__init__(**kwargs) 26 | 27 | self.mentions = [ParseMention.from_json(val) for val in self.mentions] 28 | 29 | @staticmethod 30 | def from_json(json): 31 | """ 32 | Constructs ParseResults object from given dict and returns it. 33 | 34 | :param json: Dict with parse result value. 35 | :type json: dict 36 | 37 | :returns: Parse result object 38 | :rtype: :class:`infermedica_api.models.ParseResults` 39 | """ 40 | return ParseResults(**json) 41 | 42 | def to_dict(self): 43 | """ 44 | Transform object to dict. 45 | 46 | :return: ParseResults object as dict. 47 | :rtype: dict 48 | """ 49 | return {"mentions": [mention.to_dict() for mention in self.mentions]} 50 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/red_flag.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.models.red_flag 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains red flag models for data returned from api. 8 | """ 9 | from .base import BaseModel, BaseModelList 10 | 11 | 12 | class RedFlag(BaseModel): 13 | """Model class for API red flag object.""" 14 | 15 | @staticmethod 16 | def from_json(json): 17 | """ 18 | Constructs RedFlag object from given dict and returns it. 19 | 20 | :param json: Dict with red flag values 21 | :type json: dict 22 | 23 | :returns: RedFlag details object 24 | :rtype: :class:`infermedica_api.models.RedFlag` 25 | """ 26 | return RedFlag(**json) 27 | 28 | 29 | class RedFlagList(BaseModelList): 30 | """Model class for API list of red flag objects.""" 31 | 32 | @staticmethod 33 | def from_json(json): 34 | """ 35 | Constructs RedFlagList object from list of dicts and returns it. 36 | 37 | :param json: List with red flag details dict values 38 | :type json: list 39 | 40 | :returns: RedFlagList list object 41 | :rtype: :class:`infermedica_api.models.RedFlagList` 42 | """ 43 | mapping = {} 44 | for i, item in enumerate(json): 45 | item = RedFlag(**item) 46 | mapping[item.id] = i 47 | return RedFlagList(json, mapping=mapping) 48 | 49 | def get_red_flag_details(self, _id): 50 | return self._get_details(_id) 51 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/symptom.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.models.symptom 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains symptom models for data returned from api. 8 | """ 9 | from .base import BaseModel, BaseModelList 10 | 11 | 12 | class Symptom(BaseModel): 13 | """Model class for API symptoms details objects.""" 14 | 15 | @staticmethod 16 | def from_json(json): 17 | """ 18 | Constructs Symptom object from given dict and returns it. 19 | 20 | :param json: Dict with symptom values 21 | :type json: dict 22 | 23 | :returns: Symptom details object 24 | :rtype: :class:`infermedica_api.models.Symptom` 25 | """ 26 | return Symptom(**json) 27 | 28 | 29 | class SymptomList(BaseModelList): 30 | """Model class for API list of symptom details objects.""" 31 | 32 | @staticmethod 33 | def from_json(json): 34 | """ 35 | Constructs SymptomList object from list of dicts and returns it. 36 | 37 | :param json: List with symptom details dict values 38 | :type json: list 39 | 40 | :returns: Symptoms details list object 41 | :rtype: :class:`infermedica_api.models.SymptomList` 42 | """ 43 | mapping = {} 44 | for i, item in enumerate(json): 45 | item = Symptom(**item) 46 | mapping[item.id] = i 47 | return SymptomList(json, mapping=mapping) 48 | 49 | def get_symptom_details(self, _id): 50 | return self._get_details(_id) 51 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/condition.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.models.condition 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains condition models for data returned from api. 8 | """ 9 | from .base import BaseModel, BaseModelList 10 | 11 | 12 | class Condition(BaseModel): 13 | """Model class for API condition details objects.""" 14 | 15 | @staticmethod 16 | def from_json(json): 17 | """ 18 | Constructs Condition object from given dict and returns it. 19 | 20 | :param json: Dict with condition values 21 | :type json: dict 22 | 23 | :returns: Condition details object 24 | :rtype: :class:`infermedica_api.models.Condition` 25 | """ 26 | return Condition(**json) 27 | 28 | 29 | class ConditionList(BaseModelList): 30 | """Model class for API list of condition details objects.""" 31 | 32 | @staticmethod 33 | def from_json(json): 34 | """ 35 | Constructs ConditionList object from list of dicts and returns it. 36 | 37 | :param json: List with condition details dict values 38 | :type json: list 39 | 40 | :returns: Conditions details list object 41 | :rtype: :class:`infermedica_api.models.ConditionList` 42 | """ 43 | mapping = {} 44 | for i, item in enumerate(json): 45 | item = Condition(**item) 46 | mapping[item.id] = i 47 | return ConditionList(json, mapping=mapping) 48 | 49 | def get_condition_details(self, _id): 50 | return self._get_details(_id) 51 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/lab_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.models.lab_test 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains laboratory test models for data returned from api. 8 | """ 9 | from .base import BaseModel, BaseModelList 10 | 11 | 12 | class LabTest(BaseModel): 13 | """Model class for API laboratory testss details objects.""" 14 | 15 | @staticmethod 16 | def from_json(json): 17 | """ 18 | Constructs LabTest object from given dict and returns it. 19 | 20 | :param json: Dict with laboratory test values 21 | :type json: dict 22 | 23 | :returns: LabTest details object 24 | :rtype: :class:`infermedica_api.models.LabTest` 25 | """ 26 | return LabTest(**json) 27 | 28 | 29 | class LabTestList(BaseModelList): 30 | """Model class for API list of laboratory test details objects.""" 31 | 32 | @staticmethod 33 | def from_json(json): 34 | """ 35 | Constructs LabTestList object from list of dicts and returns it. 36 | 37 | :param json: List with laboratory test details dict values 38 | :type json: list 39 | 40 | :returns: LabTests details list object 41 | :rtype: :class:`infermedica_api.models.LabTestList` 42 | """ 43 | mapping = {} 44 | for i, item in enumerate(json): 45 | item = LabTest(**item) 46 | mapping[item.id] = i 47 | return LabTestList(json, mapping=mapping) 48 | 49 | def get_lab_test_details(self, _id): 50 | return self._get_details(_id) 51 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/risk_factor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.models.risk_factor 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains risk factor models for data returned from api. 8 | """ 9 | from .base import BaseModel, BaseModelList 10 | 11 | 12 | class RiskFactor(BaseModel): 13 | """Model class for API risk factor details objects.""" 14 | 15 | @staticmethod 16 | def from_json(json): 17 | """ 18 | Constructs RiskFactor object from given dict and returns it. 19 | 20 | :param json: Dict with risk factor values 21 | :type json: dict 22 | 23 | :returns: Risk factor details object 24 | :rtype: :class:`infermedica_api.models.RiskFactor` 25 | """ 26 | return RiskFactor(**json) 27 | 28 | 29 | class RiskFactorList(BaseModelList): 30 | """Model class for API list of risk factor details objects.""" 31 | 32 | @staticmethod 33 | def from_json(json): 34 | """ 35 | Constructs RiskFactorList object from list of dicts and returns it. 36 | 37 | :param json: List with risk factor details dict values 38 | :type json: list 39 | 40 | :returns: Risk factor details list object 41 | :rtype: :class:`infermedica_api.models.RiskFactorList` 42 | """ 43 | mapping = {} 44 | for i, item in enumerate(json): 45 | item = RiskFactor(**item) 46 | mapping[item.id] = i 47 | return RiskFactorList(json, mapping=mapping) 48 | 49 | def get_risk_factor_details(self, _id): 50 | return self._get_details(_id) 51 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/explain.py: -------------------------------------------------------------------------------- 1 | from .base import BaseModel 2 | 3 | 4 | class ExplainResults(BaseModel): 5 | """Model class for API explain object.""" 6 | 7 | def __init__(self, **kwargs): 8 | super().__init__(**kwargs) 9 | 10 | self.supporting_evidence = [ 11 | ExplainResult.from_json(val) for val in self.supporting_evidence 12 | ] 13 | self.conflicting_evidence = [ 14 | ExplainResult.from_json(val) for val in self.conflicting_evidence 15 | ] 16 | 17 | @staticmethod 18 | def from_json(json): 19 | """ 20 | Constructs ExplainResults object from given dict and returns it. 21 | 22 | :param json: Dict with explain results value. 23 | :type json: dict 24 | 25 | :returns: Explain result details object 26 | :rtype: :class:`infermedica_api.models.ExplainResults` 27 | """ 28 | return ExplainResults(**json) 29 | 30 | def to_dict(self): 31 | """ 32 | Transform object to dict. 33 | 34 | :return: ExplainResults object as dict. 35 | :rtype: dict 36 | """ 37 | return { 38 | "supporting_evidence": [ 39 | result.to_dict() for result in self.supporting_evidence 40 | ], 41 | "conflicting_evidence": [ 42 | result.to_dict() for result in self.conflicting_evidence 43 | ], 44 | } 45 | 46 | 47 | class ExplainResult(BaseModel): 48 | """Model class for handling single explain result object.""" 49 | 50 | @staticmethod 51 | def from_json(json): 52 | """ 53 | Constructs ExplainResult object from given dict and returns it. 54 | 55 | :param json: Dict with single explain result value. 56 | :type json: dict 57 | 58 | :returns: Explain result details object 59 | :rtype: :class:`infermedica_api.models.ExplainResult` 60 | """ 61 | return ExplainResult(**json) 62 | -------------------------------------------------------------------------------- /examples/config.py: -------------------------------------------------------------------------------- 1 | import http.client 2 | import logging 3 | import os 4 | 5 | 6 | def setup_examples(): 7 | """ 8 | Setup environment to easily run examples. 9 | API credentials need to be provided here in order 10 | to set up api object correctly. 11 | """ 12 | try: 13 | import infermedica_api 14 | except ImportError: 15 | import sys 16 | 17 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) 18 | import infermedica_api 19 | 20 | # !!! SET YOUR CREDENTIALS AS ENVIRONMENTAL VARIABLES "APP_ID" & "APP_KEY" OR SET THEM HERE !!! 21 | app_id = os.getenv("APP_ID", "YOUR_APP_ID") 22 | app_key = os.getenv("APP_KEY", "YOUR_APP_KEY") 23 | 24 | # Prepare API v3 connector as default one 25 | infermedica_api.configure( 26 | **{ 27 | "app_id": app_id, 28 | "app_key": app_key, 29 | "dev_mode": True, # Use only during development or testing/staging, on production remove this parameter 30 | } 31 | ) 32 | 33 | # Prepare API v2 connector under 'v2' alias 34 | infermedica_api.configure( 35 | **{ 36 | "alias": "v2", 37 | "api_connector": "ModelAPIv2Connector", 38 | "app_id": app_id, 39 | "app_key": app_key, 40 | "dev_mode": True, # Use only during development or testing/staging, on production remove this parameter 41 | } 42 | ) 43 | 44 | # enable logging of requests and responses 45 | http.client.HTTPConnection.debuglevel = 1 46 | 47 | logging.basicConfig() 48 | logging.getLogger().setLevel(logging.DEBUG) 49 | requests_log = logging.getLogger("requests.packages.urllib3") 50 | requests_log.setLevel(logging.DEBUG) 51 | requests_log.propagate = True 52 | 53 | 54 | def get_example_request_data(): 55 | return { 56 | "sex": "male", 57 | "age": 30, 58 | "evidence": [ 59 | {"id": "s_1193", "choice_id": "present", "source": "initial"}, 60 | {"id": "s_488", "choice_id": "present"}, 61 | {"id": "s_418", "choice_id": "absent"}, 62 | ], 63 | } 64 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/basic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.connectors.v2 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains API Connector classes for API v2 version. 8 | """ 9 | 10 | from typing import Optional, List, Dict, Any, Union 11 | 12 | from ..common import ( 13 | EvidenceList, 14 | ExtrasDict, 15 | BaseAPIConnector, 16 | BasicAPICommonMethodsMixin, 17 | ) 18 | 19 | 20 | # Types 21 | DiagnosticDict = Dict[str, Union[str, int, EvidenceList, ExtrasDict]] 22 | 23 | 24 | class BasicAPIv2Connector(BasicAPICommonMethodsMixin, BaseAPIConnector): 25 | def __init__(self, *args, api_version="v2", **kwargs: Any): 26 | """ 27 | Initialize API connector. 28 | 29 | :param args: (optional) Arguments passed to lower level parent :class:`BaseAPIConnector` method 30 | :param api_version: (optional) API version, default is 'v2' 31 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BaseAPIConnector` method 32 | 33 | Usage:: 34 | >>> import infermedica_api 35 | >>> api = infermedica_api.BasicAPIv2Connector(app_id='YOUR_APP_ID', app_key='YOUR_APP_KEY') 36 | """ 37 | super().__init__(*args, api_version=api_version, **kwargs) 38 | 39 | def red_flags( 40 | self, data: Dict, params: Optional[Dict] = None, headers: Optional[Dict] = None 41 | ) -> List[Dict[str, str]]: 42 | """ 43 | Makes an API request with provided diagnosis data and returns a list 44 | of evidence that may be related to potentially life-threatening 45 | conditions. 46 | See the docs: https://developer.infermedica.com/docs/red-flags. 47 | 48 | :param data: Request data 49 | :param params: (optional) URL query params 50 | :param headers: (optional) HTTP request headers 51 | 52 | :returns: A list of dicts with 'id', 'name' and 'common_name' keys 53 | """ 54 | method = self._get_method("red_flags") 55 | 56 | return self.call_api_post( 57 | method=method, data=data, params=params, headers=headers 58 | ) 59 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import re 3 | 4 | from setuptools import setup, find_packages 5 | 6 | 7 | VERSION = "" 8 | with open("infermedica_api/__init__.py", "r") as f: 9 | VERSION = re.search( 10 | r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE 11 | ).group(1) 12 | 13 | long_description = """ 14 | The Infermedica Python client provides access to powerful medical diagnostic API created by Infermedica. 15 | 16 | README and Samples - https://github.com/infermedica/python-api 17 | 18 | Developer portal - https://developer.infermedica.com 19 | 20 | API Reference - https://developer.infermedica.com/docs/api 21 | """ 22 | 23 | setup( 24 | name="infermedica-api", 25 | version=VERSION, 26 | description="The Infermedica Python client for Infermedica API.", 27 | long_description=long_description, 28 | classifiers=[ 29 | "Environment :: Web Environment", 30 | "Intended Audience :: Developers", 31 | "Intended Audience :: Healthcare Industry", 32 | "License :: OSI Approved :: Apache Software License", 33 | "Natural Language :: English", 34 | "Operating System :: OS Independent", 35 | "Programming Language :: Python", 36 | "Programming Language :: Python :: 3", 37 | "Programming Language :: Python :: 3.6", 38 | "Programming Language :: Python :: 3.7", 39 | "Programming Language :: Python :: 3.8", 40 | "Programming Language :: Python :: 3.9", 41 | "Programming Language :: Python :: 3.10", 42 | "Programming Language :: Python :: 3.11", 43 | "Programming Language :: Python :: 3.12", 44 | "Topic :: Software Development :: Libraries", 45 | "Topic :: Scientific/Engineering :: Medical Science Apps.", 46 | "Topic :: Scientific/Engineering :: Artificial Intelligence", 47 | ], 48 | keywords="infermedica medical api library rest http", 49 | author="Infermedica", 50 | author_email="support@infermedica.com", 51 | url="https://github.com/infermedica/python-api", 52 | license="Apache 2.0", 53 | packages=find_packages(exclude=["examples"]), 54 | install_requires=["requests>=2.32.2"], 55 | ) 56 | -------------------------------------------------------------------------------- /infermedica_api/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.exceptions 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains the set of API request exceptions. 8 | """ 9 | 10 | 11 | class ConnectionError(Exception): 12 | """API connection error.""" 13 | 14 | def __init__(self, response, content=None): 15 | self.response = response 16 | self.content = content 17 | 18 | def __str__(self): 19 | message = "Failed." 20 | if hasattr(self.response, "status_code"): 21 | message += f" Response status: {self.response.status_code}." 22 | if hasattr(self.response, "reason"): 23 | message += f" Reason: {self.response.reason}." 24 | if self.content is not None: 25 | message += f" Error message: {self.content}" 26 | return message 27 | 28 | 29 | class BadRequest(ConnectionError): 30 | """400 Bad Request.""" 31 | 32 | 33 | class UnauthorizedAccess(ConnectionError): 34 | """401 Unauthorized.""" 35 | 36 | 37 | class ForbiddenAccess(ConnectionError): 38 | """403 Forbidden.""" 39 | 40 | 41 | class ResourceNotFound(ConnectionError): 42 | """404 Not found.""" 43 | 44 | 45 | class ServerError(ConnectionError): 46 | """5xx Server Error.""" 47 | 48 | 49 | class MissingConfiguration(Exception): 50 | """API not configured.""" 51 | 52 | def __init__(self, alias=None): 53 | self.alias = alias 54 | 55 | def __str__(self): 56 | if self.alias: 57 | return ( 58 | f"API configuration for alias '{self.alias}' has not been configured." 59 | ) 60 | return "Default API configuration has not been configured." 61 | 62 | 63 | class MissingAPIDefinition(Exception): 64 | """API not configured.""" 65 | 66 | def __init__(self, api_version=None): 67 | self.api_version = api_version 68 | 69 | def __str__(self): 70 | return f"Missing API definition for '{self.api_version}'." 71 | 72 | 73 | class MethodNotAllowed(ConnectionError): 74 | """405 Method Not Allowed.""" 75 | 76 | 77 | class MethodNotAvailableInAPIVersion(Exception): 78 | """Try to call API method, which is not available.""" 79 | 80 | def __init__(self, current_api_version, method): 81 | self.api_version = current_api_version 82 | self.method = method 83 | 84 | def __str__(self): 85 | return f"Method '{self.method}' is not available in the {self.api_version} api version." 86 | 87 | 88 | class InvalidSearchConceptType(Exception): 89 | def __init__(self, filter_name): 90 | self.filter_name = filter_name 91 | 92 | def __str__(self): 93 | return f"Invalid search filter: '{self.filter_name}'." 94 | 95 | 96 | class InvalidAgeUnit(Exception): 97 | def __init__(self, age_unit): 98 | self.age_unit = age_unit 99 | 100 | def __str__(self): 101 | return f"Invalid age unit: '{self.age_unit}', use 'year' or 'month'." 102 | 103 | 104 | class InvalidConceptType(Exception): 105 | def __init__(self, concept_type): 106 | self.concept_type = concept_type 107 | 108 | def __str__(self): 109 | return f"Invalid concept type: '{self.concept_type}'." 110 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v3/basic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.connectors.v3 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains API Connector classes for API v3 version. 8 | """ 9 | 10 | from typing import Optional, List, Dict, Any 11 | 12 | from ..common import ( 13 | ConceptDetails, 14 | BaseAPIConnector, 15 | BasicAPICommonMethodsMixin, 16 | ) 17 | 18 | 19 | class BasicAPIv3Connector(BasicAPICommonMethodsMixin, BaseAPIConnector): 20 | def __init__(self, *args, api_version="v3", **kwargs: Any): 21 | """ 22 | Initialize API connector. 23 | 24 | :param args: (optional) Arguments passed to lower level parent :class:`BaseAPIConnector` method 25 | :param api_version: (optional) API version, default is 'v3' 26 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BaseAPIConnector` method 27 | 28 | Usage:: 29 | >>> import infermedica_api 30 | >>> api = infermedica_api.BasicAPIv3Connector(app_id='YOUR_APP_ID', app_key='YOUR_APP_KEY') 31 | """ 32 | super().__init__(*args, api_version=api_version, **kwargs) 33 | 34 | def specialist_recommender( 35 | self, data: Dict, params: Optional[Dict] = None, headers: Optional[Dict] = None 36 | ) -> Dict: 37 | """ 38 | Makes a specialist recommendation API request with provided diagnosis data. 39 | See the docs: https://developer.infermedica.com/docs/v3/specialist-recommender. 40 | 41 | :param data: Request data 42 | :param params: (optional) URL query params 43 | :param headers: (optional) HTTP request headers 44 | 45 | :returns: A dict object with api response 46 | """ 47 | method = self._get_method("specialist_recommender") 48 | 49 | return self.call_api_post( 50 | method=method, data=data, params=params, headers=headers 51 | ) 52 | 53 | def concept_details( 54 | self, 55 | concept_id: str, 56 | params: Optional[Dict] = None, 57 | headers: Optional[Dict] = None, 58 | ) -> ConceptDetails: 59 | """ 60 | Makes an API request and returns concept details object. 61 | See the docs: https://developer.infermedica.com/docs/v3/concepts. 62 | 63 | :param concept_id: Concept id 64 | :param params: (optional) URL query params 65 | :param headers: (optional) HTTP request headers 66 | 67 | :returns: A dict object with concept details 68 | """ 69 | method = self._get_method("concept_details") 70 | method = method.format(**{"id": concept_id}) 71 | 72 | return self.call_api_get(method=method, params=params, headers=headers) 73 | 74 | def concept_list( 75 | self, params: Optional[Dict] = None, headers: Optional[Dict] = None 76 | ) -> List[ConceptDetails]: 77 | """ 78 | Makes an API request and returns list of concept details objects. 79 | See the docs: https://developer.infermedica.com/docs/v3/concepts. 80 | 81 | :param params: (optional) URL query params 82 | :param headers: (optional) HTTP request headers 83 | 84 | :returns: A list of dict objects with concept details 85 | """ 86 | method = self._get_method("concepts") 87 | 88 | return self.call_api_get(method=method, params=params, headers=headers) 89 | -------------------------------------------------------------------------------- /infermedica_api/webservice.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.webservice 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains function responsible for manging a global handlers for API Connector classes. 8 | """ 9 | from inspect import isclass 10 | from typing import Optional, Any, Union 11 | 12 | from . import exceptions, connectors 13 | 14 | __api__ = None 15 | __api_aliased__ = {} 16 | 17 | 18 | def get_api(alias: str = None) -> connectors.APIConnectorType: 19 | """ 20 | Returns global API object if present, 21 | otherwise raise MissingConfiguration exception. 22 | 23 | :param alias: Alias of the API to retrieve 24 | 25 | :returns: An API object 26 | 27 | :raises: :class:`infermedica_api.exceptions.MissingConfiguration` 28 | """ 29 | global __api__ 30 | global __api_aliased__ 31 | 32 | if alias: 33 | try: 34 | return __api_aliased__[alias] 35 | except KeyError: 36 | raise exceptions.MissingConfiguration(alias) 37 | 38 | if __api__ is None: 39 | raise exceptions.MissingConfiguration() 40 | 41 | return __api__ 42 | 43 | 44 | def configure( 45 | app_id: str, 46 | app_key: str, 47 | alias: Optional[str] = None, 48 | default: Optional[bool] = False, 49 | api_connector: Optional[Union[connectors.APIConnectorType, str]] = "APIv3Connector", 50 | **kwargs: Any 51 | ) -> connectors.APIConnectorType: 52 | """ 53 | Configure and create new global API object with given configuration. Many global configurations can be created 54 | upfront (e.g. with different credentials or language models configured) by providing a unique alias 55 | for each configuration. The configrations can be latter accessed any where in the projcts by 56 | simply calling `infermedica_api.get_api()` or `infermedica_api.get_api('')`. 57 | 58 | Usage: 59 | >>> import infermedica_api 60 | >>> infermedica_api.configure(app_id='YOUR_APP_ID', app_key='YOUR_APP_KEY') 61 | 62 | # Then somewhere in the project: 63 | >>> import infermedica_api 64 | >>> api = infermedica_api.get_api() 65 | >>> api.info() 66 | 67 | :param app_id: Infermedica API App Id 68 | :param app_key: Infermedica API App Key 69 | :param alias: (optional) Alias for the configuration, if not provided the configuration 70 | became the default configuration when calling `get_api` without alias 71 | :param default: (optional) If alias proveded determinates if this configuration 72 | should also be set as the default configuration when calling `get_api` without alias 73 | :param api_connector: (optional) APIConnector class (may be custom) or a name of the of 74 | the build in API connector classes to be used 75 | :param kwargs: (optional) Config to be used to initialise API connector class 76 | 77 | :returns: An API connctor object 78 | """ 79 | global __api__ 80 | global __api_aliased__ 81 | 82 | if isclass(api_connector): 83 | api_connector_class = api_connector 84 | else: 85 | api_connector_class = getattr(connectors, api_connector) 86 | api_obj = api_connector_class(app_id=app_id, app_key=app_key, **kwargs) 87 | 88 | if alias: 89 | __api_aliased__[alias] = api_obj 90 | 91 | if default: 92 | __api__ = api_obj 93 | else: 94 | __api__ = api_obj 95 | 96 | return api_obj 97 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.models.base 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains common models for data returned from api. 8 | """ 9 | import json 10 | 11 | 12 | class ModelCommon: 13 | """Abstract class with implementation of commonly used functions.""" 14 | 15 | class Meta: 16 | abstract = True 17 | 18 | def __str__(self): 19 | """Returns nicely formatted object.""" 20 | return self.to_json(pretty_print=True) 21 | 22 | def to_json(self, pretty_print=False): 23 | """ 24 | Returns json dumps of the class object. 25 | If pretty_print argument set to True, 26 | returned string is nicely formatted. 27 | 28 | :param pretty_print: Boolean which determinants 29 | if output should be humanly readable formatted 30 | :type pretty_print: bool 31 | 32 | :returns: String with json data of the object 33 | :rtype: str 34 | """ 35 | if pretty_print: 36 | return json.dumps( 37 | self, default=lambda o: o.__dict__, sort_keys=True, indent=4 38 | ) 39 | return json.dumps(self, default=lambda o: o.__dict__) 40 | 41 | def to_dict(self): 42 | """ 43 | Transform class object to dict. 44 | 45 | :return: Class object as dict. 46 | :rtype: dict 47 | """ 48 | return dict( 49 | { 50 | key: val.to_dict() if hasattr(val, "to_dict") else val 51 | for key, val in self.__dict__.items() 52 | } 53 | ) 54 | 55 | 56 | class BaseModel(ModelCommon): 57 | """ 58 | Abstract class of model with init function, 59 | which generically assign object parameters. 60 | """ 61 | 62 | class Meta: 63 | abstract = True 64 | 65 | def __init__(self, **kwargs): 66 | for key, value in kwargs.items(): 67 | setattr(self, key, value) 68 | 69 | 70 | class BaseModelList(list, ModelCommon): 71 | """ 72 | Abstract class of list model with init function, which assign key mapping. 73 | """ 74 | 75 | class Meta: 76 | abstract = True 77 | 78 | def __init__(self, *args, **kwargs): 79 | if "mapping" in kwargs: 80 | self.mapping = kwargs.pop("mapping") 81 | else: 82 | self.mapping = {} 83 | 84 | super().__init__(*args, **kwargs) 85 | 86 | def _get_details(self, _id): 87 | """ 88 | Generic function to handle object returns by the object id. 89 | 90 | :param _id: Object id 91 | :type _id: str 92 | 93 | :returns: Model object with given id 94 | """ 95 | try: 96 | return self[self.mapping[_id]] 97 | except (IndexError, KeyError) as e: 98 | return None 99 | 100 | def to_list(self): 101 | """ 102 | Transform class object to simple list. 103 | 104 | :return: Class object as simple list. 105 | :rtype: list 106 | """ 107 | return [val.to_dict() if hasattr(val, "to_dict") else val for val in self] 108 | 109 | def to_dict(self): 110 | """ 111 | Transform class object to list. 112 | It does not return dict as the name suggest, but it's created for consistency reasons. 113 | 114 | :return: Class object as list. 115 | :rtype: list 116 | """ 117 | return self.to_list() 118 | -------------------------------------------------------------------------------- /infermedica_api/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Infermedica Python API client 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | Infermedica Python API client provides access to powerful medical diagnostic API created by Infermedica. 8 | Basic usage: 9 | 10 | >>> import infermedica_api 11 | >>> api = infermedica_api.APIv3Connector(app_id='YOUR_APP_ID', app_key='YOUR_APP_KEY') 12 | >>> print(api.info()) 13 | { 14 | "updated_at": "2016-09-27T07:08:25Z", 15 | "conditions_count": 504, 16 | "symptoms_count": 1114, 17 | "risk_factors_count": 41, 18 | "lab_tests_count": 448 19 | } 20 | 21 | ... you can also configure API object globally and use it without initialization in any module. 22 | First configure the API: 23 | 24 | >>> import infermedica_api 25 | >>> infermedica_api.configure(app_id='YOUR_APP_ID', app_key='YOUR_APP_KEY') 26 | 27 | ... and then in any module just import infermedica module and get configured API object: 28 | 29 | >>> import infermedica_api 30 | >>> api = infermedica_api.get_api() 31 | 32 | The other requests are presented in `examples` and are described in our documentation. Full documentation 33 | is available at . 34 | 35 | :copyright: (c) 2017 by Infermedica. 36 | :license: Apache 2.0, see LICENSE for more details. 37 | 38 | """ 39 | 40 | __title__ = "Infermedica API" 41 | __version__ = "1.0.0" 42 | __author__ = "Arkadiusz Szydelko" 43 | __license__ = "Apache 2.0" 44 | __copyright__ = "Copyright 2021 Infermedica" 45 | 46 | DEFAULT_API_VERSION = "v3" 47 | DEFAULT_API_ENDPOINT = "https://api.infermedica.com/" 48 | 49 | API_CONFIG = { 50 | "v2": { 51 | "methods": { 52 | "info": "/info", 53 | "search": "/search", 54 | "suggest": "/suggest", 55 | "parse": "/parse", 56 | "diagnosis": "/diagnosis", 57 | "explain": "/explain", 58 | "triage": "/triage", 59 | "conditions": "/conditions", 60 | "condition_details": "/conditions/{id}", 61 | "symptoms": "/symptoms", 62 | "symptom_details": "/symptoms/{id}", 63 | "lab_tests": "/lab_tests", 64 | "lab_test_details": "/lab_tests/{id}", 65 | "risk_factors": "/risk_factors", 66 | "risk_factor_details": "/risk_factors/{id}", 67 | "red_flags": "/red_flags", 68 | "rationale": "/rationale", 69 | }, 70 | }, 71 | "v3": { 72 | "methods": { 73 | "info": "/info", 74 | "search": "/search", 75 | "parse": "/parse", 76 | "suggest": "/suggest", 77 | "diagnosis": "/diagnosis", 78 | "rationale": "/rationale", 79 | "explain": "/explain", 80 | "triage": "/triage", 81 | "specialist_recommender": "/recommend_specialist", 82 | "conditions": "/conditions", 83 | "condition_details": "/conditions/{id}", 84 | "symptoms": "/symptoms", 85 | "symptom_details": "/symptoms/{id}", 86 | "lab_tests": "/lab_tests", 87 | "lab_test_details": "/lab_tests/{id}", 88 | "risk_factors": "/risk_factors", 89 | "risk_factor_details": "/risk_factors/{id}", 90 | "red_flags": "/red_flags", 91 | "concepts": "/concepts", 92 | "concept_details": "/concepts/{id}", 93 | }, 94 | }, 95 | } 96 | 97 | from .connectors import ( 98 | SearchConceptType, 99 | ConceptType, 100 | BasicAPIv2Connector, 101 | APIv2Connector, 102 | BasicAPIv3Connector, 103 | ModelAPIv2Connector, 104 | APIv3Connector, 105 | ) 106 | from .webservice import configure, get_api 107 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/models/diagnosis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.models.diagnosis 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains models for data returned from api as well as object to construct api requests, 8 | related to /diagnosis method. 9 | """ 10 | from .base import BaseModel, BaseModelList, ModelCommon 11 | 12 | 13 | class ConditionResult(BaseModel): 14 | """Model class for API condition results objects from diagnosis calls.""" 15 | 16 | @staticmethod 17 | def from_json(json): 18 | """ 19 | Constructs ConditionResult object from given dict and returns it. 20 | 21 | :param json: Dict with condition result values 22 | :type json: dict 23 | 24 | :returns: Condition result details object 25 | :rtype: :class:`infermedica_api.models.ConditionResult` 26 | """ 27 | return ConditionResult(**json) 28 | 29 | 30 | class ConditionResultList(BaseModelList): 31 | """Model class for API list of condition result objects from diagnosis calls.""" 32 | 33 | @staticmethod 34 | def from_json(json): 35 | """ 36 | Constructs ConditionResultList object from list of dicts and returns it. 37 | 38 | :param json: List with condition result details dict values 39 | :type json: list 40 | 41 | :returns: Condition result details list object 42 | :rtype: :class:`infermedica_api.models.ConditionResultList` 43 | """ 44 | mapping = {} 45 | for i, item in enumerate(json): 46 | item = ConditionResult(**item) 47 | mapping[item.id] = i 48 | return ConditionResultList(json, mapping=mapping) 49 | 50 | def get_condition_details(self, _id): 51 | return self._get_details(_id) 52 | 53 | 54 | class DiagnosisQuestion(BaseModel): 55 | """Model class for API question objects from diagnosis calls.""" 56 | 57 | @staticmethod 58 | def from_json(json): 59 | """ 60 | Constructs DiagnosisQuestion object from given dict and returns it. 61 | 62 | :param json: Dict with diagnosis question values 63 | :type json: dict 64 | 65 | :returns: Diagnosis question details object 66 | :rtype: :class:`infermedica_api.models.DiagnosisQuestion` 67 | """ 68 | return DiagnosisQuestion(**json) 69 | 70 | 71 | class Diagnosis(ModelCommon): 72 | """ 73 | Model class for handling diagnosis requests and responses. 74 | It construct diagnosis request and is updated after next diagnosis calls. 75 | It will contain diagnosis questions, as well as results. 76 | """ 77 | 78 | def __init__(self, sex, age, interview_id=None, **kwargs): 79 | """ 80 | Initialize diagnosis object with basic information about patient. 81 | 82 | :param sex: Patient's sex ("female" or "male") 83 | :type sex: str 84 | :param age: Patient's age 85 | :type age: int 86 | :param interview_id: Unique interview id for diagnosis 87 | :type interview_id: str 88 | """ 89 | self.patient_sex = sex 90 | self.patient_age = age 91 | 92 | self.symptoms = [] 93 | self.lab_tests = [] 94 | self.risk_factors = [] 95 | 96 | self.pursued = [] 97 | 98 | self.question = None 99 | self.conditions = ConditionResultList() 100 | self.should_stop = None 101 | self.extras = {} 102 | self.extras_permanent = {} 103 | 104 | self.interview_id = interview_id 105 | 106 | def __add_evidence(self, collection, _id, state, source=None): 107 | """Helper function to update evidence list.""" 108 | evidence = {"id": _id, "choice_id": state} 109 | if source: 110 | evidence["source"] = source 111 | 112 | collection.append(evidence) 113 | 114 | def add_symptom(self, _id, state, source=None): 115 | """ 116 | Adds symptom with given presence to evidence list. 117 | 118 | :param _id: Symptom id 119 | :type _id: str 120 | :param state: Symptom presence, one of three values ("present", "absent" or "unknown") 121 | :type state: str 122 | :param source: (optional) Symptom source, 123 | one of values: ("initial", "suggest", "predefined", "red_flags") 124 | :type source: str 125 | """ 126 | self.__add_evidence(self.symptoms, _id, state, source=source) 127 | 128 | def add_lab_test(self, _id, state, source=None): 129 | """ 130 | Adds laboratory test with given presence to evidence list. 131 | 132 | :param _id: Laboratory test id 133 | :type _id: str 134 | :param state: Laboratory test presence, one of three values ("present", "absent" or "unknown") 135 | :type state: str 136 | :param source: (optional) Laboratory test source, 137 | one of values: ("initial", "suggest", "predefined", "red_flags") 138 | :type source: str 139 | """ 140 | self.__add_evidence(self.lab_tests, _id, state, source=source) 141 | 142 | def add_risk_factor(self, _id, state, source=None): 143 | """ 144 | Adds risk factor with given presence to evidence list. 145 | 146 | :param _id: Risk factor id 147 | :type _id: str 148 | :param state: Risk factor presence, 149 | one of three values ("present", "absent" or "unknown") 150 | :type state: str 151 | :param source: (optional) Risk factor source, 152 | one of values: ("initial", "suggest", "predefined", "red_flags") 153 | :type source: str 154 | """ 155 | self.__add_evidence(self.risk_factors, _id, state, source=source) 156 | 157 | def add_evidence(self, _id, state, source=None): 158 | """ 159 | Adds evidence with given presence to evidence list. 160 | 161 | :param _id: Evidence id 162 | :type _id: str 163 | :param state: Evidence presence, 164 | one of three values ("present", "absent" or "unknown") 165 | :type state: str 166 | :param source: (optional) Evidence source, 167 | one of values: ("initial", "suggest", "predefined", "red_flags") 168 | :type source: str 169 | """ 170 | evidence_list = self.symptoms 171 | 172 | if _id.startswith(("p_", "rf_")): 173 | evidence_list = self.risk_factors 174 | elif _id.startswith("lt_"): 175 | evidence_list = self.lab_tests 176 | 177 | self.__add_evidence(evidence_list, _id, state, source=source) 178 | 179 | def set_pursued_conditions(self, pursued): 180 | """ 181 | Sets conditions for pursued diagnosis. 182 | 183 | :param pursued: List of condition ids 184 | :type pursued: list of strings 185 | """ 186 | self.pursued = pursued 187 | 188 | def set_interview_id(self, value): 189 | """ 190 | Sets interview id for diagnosis. 191 | 192 | :param value: Unique interview id for diagnosis 193 | :type value: str 194 | """ 195 | self.interview_id = value 196 | 197 | def set_extras(self, attribute, value, permanent=False): 198 | """ 199 | Sets extras attributes to be sent with the diagnosis requests. 200 | 201 | :param attribute: String with the attribute name 202 | :type attribute: str 203 | :param value: Value to set for the attribute 204 | :type value: bool | str | number 205 | :param permanent: Conditions if the attribute shall be sent only in the next diagnosis request 206 | or should persists through multiple diagnosis calls. 207 | :type permanent: bool 208 | """ 209 | if permanent: 210 | self.extras_permanent[attribute] = value 211 | else: 212 | self.extras[attribute] = value 213 | 214 | def update_from_api(self, json): 215 | """ 216 | Updates current object by diagnosis response from the API. 217 | 218 | :param json: Dict obtained from the API diagnosis response 219 | :type json: dict 220 | """ 221 | if "question" in json and isinstance(json["question"], dict): 222 | self.question = DiagnosisQuestion.from_json(json["question"]) 223 | else: 224 | self.question = None 225 | 226 | self.conditions = ConditionResultList.from_json( 227 | json.get("conditions", []) or [] 228 | ) 229 | self.should_stop = json.get("should_stop", None) 230 | self.extras = json.get("extras", {}) or {} 231 | 232 | def get_evidence(self): 233 | return self.symptoms + self.lab_tests + self.risk_factors 234 | 235 | def get_api_request(self): 236 | """ 237 | Based on current Diagnosis object construct 238 | dict object of the format accepted by diagnosis API method. 239 | 240 | :return: Diagnosis API request dict 241 | :rtype: dict 242 | """ 243 | request = { 244 | "sex": self.patient_sex, 245 | "age": self.patient_age, 246 | "evidence": self.get_evidence(), 247 | "extras": dict(self.extras_permanent, **self.extras), 248 | } 249 | 250 | if self.pursued: 251 | request["pursued"] = self.pursued 252 | 253 | return request 254 | 255 | def to_dict(self): 256 | """ 257 | Transform object to dict. 258 | 259 | :return: Diagnosis object as dict. 260 | :rtype: dict 261 | """ 262 | return dict( 263 | self.get_api_request(), 264 | **{ 265 | "question": ( 266 | self.question.to_dict() 267 | if hasattr(self.question, "to_dict") 268 | else None 269 | ), 270 | "conditions": ( 271 | self.conditions.to_dict() 272 | if hasattr(self.conditions, "to_dict") 273 | else None 274 | ), 275 | "should_stop": self.should_stop, 276 | } 277 | ) 278 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Python client for Infermedica API 2 | ================================= 3 | 4 | Infermedica Python client provides pragmatical access to a medical diagnostic API created by [Infermedica](http://infermedica.com). 5 | Before using the library one must register for a developer account and obtain App-Id and App-Key at [Infermedica Developer Portal](https://developer.infermedica.com). All the API documentation is also available there. 6 | 7 | 8 | # Installation 9 | 10 | Install using `pip`: 11 | 12 | ```bash 13 | pip install infermedica-api 14 | ``` 15 | 16 | ### Quick start 17 | 18 | A Quick verification if all works fine: 19 | 20 | ```python 21 | import infermedica_api 22 | 23 | api = infermedica_api.APIv3Connector(app_id="YOUR_APP_ID", app_key="YOUR_APP_KEY") 24 | print(api.info()) 25 | ``` 26 | 27 | # Configuration 28 | 29 | ## API Connectors 30 | The library provides several "API Connectors", that can be used for communication with the Infermedica API. Depending on the required API version and programmatic integration level, a different API Connector should be used. The connectors can be distinguished by two factors. The first factor is the Infermedica API version. The API version is indicated by the connector class name e.g. `APIv3Connector` or `APIv2Connector`. The second factor is the programmatic integration level. There are three levels available. 31 | 32 | Here is a list of currently available API Connectors: 33 | * `BasicAPIv3Connector` - API version: "v3", Integration level: "Basic" 34 | * `APIv3Connector` - API version: "v3", Integration level: "Standard" _(default, recommended)_ 35 | * `BasicAPIv2Connector` - API version: "v2", Integration level: "Basic" 36 | * `APIv2Connector` - API version: "v2", Integration level: "Standard" 37 | * `ModelAPIv2Connector` - API version: "v2", Integration level: "Model" 38 | 39 | ### Basic Connectors 40 | Provides all Infermedica API capabilities as methods, but all methods expose low level HTTP parameters e.g. query params, data or headers. This type of connector should be used if one needs to have full control over the underlying HTTP request content. Each basic connector class is prefixed with "Basic" keyword, e.g. `BasicAPIv3Connector` or `BasicAPIv2Connector`. 41 | 42 | Here is an example to illustrate the basic connector method signature: 43 | ```python 44 | def diagnosis(data, params=None, headers=None) -> Dict: ... 45 | ``` 46 | 47 | ### Standard Connectors 48 | _(default, recommended)_ 49 | 50 | Standard Connectors are based on Basic Connectors, but provide methods with more detailed attribute signatures. At the same time they operate on basic Python data types like `dict`, `list` or `string`. Therefore, it is easier to understand what is required for each method, and they take care of constructing proper a request to Infermedica API. The standard API connectors do not have s prefix in the class name e.g. `APIv3Connector` or `APIv2Connector`. It is the default and recommended level of integration. 51 | 52 | Here is an example to illustrate standard connector method signature: 53 | ```python 54 | def diagnosis(evidence, sex, age, age_unit=None, extras=None, interview_id=None) -> Dict: ... 55 | ``` 56 | 57 | ### Model Connectors 58 | 59 | Model Connectors are even higher level connectors, that are based on Standard Connectors. They expose methods that operate on predefined model classes as their inputs and outputs. Each model connector class is prefixed with the "Model" keyword, e.g. `ModelAPIv2Connector`. 60 | 61 | Here is an example to illustrate model connector method signature: 62 | ```python 63 | def diagnosis(diagnosis_request: Diagnosis) -> Diagnosis: ... 64 | ``` 65 | 66 | 67 | ## Configuration types 68 | There are two options to configure the API module, one may choose the most suitable option for related project. 69 | 70 | ### Local Configuration 71 | Single local API Connector. 72 | 73 | ```python 74 | import infermedica_api 75 | 76 | api = infermedica_api.APIv3Connector(app_id="YOUR_APP_ID", app_key="YOUR_APP_KEY") 77 | 78 | print(api.info()) 79 | ``` 80 | 81 | Each API Connector constructor takes several configuration parameters, among others: 82 | * `app_id` & `app_key` - Infermedica API credentials, the only two mandatory arguments. 83 | * `model` - Infermedica API language model to be used, [see the docs](https://developer.infermedica.com/docs/v3/basics#models). 84 | * `dev_mode` - Flag that indicates if requests are made on local or testing environment and are not real medical cases. 85 | 86 | 87 | ### Global Configuration 88 | Configuration of one or many global API Connectors, that are easily available everywhere. For this purpose the `configure` function has to be used, to register global configurations. The configuration should be done in a place of the project that is executed on project startup, e.g. in some `__init__.py` file. 89 | 90 | The `configure` function takes exactly the same parameters as each API Connector class constructor, but expect few additional optional parameters: 91 | * `alias` - A unique string identifier of the configuration, if not provided configuration is registered as default. 92 | * `default` - In case `alias` is provided, this flag determinate if the configuration should be also registered as the default one. There may be only one default. 93 | * `api_connector` - API connector class to be used for the configuration or just a string with API Connector name, e.g. `"APIv3Connector"` _(default)_ 94 | 95 | To define a single global connector: 96 | 97 | ```python 98 | import infermedica_api 99 | 100 | infermedica_api.configure(app_id="YOUR_APP_ID", app_key="YOUR_APP_KEY") 101 | ``` 102 | 103 | To define multiple global connectors: 104 | 105 | ```python 106 | import infermedica_api 107 | 108 | # Aliased, default configuration with English language 109 | infermedica_api.configure( 110 | alias="en", 111 | default=True, 112 | app_id="YOUR_APP_ID", 113 | app_key="YOUR_APP_KEY", 114 | model="infermedica-en" 115 | ) 116 | 117 | # Configuration with Polish language 118 | infermedica_api.configure( 119 | alias="pl", 120 | app_id="YOUR_APP_ID", 121 | app_key="YOUR_APP_KEY", 122 | model="infermedica-pl" 123 | ) 124 | 125 | # Configuration with English language based on non-default connector 126 | infermedica_api.configure( 127 | alias="basic-en", 128 | app_id="YOUR_APP_ID", 129 | app_key="YOUR_APP_KEY", 130 | model="infermedica-en", 131 | api_connector=infermedica_api.BasicAPIv3Connector 132 | ) 133 | ``` 134 | 135 | ... and then at any place in the project just import the infermedica module and get an already configured API connector through `get_api` function: 136 | 137 | ```python 138 | import infermedica_api 139 | 140 | api = infermedica_api.get_api() # Get default connector 141 | print(api.info()) 142 | 143 | api = infermedica_api.get_api("pl") # Get connector by alias 144 | print(api.info()) 145 | ``` 146 | 147 | # Usage 148 | 149 | Here is an example of how to use the API to get a list of patient's likely conditions. 150 | 151 | ```python 152 | import infermedica_api 153 | 154 | api = infermedica_api.APIv3Connector(app_id="YOUR_APP_ID", app_key="YOUR_APP_KEY") 155 | 156 | # Prepare initial patients diagnostic information. 157 | sex = "female" 158 | age = 32 159 | evidence = [ 160 | {"id": "s_21", "choice_id": "present", "source": "initial"}, 161 | {"id": "s_98", "choice_id": "present", "source": "initial"}, 162 | {"id": "s_107", "choice_id": "present"} 163 | ] 164 | 165 | # call diagnosis 166 | response = api.diagnosis(evidence=evidence, sex=sex, age=age) 167 | 168 | # Access question asked by API 169 | print(response["question"]) 170 | print(response["question"]["text"]) # actual text of the question 171 | print(response["question"]["items"]) # list of related evidence with possible answers 172 | print(response["question"]["items"][0]["id"]) 173 | print(response["question"]["items"][0]["name"]) 174 | print(response["question"]["items"][0]["choices"]) # list of possible answers 175 | print(response["question"]["items"][0]["choices"][0]["id"]) # answer id 176 | print(response["question"]["items"][0]["choices"][0]["label"]) # answer label 177 | 178 | # Check the "should_stop" flag 179 | print(response["should_stop"]) 180 | 181 | # Next update the request and get next question: 182 | evidence.append({ 183 | "id": response["question"]["items"][0]["id"], 184 | "choice_id": response["question"]["items"][0]["choices"][0]["id"] # Just example, the choice_id shall be taken from the real user answer 185 | }) 186 | 187 | # call diagnosis method again 188 | response = api.diagnosis(evidence=evidence, sex=sex, age=age) 189 | 190 | # ... and so on, continue the interview and watch for the "should_stop" flag. 191 | # Once the API returns a "should_stop" flag with the value set to true, the interview questions should stop and you can present the condition results: 192 | 193 | # Access list of conditions with probabilities 194 | print(response["conditions"]) 195 | print(response["conditions"][0]["id"]) 196 | print(response["conditions"][0]["name"]) 197 | print(response["conditions"][0]["probability"]) 198 | ``` 199 | 200 | 201 | ## Examples 202 | 203 | To see more use cases on how to use the library look at the examples provided. One can easily check out and run examples, first define environmental variables with credentials then run the selected example: 204 | 205 | ```bash 206 | export APP_ID= 207 | export APP_KEY= 208 | 209 | python -m examples.v3.search 210 | python -m examples.v3.parse 211 | python -m examples.v3.symptoms 212 | python -m examples.v3.risk_factors 213 | python -m examples.v3.conditions 214 | python -m examples.v3.lab_tests 215 | python -m examples.v3.concepts 216 | python -m examples.v3.diagnosis 217 | python -m examples.v3.explain 218 | python -m examples.v3.triage 219 | python -m examples.v3.suggest 220 | python -m examples.v3.specialist_recommender 221 | ``` 222 | 223 | ## Exceptions 224 | 225 | The library provides its own set of exceptions. Here is a list of exceptions, that are related to network communication and account permissions, which can be raised on any of the API Connector method calls: 226 | 227 | ``` 228 | infermedica_api.exceptions.BadRequest 229 | infermedica_api.exceptions.UnauthorizedAccess 230 | infermedica_api.exceptions.ForbiddenAccess 231 | infermedica_api.exceptions.ResourceNotFound 232 | infermedica_api.exceptions.MethodNotAllowed 233 | infermedica_api.exceptions.ServerError 234 | infermedica_api.exceptions.ConnectionError 235 | ``` 236 | 237 | There are also few additional exception that may occur: 238 | 239 | * `MissingConfiguration` is raised during the `get_api` call if the API configuration for a given alias was not registered. Or the default API has not been configured, while calling `get_api` without the alias parameter. 240 | * `MethodNotAvailableInAPIVersion` is raised if one tries to use a method from different API version. 241 | * `InvalidSearchConceptType` is raised when the wrong filters are provided to the `search` method. 242 | * `InvalidConceptType` is raised when the wrong concept types are provided to the `concepts` method. 243 | * `InvalidAgeUnit` is raised when the wrong age unit is provided in API v3 Connectors. 244 | 245 | 246 | # Contributing 247 | 248 | Arkadiusz Szydełko ([akszydelko](https://github.com/akszydelko)) and Paweł Iwaszko ([iwaszko](https://github.com/iwaszko)) are the creators and current maintainers of the Infermedica API Python client. 249 | 250 | Pull requests are always welcome. Before submitting a pull request, please ensure that your coding style follows PEP 8 and rules form `.editorconfig` file. Also note that the library if formatted according to `black` rules so please apply them. 251 | 252 | # Legal 253 | 254 | Copyright 2024 by [Infermedica](http://infermedica.com). 255 | 256 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 257 | 258 | http://www.apache.org/licenses/LICENSE-2.0 259 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.connectors.v2 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains API Connector classes for API v2 version. 8 | """ 9 | 10 | from typing import Optional, List, Dict, Any 11 | 12 | from .standard import APIv2Connector 13 | from . import models 14 | 15 | 16 | class ModelAPIv2Connector(APIv2Connector): 17 | """ 18 | High level class which handles requests to the Infermedica API, 19 | provides methods that operates on data models. 20 | """ 21 | 22 | def suggest( 23 | self, 24 | diagnosis_request: models.Diagnosis, 25 | max_results: Optional[int] = 8, 26 | **kwargs: Any 27 | ) -> List[Dict[str, str]]: 28 | """ 29 | Makes an API suggest request and returns a list of suggested evidence. 30 | See the docs: https://developer.infermedica.com/docs/suggest-related-concepts. 31 | 32 | :param diagnosis_request: Diagnosis request object 33 | :param max_results: (optional) Maximum number of results to return, default is 8 34 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 35 | 36 | :returns: A list of suggestions, dicts with 'id', 'name' and 'common_name' keys 37 | """ 38 | data = diagnosis_request.get_api_request() 39 | 40 | response = super().suggest( 41 | max_results=max_results, interview_id=diagnosis_request.interview_id, **data 42 | ) 43 | 44 | return response # TODO: Pack response into model class 45 | 46 | def red_flags( 47 | self, 48 | diagnosis_request: models.Diagnosis, 49 | max_results: Optional[int] = 8, 50 | **kwargs: Any 51 | ) -> models.RedFlagList: 52 | """ 53 | Makes an API request with provided diagnosis data and returns a list 54 | of evidence that may be related to potentially life-threatening 55 | conditions. 56 | See the docs: https://developer.infermedica.com/docs/red-flags. 57 | 58 | :param diagnosis_request: Diagnosis request object 59 | :param max_results: (optional) Maximum number of results to return, default is 8 60 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 61 | 62 | :returns: A list of RedFlag objects 63 | """ 64 | data = diagnosis_request.get_api_request() 65 | 66 | response = super().red_flags( 67 | max_results=max_results, interview_id=diagnosis_request.interview_id, **data 68 | ) 69 | 70 | return models.RedFlagList.from_json(response) 71 | 72 | def parse( 73 | self, 74 | text: str, 75 | include_tokens: Optional[bool] = False, 76 | interview_id: Optional[str] = None, 77 | **kwargs: Any 78 | ) -> models.ParseResults: 79 | """ 80 | Makes an parse API request with provided text and include_tokens parameter. 81 | Returns parse results with detailed list of mentions found in the text. 82 | See the docs: https://developer.infermedica.com/docs/nlp. 83 | 84 | :param text: Text to parse 85 | :param include_tokens: (optional) Switch to manipulate the include_tokens parameter 86 | :param interview_id: (optional) Unique interview id for diagnosis session 87 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 88 | 89 | :returns: A ParseResults object 90 | """ 91 | response = super().parse( 92 | text=text, 93 | include_tokens=include_tokens, 94 | interview_id=interview_id, 95 | **kwargs 96 | ) 97 | 98 | return models.ParseResults.from_json(response) 99 | 100 | def diagnosis( 101 | self, diagnosis_request: models.Diagnosis, **kwargs: Any 102 | ) -> models.Diagnosis: 103 | """ 104 | Makes a diagnosis API request with provided diagnosis data 105 | and returns diagnosis question with possible conditions. 106 | See the docs: https://developer.infermedica.com/docs/diagnosis. 107 | 108 | :param diagnosis_request: Diagnosis request object 109 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 110 | 111 | :returns: A Diagnosis object with api response 112 | """ 113 | data = diagnosis_request.get_api_request() 114 | 115 | response = super().diagnosis( 116 | interview_id=diagnosis_request.interview_id, **data, **kwargs 117 | ) 118 | diagnosis_request.update_from_api(response) 119 | 120 | return diagnosis_request 121 | 122 | def rationale( 123 | self, diagnosis_request: models.Diagnosis, **kwargs: Any 124 | ) -> models.RationaleResult: 125 | """ 126 | Makes an API request with provided diagnosis data and returns 127 | an explanation of why the given question has been selected by 128 | the reasoning engine. 129 | See the docs: https://developer.infermedica.com/docs/rationale. 130 | 131 | :param diagnosis_request: Diagnosis request object 132 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 133 | 134 | :returns: An instance of the RationaleResult 135 | """ 136 | data = diagnosis_request.get_api_request() 137 | 138 | response = super().rationale( 139 | interview_id=diagnosis_request.interview_id, **data, **kwargs 140 | ) 141 | 142 | return models.RationaleResult.from_json(response) 143 | 144 | def explain( 145 | self, diagnosis_request: models.Diagnosis, target_id, **kwargs: Any 146 | ) -> models.ExplainResults: 147 | """ 148 | Makes an explain API request with provided diagnosis data and target condition. 149 | Returns explain results with supporting and conflicting evidence. 150 | See the docs: https://developer.infermedica.com/docs/explain. 151 | 152 | :param diagnosis_request: Diagnosis request object 153 | :param target_id: Condition id for which explain shall be calculated 154 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 155 | 156 | :returns: A Diagnosis object with api response 157 | """ 158 | data = diagnosis_request.get_api_request() 159 | 160 | response = super().explain( 161 | target_id=target_id, 162 | interview_id=diagnosis_request.interview_id, 163 | **data, 164 | **kwargs 165 | ) 166 | 167 | return models.ExplainResults.from_json(response) 168 | 169 | def triage(self, diagnosis_request: models.Diagnosis, **kwargs: Any) -> Dict: 170 | """ 171 | Makes a triage API request with provided diagnosis data. 172 | Returns triage results dict. 173 | See the docs: https://developer.infermedica.com/docs/triage. 174 | 175 | :param diagnosis_request: Diagnosis request object 176 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 177 | 178 | :returns: A dict object with api response 179 | """ 180 | data = diagnosis_request.get_api_request() 181 | 182 | response = super().triage( 183 | interview_id=diagnosis_request.interview_id, **data, **kwargs 184 | ) 185 | 186 | return response # TODO: Pack response into model class 187 | 188 | def condition_details(self, condition_id: str, **kwargs: Any) -> models.Condition: 189 | """ 190 | Makes an API request and returns condition details object. 191 | See the docs: https://developer.infermedica.com/docs/medical-concepts#conditions. 192 | 193 | :param condition_id: Condition id 194 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 195 | 196 | :returns:A Condition object 197 | """ 198 | response = super().condition_details(condition_id=condition_id, **kwargs) 199 | 200 | return models.Condition.from_json(response) 201 | 202 | def condition_list(self, **kwargs: Any) -> models.ConditionList: 203 | """ 204 | Makes an API request and returns list of condition details objects. 205 | See the docs: https://developer.infermedica.com/docs/medical-concepts#conditions. 206 | 207 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 208 | 209 | :returns: A ConditionList list object with Condition objects 210 | """ 211 | response = super().condition_list(**kwargs) 212 | 213 | return models.ConditionList.from_json(response) 214 | 215 | def symptom_details(self, symptom_id: str, **kwargs: Any) -> models.Symptom: 216 | """ 217 | Makes an API request and returns symptom details object. 218 | See the docs: https://developer.infermedica.com/docs/medical-concepts#symptoms. 219 | 220 | :param symptom_id: Symptom id 221 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 222 | 223 | :returns: A Symptom object 224 | """ 225 | response = super().symptom_details(symptom_id=symptom_id, **kwargs) 226 | 227 | return models.Symptom.from_json(response) 228 | 229 | def symptom_list(self, **kwargs: Any) -> models.SymptomList: 230 | """ 231 | Makes an API request and returns list of symptom details objects. 232 | See the docs: https://developer.infermedica.com/docs/medical-concepts#symptoms. 233 | 234 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 235 | 236 | :returns: A SymptomList list object with Symptom objects 237 | """ 238 | response = super().symptom_list(**kwargs) 239 | 240 | return models.SymptomList.from_json(response) 241 | 242 | def risk_factor_details( 243 | self, risk_factor_id: str, **kwargs: Any 244 | ) -> models.RiskFactor: 245 | """ 246 | Makes an API request and returns risk factor details object. 247 | See the docs: https://developer.infermedica.com/docs/medical-concepts#risk-factors. 248 | 249 | :param risk_factor_id: Risk factor id 250 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 251 | 252 | :returns: A RiskFactor object 253 | """ 254 | response = super().risk_factor_details(risk_factor_id=risk_factor_id, **kwargs) 255 | 256 | return models.RiskFactor.from_json(response) 257 | 258 | def risk_factor_list(self, **kwargs: Any) -> models.RiskFactorList: 259 | """ 260 | Makes an API request and returns list of risk factors details objects. 261 | See the docs: https://developer.infermedica.com/docs/medical-concepts#risk-factors. 262 | 263 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 264 | 265 | :returns: A RiskFactorList list object with RiskFactor objects 266 | """ 267 | response = super().risk_factor_list(**kwargs) 268 | 269 | return models.RiskFactorList.from_json(response) 270 | 271 | def lab_test_details(self, lab_test_id: str, **kwargs: Any) -> models.LabTest: 272 | """ 273 | Makes an API request and returns lab_test details object. 274 | See the docs: https://developer.infermedica.com/docs/medical-concepts#lab-tests-and-lab-test-results. 275 | 276 | :param lab_test_id: Lab test id 277 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 278 | 279 | :returns: A LabTest object 280 | """ 281 | response = super().lab_test_details(lab_test_id=lab_test_id, **kwargs) 282 | 283 | return models.LabTest.from_json(response) 284 | 285 | def lab_test_list(self, **kwargs: Any) -> models.LabTestList: 286 | """ 287 | Makes an API request and returns list of lab_test details objects. 288 | See the docs: https://developer.infermedica.com/docs/medical-concepts#lab-tests-and-lab-test-results. 289 | 290 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIv2Connector` method 291 | 292 | :returns: A LabTestList list object with LabTest objects 293 | """ 294 | response = super().lab_test_list(**kwargs) 295 | 296 | return models.LabTestList.from_json(response) 297 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v2/standard.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.connectors.v2 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains API Connector classes for API v2 version. 8 | """ 9 | 10 | from typing import Optional, List, Dict, Any, Union 11 | 12 | from .basic import BasicAPIv2Connector 13 | from ..common import ( 14 | SearchConceptType, 15 | EvidenceList, 16 | ExtrasDict, 17 | ) 18 | from ... import exceptions 19 | 20 | 21 | # Types 22 | DiagnosticDict = Dict[str, Union[str, int, EvidenceList, ExtrasDict]] 23 | 24 | 25 | class APIv2Connector(BasicAPIv2Connector): 26 | def get_diagnostic_data_dict( 27 | self, 28 | evidence: EvidenceList, 29 | sex: str, 30 | age: int, 31 | extras: Optional[ExtrasDict] = None, 32 | ) -> DiagnosticDict: 33 | data = {"sex": sex, "age": age, "evidence": evidence} 34 | 35 | if extras: 36 | data["extras"] = extras 37 | 38 | return data 39 | 40 | def search( 41 | self, 42 | phrase: str, 43 | sex: Optional[str] = None, 44 | max_results: Optional[int] = 8, 45 | types: Optional[List[Union[SearchConceptType, str]]] = None, 46 | **kwargs: Any 47 | ) -> List[Dict[str, str]]: 48 | """ 49 | Makes an API search request and returns list of dicts containing keys: 'id', 'label' and 'type'. 50 | Each dict represent an evidence (symptom, lab test or risk factor). 51 | By default only symptoms are returned, to include other evidence types use filters. 52 | 53 | :param phrase: Phrase to look for 54 | :param sex: (optional) Sex of the patient 'female' or 'male' to narrow results 55 | :param max_results: (optional) Maximum number of results to return, default is 8 56 | :param types: (optional) List of search filters (enums SearchConceptType or str) to narrow the response 57 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv2Connector` method 58 | 59 | :returns: A List of dicts with 'id' and 'label' keys 60 | 61 | :raises: :class:`infermedica_api.exceptions.InvalidSearchConceptType` 62 | """ 63 | params = kwargs.pop("params", {}) 64 | params.update({"phrase": phrase, "max_results": max_results}) 65 | 66 | if sex: 67 | params["sex"] = sex 68 | 69 | if types: 70 | types_as_str_list = [ 71 | SearchConceptType.get_value(concept_type) for concept_type in types 72 | ] 73 | 74 | for concept_type in types_as_str_list: 75 | if not SearchConceptType.has_value(concept_type): 76 | raise exceptions.InvalidSearchConceptType(concept_type) 77 | 78 | params["type"] = types_as_str_list 79 | 80 | return super().search(params=params, **kwargs) 81 | 82 | def parse( 83 | self, 84 | text: str, 85 | include_tokens: Optional[bool] = False, 86 | interview_id: Optional[str] = None, 87 | **kwargs: Any 88 | ) -> Dict: 89 | """ 90 | Makes an parse API request with provided text and include_tokens parameter. 91 | Returns parse results with detailed list of mentions found in the text. 92 | See the docs: https://developer.infermedica.com/docs/nlp. 93 | 94 | :param text: Text to parse 95 | :param include_tokens: (optional) Switch to manipulate the include_tokens parameter 96 | :param interview_id: (optional) Unique interview id for diagnosis session 97 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIConnector` method 98 | 99 | :returns: A dict object with api response 100 | """ 101 | params = kwargs.pop("params", None) 102 | 103 | headers = kwargs.pop("headers", {}) 104 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 105 | 106 | data = kwargs.pop("data", {}) 107 | data.update({"text": text, "include_tokens": include_tokens}) 108 | 109 | return super().parse(data=data, params=params, headers=headers) 110 | 111 | def suggest( 112 | self, 113 | evidence: EvidenceList, 114 | sex: str, 115 | age: int, 116 | extras: Optional[ExtrasDict] = None, 117 | max_results: Optional[int] = 8, 118 | interview_id: Optional[str] = None, 119 | **kwargs: Any 120 | ) -> List[Dict[str, str]]: 121 | """ 122 | Makes an API suggest request and returns a list of suggested evidence. 123 | See the docs: https://developer.infermedica.com/docs/suggest-related-concepts. 124 | 125 | :param evidence: Diagnostic evidence list 126 | :param sex: Biological sex value, one of values 'female' or 'male' 127 | :param age: Age value 128 | :param extras: (optional) Dict with API extras 129 | :param max_results: (optional) Maximum number of results to return, default is 8 130 | :param interview_id: (optional) Unique interview id for diagnosis session 131 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIConnector` method 132 | 133 | :returns: A list of dicts with 'id', 'name' and 'common_name' keys 134 | """ 135 | params = kwargs.pop("params", {}) 136 | params.update({"max_results": max_results}) 137 | 138 | headers = kwargs.pop("headers", {}) 139 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 140 | 141 | data = self.get_diagnostic_data_dict( 142 | evidence=evidence, sex=sex, age=age, extras=extras 143 | ) 144 | 145 | return super().suggest(data=data, params=params, headers=headers) 146 | 147 | def red_flags( 148 | self, 149 | evidence: EvidenceList, 150 | sex: str, 151 | age: int, 152 | extras: Optional[ExtrasDict] = None, 153 | max_results: Optional[int] = 8, 154 | interview_id: Optional[str] = None, 155 | **kwargs: Any 156 | ) -> List[Dict[str, str]]: 157 | """ 158 | Makes an API request with provided diagnosis data and returns a list 159 | of evidence that may be related to potentially life-threatening 160 | conditions. 161 | See the docs: https://developer.infermedica.com/docs/red-flags. 162 | 163 | :param evidence: Diagnostic evidence list 164 | :param sex: Biological sex value, one of values 'female' or 'male' 165 | :param age: Age value 166 | :param extras: (optional) Dict with API extras 167 | :param max_results: (optional) Maximum number of results to return, default is 8 168 | :param interview_id: (optional) Unique interview id for diagnosis session 169 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIConnector` method 170 | 171 | :returns: A list of dicts with 'id', 'name' and 'common_name' keys 172 | """ 173 | params = kwargs.pop("params", {}) 174 | params.update({"max_results": max_results}) 175 | 176 | headers = kwargs.pop("headers", {}) 177 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 178 | 179 | data = self.get_diagnostic_data_dict( 180 | evidence=evidence, sex=sex, age=age, extras=extras 181 | ) 182 | 183 | return super().red_flags(data=data, params=params, headers=headers) 184 | 185 | def diagnosis( 186 | self, 187 | evidence: EvidenceList, 188 | sex: str, 189 | age: int, 190 | extras: Optional[ExtrasDict] = None, 191 | interview_id: Optional[str] = None, 192 | **kwargs: Any 193 | ) -> Dict: 194 | """ 195 | Makes a diagnosis API request with provided diagnosis data 196 | and returns diagnosis question with possible conditions. 197 | See the docs: https://developer.infermedica.com/docs/diagnosis. 198 | 199 | :param evidence: Diagnostic evidence list 200 | :param sex: Biological sex value, one of values 'female' or 'male' 201 | :param age: Age value 202 | :param extras: (optional) Dict with API extras 203 | :param interview_id: (optional) Unique interview id for diagnosis session 204 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIConnector` method 205 | 206 | :returns: A dict object with api response 207 | """ 208 | headers = kwargs.pop("headers", {}) 209 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 210 | 211 | data = self.get_diagnostic_data_dict( 212 | evidence=evidence, sex=sex, age=age, extras=extras 213 | ) 214 | 215 | return super().diagnosis(data=data, headers=headers, **kwargs) 216 | 217 | def rationale( 218 | self, 219 | evidence: EvidenceList, 220 | sex: str, 221 | age: int, 222 | extras: Optional[ExtrasDict] = None, 223 | interview_id: Optional[str] = None, 224 | **kwargs: Any 225 | ) -> Dict: 226 | """ 227 | Makes an API request with provided diagnosis data and returns 228 | an explanation of why the given question has been selected by 229 | the reasoning engine. 230 | See the docs: https://developer.infermedica.com/docs/rationale. 231 | 232 | :param evidence: Diagnostic evidence list 233 | :param sex: Biological sex value, one of values 'female' or 'male' 234 | :param age: Age value 235 | :param extras: (optional) Dict with API extras 236 | :param interview_id: (optional) Unique interview id for diagnosis session 237 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIConnector` method 238 | 239 | :returns: A dict object with api response 240 | """ 241 | headers = kwargs.pop("headers", {}) 242 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 243 | 244 | data = self.get_diagnostic_data_dict( 245 | evidence=evidence, sex=sex, age=age, extras=extras 246 | ) 247 | 248 | return super().rationale(data=data, headers=headers, **kwargs) 249 | 250 | def explain( 251 | self, 252 | target_id: str, 253 | evidence: EvidenceList, 254 | sex: str, 255 | age: Union[int, str], 256 | extras: Optional[ExtrasDict] = None, 257 | interview_id: Optional[str] = None, 258 | **kwargs: Any 259 | ) -> Dict: 260 | """ 261 | Makes an explain API request with provided diagnosis data and target condition. 262 | Returns explain results with supporting and conflicting evidence. 263 | See the docs: https://developer.infermedica.com/docs/explain. 264 | 265 | :param target_id: Condition id for which explain shall be calculated 266 | :param evidence: Diagnostic evidence list 267 | :param sex: Biological sex value, one of values 'female' or 'male' 268 | :param age: Age value 269 | :param extras: (optional) Dict with API extras 270 | :param interview_id: (optional) Unique interview id for diagnosis session 271 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 272 | 273 | :returns: A dict object with api response 274 | """ 275 | 276 | headers = kwargs.pop("headers", {}) 277 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 278 | 279 | data = self.get_diagnostic_data_dict( 280 | evidence=evidence, sex=sex, age=age, extras=extras 281 | ) 282 | data["target"] = target_id 283 | 284 | return super().explain( 285 | data=data, 286 | headers=headers, 287 | **kwargs, 288 | ) 289 | 290 | def triage( 291 | self, 292 | evidence: EvidenceList, 293 | sex: str, 294 | age: Union[int, str], 295 | extras: Optional[ExtrasDict] = None, 296 | interview_id: Optional[str] = None, 297 | **kwargs: Any 298 | ) -> Dict: 299 | """ 300 | Makes a triage API request with provided diagnosis data. 301 | Returns triage results dict. 302 | See the docs: https://developer.infermedica.com/docs/triage. 303 | 304 | :param evidence: Diagnostic evidence list 305 | :param sex: Biological sex value, one of values 'female' or 'male' 306 | :param age: Age value 307 | :param extras: (optional) Dict with API extras 308 | :param interview_id: (optional) Unique interview id for diagnosis session 309 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 310 | 311 | :returns: A dict object with api response 312 | """ 313 | headers = kwargs.pop("headers", {}) 314 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 315 | 316 | data = self.get_diagnostic_data_dict( 317 | evidence=evidence, sex=sex, age=age, extras=extras 318 | ) 319 | 320 | return super().triage(data=data, headers=headers, **kwargs) 321 | -------------------------------------------------------------------------------- /infermedica_api/connectors/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.connectors.common 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains base a set of API Connector classes responsible for making API requests. 8 | """ 9 | 10 | import json 11 | import platform 12 | from abc import ABC 13 | from enum import Enum 14 | from typing import Optional, Dict, Union, List, Any 15 | 16 | import requests 17 | 18 | from .. import ( 19 | __version__, 20 | exceptions, 21 | API_CONFIG, 22 | DEFAULT_API_VERSION, 23 | DEFAULT_API_ENDPOINT, 24 | ) 25 | 26 | ConditionDetails = Dict[str, Any] 27 | SymptomDetails = Dict[str, Any] 28 | RiskFactorDetails = Dict[str, Any] 29 | LabTestDetails = Dict[str, Any] 30 | ConceptDetails = Dict[str, Any] 31 | 32 | EvidenceList = List[Dict[str, str]] 33 | ExtrasDict = Dict[str, Union[bool, str]] 34 | 35 | 36 | class SearchConceptType(Enum): 37 | """Enum to hold search filter constants.""" 38 | 39 | SYMPTOM = "symptom" 40 | RISK_FACTOR = "risk_factor" 41 | LAB_TEST = "lab_test" 42 | 43 | @staticmethod 44 | def has_value(val: Union["SearchConceptType", str]) -> bool: 45 | if isinstance(val, SearchConceptType): 46 | return val in SearchConceptType 47 | return val in (item.value for item in SearchConceptType) 48 | 49 | @staticmethod 50 | def get_value(val: Union["SearchConceptType", str]) -> str: 51 | if isinstance(val, SearchConceptType): 52 | return val.value 53 | return val 54 | 55 | 56 | class BaseAPIConnector(ABC): 57 | """Low level class which handles requests to the Infermedica API, works with row objects.""" 58 | 59 | def __init__( 60 | self, 61 | app_id: str, 62 | app_key: str, 63 | endpoint: Optional[str] = DEFAULT_API_ENDPOINT, 64 | api_version: Optional[str] = DEFAULT_API_VERSION, 65 | model: Optional[str] = None, 66 | dev_mode: Optional[bool] = None, 67 | default_headers: Optional[Dict] = None, 68 | api_definitions: Optional[Dict] = None, 69 | ) -> None: 70 | """ 71 | Initialize API connector. 72 | 73 | :param app_id: Infermedica API App Id 74 | :param app_key: Infermedica API App Key 75 | :param endpoint: (optional) Base API URL, default is 'https://api.infermedica.com/' 76 | :param api_version: (optional) API version, default is 'v3' 77 | :param model: (optional) API model to be used 78 | :param dev_mode: (optional) Flag that indicates request is made in testing environment 79 | and does not provide real patient case 80 | :param default_headers: (optional) Dict with default headers that will be send with every request 81 | :param api_definitions: (optional) Dict with custom API method definitions 82 | 83 | :raises: infermedica_api.exceptions.MissingAPIDefinition 84 | """ 85 | self.app_id = app_id 86 | self.app_key = app_key 87 | self.endpoint = endpoint 88 | self.api_version = api_version 89 | self.default_headers = self.__calculate_default_headers( 90 | model=model, dev_mode=dev_mode, default_headers=default_headers 91 | ) 92 | 93 | if api_definitions and self.api_version in api_definitions: 94 | self.api_methods = api_definitions[self.api_version]["methods"] 95 | elif self.api_version in API_CONFIG: 96 | self.api_methods = API_CONFIG[self.api_version]["methods"] 97 | else: 98 | raise exceptions.MissingAPIDefinition(self.api_version) 99 | 100 | def __calculate_default_headers( 101 | self, 102 | model: Optional[str] = None, 103 | dev_mode: Optional[bool] = None, 104 | default_headers: Optional[Dict] = None, 105 | ) -> Dict: 106 | headers = default_headers or {} 107 | 108 | if model: 109 | headers["Model"] = model 110 | 111 | if dev_mode: 112 | headers["Dev-Mode"] = "true" 113 | 114 | return headers 115 | 116 | def __get_headers(self, passed_headers: Dict) -> Dict: 117 | """Returns default HTTP headers.""" 118 | 119 | # User-Agent for HTTP request 120 | library_details = [ 121 | f"requests {requests.__version__}", 122 | f"python {platform.python_version()}", 123 | f"connector {self.__class__.__name__}", 124 | ] 125 | library_details = "; ".join(library_details) 126 | user_agent = f"Infermedica-API-Python {__version__} ({library_details})" 127 | 128 | headers = { 129 | "Accept": "application/json", 130 | "User-Agent": user_agent, 131 | "App-Id": self.app_id, 132 | "App-Key": self.app_key, 133 | } 134 | headers.update(self.default_headers) 135 | headers.update(passed_headers) # Make sure passed headers take precedence 136 | return headers 137 | 138 | def get_interview_id_headers(self, interview_id: Optional[str] = None) -> Dict: 139 | headers = {} 140 | if interview_id: 141 | headers["Interview-Id"] = interview_id 142 | 143 | return headers 144 | 145 | def __get_url(self, method: str) -> str: 146 | return self.endpoint + self.api_version + method 147 | 148 | def _get_method(self, name: str) -> str: 149 | try: 150 | return self.api_methods[name] 151 | except KeyError: 152 | raise exceptions.MethodNotAvailableInAPIVersion(self.api_version, name) 153 | 154 | def __api_call(self, url: str, method: str, **kwargs: Any) -> Union[Dict, List]: 155 | kwargs["headers"] = self.__get_headers(kwargs["headers"] or {}) 156 | 157 | response = requests.request(method, url, **kwargs) 158 | 159 | return self.__handle_response(response) 160 | 161 | def __handle_response(self, response: requests.Response) -> Union[Dict, List]: 162 | """ 163 | Validates HTTP response, if response is correct decode json data and returns dict object. 164 | If response is not correct raise appropriate exception. 165 | 166 | :returns: dict or list with response data 167 | :raises: 168 | infermedica_api.exceptions.BadRequest, 169 | infermedica_api.exceptions.UnauthorizedAccess, 170 | infermedica_api.exceptions.ForbiddenAccess, 171 | infermedica_api.exceptions.ResourceNotFound, 172 | infermedica_api.exceptions.MethodNotAllowed, 173 | infermedica_api.exceptions.ServerError, 174 | infermedica_api.exceptions.ConnectionError 175 | """ 176 | status = response.status_code 177 | content = response.content.decode("utf-8") 178 | 179 | if 200 <= status <= 299: 180 | return json.loads(content) if content else {} 181 | elif status == 400: 182 | raise exceptions.BadRequest(response, content) 183 | elif status == 401: 184 | raise exceptions.UnauthorizedAccess(response, content) 185 | elif status == 403: 186 | raise exceptions.ForbiddenAccess(response, content) 187 | elif status == 404: 188 | raise exceptions.ResourceNotFound(response, content) 189 | elif status == 405: 190 | raise exceptions.MethodNotAllowed(response, content) 191 | elif 500 <= status <= 599: 192 | raise exceptions.ServerError(response, content) 193 | else: 194 | raise exceptions.ConnectionError(response, content) 195 | 196 | def call_api_get( 197 | self, method: str, params: Optional[Dict] = None, headers: Optional[Dict] = None 198 | ) -> Union[Dict, List]: 199 | """Wrapper for a GET API call.""" 200 | return self.__api_call( 201 | self.__get_url(method), "GET", headers=headers, params=params 202 | ) 203 | 204 | def call_api_post( 205 | self, 206 | method: str, 207 | data: Dict, 208 | params: Optional[Dict] = None, 209 | headers: Optional[Dict] = None, 210 | ) -> Union[Dict, List]: 211 | """Wrapper for a GET API call.""" 212 | return self.__api_call( 213 | self.__get_url(method), "POST", headers=headers, json=data, params=params 214 | ) 215 | 216 | 217 | # Common API functionalities 218 | 219 | 220 | class BasicAPIInfoMixin(ABC): 221 | def info( 222 | self, params: Optional[Dict] = None, headers: Optional[Dict] = None 223 | ) -> Dict: 224 | """ 225 | Makes an API request and returns basic API information. 226 | See the docs: https://developer.infermedica.com/docs/v3/basics#info-endpoint. 227 | 228 | :param params: (optional) URL query params 229 | :param headers: (optional) HTTP request headers 230 | 231 | :returns: A dict object with api response 232 | """ 233 | method = self._get_method("info") 234 | 235 | return self.call_api_get(method=method, params=params, headers=headers) 236 | 237 | 238 | class BasicAPISearchMixin(ABC): 239 | def search( 240 | self, params: Optional[Dict] = None, headers: Optional[Dict] = None 241 | ) -> List[Dict[str, str]]: 242 | """ 243 | Makes an API search request and returns list of dicts containing keys: 'id', 'label' and 'type'. 244 | Each dict represent an evidence (symptom, lab test or risk factor). 245 | By default only symptoms are returned, to include other evidence types use filters. 246 | 247 | :param params: (optional) URL query params 248 | :param headers: (optional) HTTP request headers 249 | 250 | :returns: A List of dicts with 'id' and 'label' keys 251 | """ 252 | method = self._get_method("search") 253 | 254 | return self.call_api_get(method=method, params=params, headers=headers) 255 | 256 | 257 | class BasicAPIParseMixin(ABC): 258 | def parse( 259 | self, data: Dict, params: Optional[Dict] = None, headers: Optional[Dict] = None 260 | ) -> Dict: 261 | """ 262 | Makes an parse API request with provided text and include_tokens parameter. 263 | Returns parse results with detailed list of mentions found in the text. 264 | See the docs: https://developer.infermedica.com/docs/v3/nlp. 265 | 266 | :param data: Request data 267 | :param params: (optional) URL query params 268 | :param headers: (optional) HTTP request headers 269 | 270 | :returns: A dict object with api response 271 | """ 272 | method = self._get_method("parse") 273 | 274 | return self.call_api_post( 275 | method=method, data=data, params=params, headers=headers 276 | ) 277 | 278 | 279 | class BasicAPISuggestMixin(ABC): 280 | def suggest( 281 | self, data: Dict, params: Optional[Dict] = None, headers: Optional[Dict] = None 282 | ) -> List[Dict[str, str]]: 283 | """ 284 | Makes an API suggest request and returns a list of suggested evidence. 285 | See the docs: https://developer.infermedica.com/docs/v3/suggest-related-concepts. 286 | 287 | :param data: Request data 288 | :param params: (optional) URL query params 289 | :param headers: (optional) HTTP request headers 290 | 291 | :returns: A list of dicts with 'id', 'name' and 'common_name' keys 292 | """ 293 | method = self._get_method("suggest") 294 | 295 | return self.call_api_post( 296 | method=method, data=data, params=params, headers=headers 297 | ) 298 | 299 | 300 | class BasicAPIDiagnosisMixin(ABC): 301 | def diagnosis( 302 | self, data: Dict, params: Optional[Dict] = None, headers: Optional[Dict] = None 303 | ) -> Dict: 304 | """ 305 | Makes a diagnosis API request with provided diagnosis data 306 | and returns diagnosis question with possible conditions. 307 | See the docs: https://developer.infermedica.com/docs/v3/diagnosis. 308 | 309 | :param data: Request data 310 | :param params: (optional) URL query params 311 | :param headers: (optional) HTTP request headers 312 | 313 | :returns: A dict object with api response 314 | """ 315 | method = self._get_method("diagnosis") 316 | 317 | return self.call_api_post( 318 | method=method, data=data, params=params, headers=headers 319 | ) 320 | 321 | 322 | class BasicAPIRationaleMixin(ABC): 323 | def rationale( 324 | self, data: Dict, params: Optional[Dict] = None, headers: Optional[Dict] = None 325 | ) -> Dict: 326 | """ 327 | Makes an API request with provided diagnosis data and returns 328 | an explanation of why the given question has been selected by 329 | the reasoning engine. 330 | See the docs: https://developer.infermedica.com/docs/v3/rationale. 331 | 332 | :param data: Request data 333 | :param params: (optional) URL query params 334 | :param headers: (optional) HTTP request headers 335 | 336 | :returns: A dict object with api response 337 | """ 338 | method = self._get_method("rationale") 339 | 340 | return self.call_api_post( 341 | method=method, data=data, params=params, headers=headers 342 | ) 343 | 344 | 345 | class BasicAPIExplainMixin(ABC): 346 | def explain( 347 | self, data: Dict, params: Optional[Dict] = None, headers: Optional[Dict] = None 348 | ) -> Dict: 349 | """ 350 | Makes an explain API request with provided diagnosis data and target condition. 351 | Returns explain results with supporting and conflicting evidence. 352 | See the docs: https://developer.infermedica.com/docs/v3/explain. 353 | 354 | :param data: Request data 355 | :param params: (optional) URL query params 356 | :param headers: (optional) HTTP request headers 357 | 358 | :returns: A dict object with api response 359 | """ 360 | method = self._get_method("explain") 361 | 362 | return self.call_api_post( 363 | method=method, data=data, params=params, headers=headers 364 | ) 365 | 366 | 367 | class BasicAPITriageMixin(ABC): 368 | def triage( 369 | self, data: Dict, params: Optional[Dict] = None, headers: Optional[Dict] = None 370 | ) -> Dict: 371 | """ 372 | Makes a triage API request with provided diagnosis data. 373 | Returns triage results dict. 374 | See the docs: https://developer.infermedica.com/docs/v3/triage. 375 | 376 | :param data: Request data 377 | :param params: (optional) URL query params 378 | :param headers: (optional) HTTP request headers 379 | 380 | :returns: A dict object with api response 381 | """ 382 | method = self._get_method("triage") 383 | 384 | return self.call_api_post( 385 | method=method, data=data, params=params, headers=headers 386 | ) 387 | 388 | 389 | class BasicAPIConditionMixin(ABC): 390 | def condition_details( 391 | self, 392 | condition_id: str, 393 | params: Optional[Dict] = None, 394 | headers: Optional[Dict] = None, 395 | ) -> ConditionDetails: 396 | """ 397 | Makes an API request and returns condition details object. 398 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#conditions. 399 | 400 | :param condition_id: Condition id 401 | :param params: (optional) URL query params 402 | :param headers: (optional) HTTP request headers 403 | 404 | :returns: A dict object with condition details 405 | """ 406 | method = self._get_method("condition_details") 407 | method = method.format(**{"id": condition_id}) 408 | 409 | return self.call_api_get(method=method, params=params, headers=headers) 410 | 411 | def condition_list( 412 | self, params: Optional[Dict] = None, headers: Optional[Dict] = None 413 | ) -> List[ConditionDetails]: 414 | """ 415 | Makes an API request and returns list of condition details objects. 416 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#conditions. 417 | 418 | :param params: (optional) URL query params 419 | :param headers: (optional) HTTP request headers 420 | 421 | :returns: A list of dict objects with condition details 422 | """ 423 | method = self._get_method("conditions") 424 | 425 | return self.call_api_get(method=method, params=params, headers=headers) 426 | 427 | 428 | class BasicAPISymptomMixin(ABC): 429 | def symptom_details( 430 | self, 431 | symptom_id: str, 432 | params: Optional[Dict] = None, 433 | headers: Optional[Dict] = None, 434 | ) -> SymptomDetails: 435 | """ 436 | Makes an API request and returns symptom details object. 437 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#symptoms. 438 | 439 | :param symptom_id: Symptom id 440 | :param params: (optional) URL query params 441 | :param headers: (optional) HTTP request headers 442 | 443 | :returns: A dict object with symptom details 444 | """ 445 | method = self._get_method("symptom_details") 446 | method = method.format(**{"id": symptom_id}) 447 | 448 | return self.call_api_get(method=method, params=params, headers=headers) 449 | 450 | def symptom_list( 451 | self, params: Optional[Dict] = None, headers: Optional[Dict] = None 452 | ) -> List[SymptomDetails]: 453 | """ 454 | Makes an API request and returns list of symptom details objects. 455 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#symptoms. 456 | 457 | :param params: (optional) URL query params 458 | :param headers: (optional) HTTP request headers 459 | 460 | :returns: A list of dict objects with symptom details 461 | """ 462 | method = self._get_method("symptoms") 463 | 464 | return self.call_api_get(method=method, params=params, headers=headers) 465 | 466 | 467 | class BasicAPIRiskFactorMixin(ABC): 468 | def risk_factor_details( 469 | self, 470 | risk_factor_id: str, 471 | params: Optional[Dict] = None, 472 | headers: Optional[Dict] = None, 473 | ) -> RiskFactorDetails: 474 | """ 475 | Makes an API request and returns risk factor details object. 476 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#risk-factors. 477 | 478 | :param risk_factor_id: risk factor id 479 | :param params: (optional) URL query params 480 | :param headers: (optional) HTTP request headers 481 | 482 | :returns: A dict object with risk factor details 483 | """ 484 | method = self._get_method("risk_factor_details") 485 | method = method.format(**{"id": risk_factor_id}) 486 | 487 | return self.call_api_get(method=method, params=params, headers=headers) 488 | 489 | def risk_factor_list( 490 | self, params: Optional[Dict] = None, headers: Optional[Dict] = None 491 | ) -> List[RiskFactorDetails]: 492 | """ 493 | Makes an API request and returns list of risk factors details objects. 494 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#risk-factors. 495 | 496 | :param params: (optional) URL query params 497 | :param headers: (optional) HTTP request headers 498 | 499 | :returns: A list of dict objects with risk factor details 500 | """ 501 | method = self._get_method("risk_factors") 502 | 503 | return self.call_api_get(method=method, params=params, headers=headers) 504 | 505 | 506 | class BasicAPILabTestMixin(ABC): 507 | def lab_test_details( 508 | self, 509 | lab_test_id: str, 510 | params: Optional[Dict] = None, 511 | headers: Optional[Dict] = None, 512 | ) -> LabTestDetails: 513 | """ 514 | Makes an API request and returns lab test details object. 515 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#lab-tests-and-lab-test-results. 516 | 517 | :param lab_test_id: LabTest id 518 | :param params: (optional) URL query params 519 | :param headers: (optional) HTTP request headers 520 | 521 | :returns: A dict object with lab test details 522 | """ 523 | method = self._get_method("lab_test_details") 524 | method = method.format(**{"id": lab_test_id}) 525 | 526 | return self.call_api_get(method=method, params=params, headers=headers) 527 | 528 | def lab_test_list( 529 | self, params: Optional[Dict] = None, headers: Optional[Dict] = None 530 | ) -> List[LabTestDetails]: 531 | """ 532 | Makes an API request and returns list of lab test details objects. 533 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#lab-tests-and-lab-test-results. 534 | 535 | :param params: (optional) URL query params 536 | :param headers: (optional) HTTP request headers 537 | 538 | :returns: A list of dict objects with lab test details 539 | """ 540 | method = self._get_method("lab_tests") 541 | 542 | return self.call_api_get(method=method, params=params, headers=headers) 543 | 544 | 545 | class BasicAPICommonMethodsMixin( 546 | BasicAPIInfoMixin, 547 | BasicAPISearchMixin, 548 | BasicAPIParseMixin, 549 | BasicAPISuggestMixin, 550 | BasicAPIDiagnosisMixin, 551 | BasicAPIRationaleMixin, 552 | BasicAPIExplainMixin, 553 | BasicAPITriageMixin, 554 | BasicAPIConditionMixin, 555 | BasicAPISymptomMixin, 556 | BasicAPIRiskFactorMixin, 557 | BasicAPILabTestMixin, 558 | ABC, 559 | ): 560 | pass 561 | -------------------------------------------------------------------------------- /infermedica_api/connectors/v3/standard.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | infermedica_api.connectors.v3 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module contains API Connector classes for API v3 version. 8 | """ 9 | 10 | from enum import Enum 11 | from typing import Optional, List, Dict, Union, Any 12 | 13 | from .basic import BasicAPIv3Connector 14 | from ..common import ( 15 | SearchConceptType, 16 | ConceptDetails, 17 | ConditionDetails, 18 | SymptomDetails, 19 | RiskFactorDetails, 20 | LabTestDetails, 21 | EvidenceList, 22 | ExtrasDict, 23 | ) 24 | from ... import exceptions 25 | 26 | 27 | # Types 28 | AgeDict = Dict[str, Union[int, str]] 29 | DiagnosticDict = Dict[str, Union[str, AgeDict, EvidenceList, ExtrasDict]] 30 | 31 | 32 | class ConceptType(Enum): 33 | """Enum to hold search filter constants.""" 34 | 35 | CONDITION = "condition" 36 | SYMPTOM = "symptom" 37 | RISK_FACTOR = "risk_factor" 38 | LAB_TEST = "lab_test" 39 | 40 | @staticmethod 41 | def has_value(val: Union["ConceptType", str]) -> bool: 42 | if isinstance(val, ConceptType): 43 | return val in ConceptType 44 | return val in (item.value for item in ConceptType) 45 | 46 | 47 | class APIv3Connector(BasicAPIv3Connector): 48 | """ 49 | Intermediate level class which handles requests to the Infermedica API, 50 | provides methods with detailed parameters, but still works on simple data structures. 51 | """ 52 | 53 | def get_age_object(self, age: int, age_unit: Optional[str] = None) -> AgeDict: 54 | """ 55 | Prepare age object to sent in API request URL query. 56 | 57 | :param age: Age value 58 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 59 | 60 | :returns: A dict object with age object accepted by the API 61 | 62 | :raises: :class:`infermedica_api.exceptions.InvalidAgeUnit` 63 | """ 64 | age_obj = {"value": age} 65 | 66 | if age_unit in ( 67 | "year", 68 | "month", 69 | ): 70 | age_obj["unit"] = age_unit 71 | elif age_unit is not None: 72 | raise exceptions.InvalidAgeUnit(age_unit) 73 | 74 | return age_obj 75 | 76 | def get_age_query_params( 77 | self, age: int, age_unit: Optional[str] = None 78 | ) -> Dict[str, Union[int, str]]: 79 | """ 80 | Prepare age object to sent in API request URL query. 81 | 82 | :param age: Age value 83 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 84 | 85 | :returns: A dict object with age URL query params accepted by the API 86 | 87 | :raises: :class:`infermedica_api.exceptions.InvalidAgeUnit` 88 | """ 89 | age_obj = self.get_age_object(age=age, age_unit=age_unit) 90 | 91 | return {f"age.{key}": value for key, value in age_obj.items()} 92 | 93 | def get_diagnostic_data_dict( 94 | self, 95 | evidence: EvidenceList, 96 | sex: str, 97 | age: int, 98 | age_unit: Optional[str] = None, 99 | extras: Optional[ExtrasDict] = None, 100 | ) -> DiagnosticDict: 101 | data = { 102 | "sex": sex, 103 | "age": self.get_age_object(age=age, age_unit=age_unit), 104 | "evidence": evidence, 105 | } 106 | 107 | if extras: 108 | data["extras"] = extras 109 | 110 | return data 111 | 112 | def search( 113 | self, 114 | phrase: str, 115 | age: int, 116 | age_unit: Optional[str] = None, 117 | sex: Optional[str] = None, 118 | max_results: Optional[int] = 8, 119 | types: Optional[List[Union[SearchConceptType, str]]] = None, 120 | interview_id: Optional[str] = None, 121 | **kwargs: Any, 122 | ) -> List[Dict[str, str]]: 123 | """ 124 | Makes an API search request and returns list of dicts containing keys: 'id', 'label' and 'type'. 125 | Each dict represent an evidence (symptom, lab test or risk factor). 126 | By default only symptoms are returned, to include other evidence types use filters. 127 | 128 | :param phrase: Phrase to look for 129 | :param age: Age value 130 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 131 | :param sex: (optional) Sex of the patient 'female' or 'male' to narrow results 132 | :param max_results: (optional) Maximum number of results to return, default is 8 133 | :param types: (optional) List of search concept types (enums SearchConceptType or str) to narrow the response 134 | :param interview_id: (optional) Unique interview id for diagnosis session 135 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 136 | 137 | :returns: A List of dicts with 'id' and 'label' keys 138 | 139 | :raises: :class:`infermedica_api.exceptions.InvalidSearchConceptType` 140 | """ 141 | headers = kwargs.pop("headers", {}) 142 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 143 | 144 | params = kwargs.pop("params", {}) 145 | params.update(self.get_age_query_params(age=age, age_unit=age_unit)) 146 | params.update({"phrase": phrase, "max_results": max_results}) 147 | 148 | if sex: 149 | params["sex"] = sex 150 | 151 | if types: 152 | types_as_str_list = [ 153 | SearchConceptType.get_value(concept_type) for concept_type in types 154 | ] 155 | 156 | for concept_type in types_as_str_list: 157 | if not SearchConceptType.has_value(concept_type): 158 | raise exceptions.InvalidSearchConceptType(concept_type) 159 | 160 | params["types"] = ",".join(types_as_str_list) 161 | 162 | return super().search(params=params, headers=headers) 163 | 164 | def parse( 165 | self, 166 | text: str, 167 | age: int, 168 | age_unit: Optional[str] = None, 169 | include_tokens: Optional[bool] = False, 170 | interview_id: Optional[str] = None, 171 | sex: Optional[str] = None, 172 | context: Optional[List[str]] = None, 173 | **kwargs: Any, 174 | ) -> Dict: 175 | """ 176 | Makes a parse API request with provided text and include_tokens parameter. 177 | Returns parse results with detailed list of mentions found in the text. 178 | See the docs: https://developer.infermedica.com/docs/v3/nlp. 179 | 180 | :param text: Text to parse 181 | :param age: Age value 182 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 183 | :param include_tokens: (optional) Switch to manipulate the include_tokens parameter 184 | :param interview_id: (optional) Unique interview id for diagnosis session 185 | :param sex: (optional) Sex of the patient 'female' or 'male' to narrow results 186 | :param context: (optional) List of ids of other present evidences, which can be used to contextualize parsed text 187 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 188 | 189 | :returns: A dict object with api response 190 | """ 191 | headers = kwargs.pop("headers", {}) 192 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 193 | 194 | data = { 195 | "text": text, 196 | "age": self.get_age_object(age=age, age_unit=age_unit), 197 | "sex": sex, 198 | "context": context, 199 | "include_tokens": include_tokens, 200 | } 201 | 202 | return super().parse(data=data, headers=headers, **kwargs) 203 | 204 | def suggest( 205 | self, 206 | evidence: EvidenceList, 207 | sex: str, 208 | age: int, 209 | age_unit: Optional[str] = None, 210 | extras: Optional[ExtrasDict] = None, 211 | suggest_method: Optional[str] = "symptoms", 212 | max_results: Optional[int] = 8, 213 | interview_id: Optional[str] = None, 214 | **kwargs: Any, 215 | ) -> List[Dict[str, str]]: 216 | """ 217 | Makes an API suggest request and returns a list of suggested evidence. 218 | See the docs: https://developer.infermedica.com/docs/v3/suggest-related-concepts. 219 | 220 | :param evidence: Diagnostic evidence list 221 | :param sex: Biological sex value, one of values 'female' or 'male' 222 | :param age: Age value 223 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 224 | :param extras: (optional) Dict with API extras 225 | :param suggest_method: (optional) Suggest method to be used, 226 | one of values 'symptoms' (default), 'risk_factors', 'red_flags' 227 | :param max_results: (optional) Maximum number of results to return, default is 8 228 | :param interview_id: (optional) Unique interview id for diagnosis session 229 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIConnector` method 230 | 231 | :returns: A list of dicts with 'id', 'name' and 'common_name' keys 232 | """ 233 | params = kwargs.pop("params", {}) 234 | params.update({"max_results": max_results}) 235 | 236 | headers = kwargs.pop("headers", {}) 237 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 238 | 239 | data = self.get_diagnostic_data_dict( 240 | evidence=evidence, sex=sex, age=age, age_unit=age_unit, extras=extras 241 | ) 242 | if suggest_method: 243 | data["suggest_method"] = suggest_method 244 | 245 | return super().suggest(data=data, params=params, headers=headers) 246 | 247 | def diagnosis( 248 | self, 249 | evidence: EvidenceList, 250 | sex: str, 251 | age: int, 252 | age_unit: Optional[str] = None, 253 | extras: Optional[ExtrasDict] = None, 254 | interview_id: Optional[str] = None, 255 | **kwargs: Any, 256 | ) -> Dict: 257 | """ 258 | Makes a diagnosis API request with provided diagnosis data 259 | and returns diagnosis question with possible conditions. 260 | See the docs: https://developer.infermedica.com/docs/v3/diagnosis. 261 | 262 | :param evidence: Diagnostic evidence list 263 | :param sex: Biological sex value, one of values 'female' or 'male' 264 | :param age: Age value 265 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 266 | :param extras: (optional) Dict with API extras 267 | :param interview_id: (optional) Unique interview id for diagnosis session 268 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIConnector` method 269 | 270 | :returns: A dict object with api response 271 | """ 272 | headers = kwargs.pop("headers", {}) 273 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 274 | 275 | data = self.get_diagnostic_data_dict( 276 | evidence=evidence, sex=sex, age=age, age_unit=age_unit, extras=extras 277 | ) 278 | 279 | return super().diagnosis(data=data, headers=headers, **kwargs) 280 | 281 | def rationale( 282 | self, 283 | evidence: EvidenceList, 284 | sex: str, 285 | age: int, 286 | age_unit: Optional[str] = None, 287 | extras: Optional[ExtrasDict] = None, 288 | interview_id: Optional[str] = None, 289 | **kwargs: Any, 290 | ) -> Dict: 291 | """ 292 | Makes an API request with provided diagnosis data and returns 293 | an explanation of why the given question has been selected by 294 | the reasoning engine. 295 | See the docs: https://developer.infermedica.com/docs/v3/rationale. 296 | 297 | :param evidence: Diagnostic evidence list 298 | :param sex: Biological sex value, one of values 'female' or 'male' 299 | :param age: Age value 300 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 301 | :param extras: (optional) Dict with API extras 302 | :param interview_id: (optional) Unique interview id for diagnosis session 303 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`APIConnector` method 304 | 305 | :returns: A dict object with api response 306 | """ 307 | headers = kwargs.pop("headers", {}) 308 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 309 | 310 | data = self.get_diagnostic_data_dict( 311 | evidence=evidence, sex=sex, age=age, age_unit=age_unit, extras=extras 312 | ) 313 | 314 | return super().rationale(data=data, headers=headers, **kwargs) 315 | 316 | def explain( 317 | self, 318 | target_id: str, 319 | evidence: EvidenceList, 320 | sex: str, 321 | age: int, 322 | age_unit: Optional[str] = None, 323 | extras: Optional[ExtrasDict] = None, 324 | interview_id: Optional[str] = None, 325 | **kwargs: Any, 326 | ) -> Dict: 327 | """ 328 | Makes an explain API request with provided diagnosis data and target condition. 329 | Returns explain results with supporting and conflicting evidence. 330 | See the docs: https://developer.infermedica.com/docs/v3/explain. 331 | 332 | :param target_id: Condition id for which explain shall be calculated 333 | :param evidence: Diagnostic evidence list 334 | :param sex: Biological sex value, one of values 'female' or 'male' 335 | :param age: Age value 336 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 337 | :param extras: (optional) Dict with API extras 338 | :param interview_id: (optional) Unique interview id for diagnosis session 339 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 340 | 341 | :returns: A dict object with api response 342 | """ 343 | 344 | headers = kwargs.pop("headers", {}) 345 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 346 | 347 | data = self.get_diagnostic_data_dict( 348 | evidence=evidence, sex=sex, age=age, age_unit=age_unit, extras=extras 349 | ) 350 | data["target"] = target_id 351 | 352 | return super().explain( 353 | data=data, 354 | headers=headers, 355 | **kwargs, 356 | ) 357 | 358 | def triage( 359 | self, 360 | evidence: EvidenceList, 361 | sex: str, 362 | age: int, 363 | age_unit: Optional[str] = None, 364 | extras: Optional[ExtrasDict] = None, 365 | interview_id: Optional[str] = None, 366 | **kwargs: Any, 367 | ) -> Dict: 368 | """ 369 | Makes a triage API request with provided diagnosis data. 370 | Returns triage results dict. 371 | See the docs: https://developer.infermedica.com/docs/v3/triage. 372 | 373 | :param evidence: Diagnostic evidence list 374 | :param sex: Biological sex value, one of values 'female' or 'male' 375 | :param age: Age value 376 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 377 | :param extras: (optional) Dict with API extras 378 | :param interview_id: (optional) Unique interview id for diagnosis session 379 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 380 | 381 | :returns: A dict object with api response 382 | """ 383 | headers = kwargs.pop("headers", {}) 384 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 385 | 386 | data = self.get_diagnostic_data_dict( 387 | evidence=evidence, sex=sex, age=age, age_unit=age_unit, extras=extras 388 | ) 389 | 390 | return super().triage(data=data, headers=headers, **kwargs) 391 | 392 | def specialist_recommender( 393 | self, 394 | evidence: List, 395 | sex: str, 396 | age: int, 397 | age_unit: Optional[str] = None, 398 | extras: Optional[ExtrasDict] = None, 399 | interview_id: Optional[str] = None, 400 | **kwargs: Any, 401 | ) -> Dict: 402 | """ 403 | Makes a specialist recommendation API request with provided diagnosis data. 404 | See the docs: https://developer.infermedica.com/docs/v3/specialist-recommender. 405 | 406 | :param evidence: Diagnostic evidence list 407 | :param sex: Biological sex value, one of values 'female' or 'male' 408 | :param age: Age value 409 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 410 | :param extras: (optional) Dict with API extras 411 | :param interview_id: (optional) Unique interview id for diagnosis session 412 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 413 | 414 | :returns: A dict object with api response 415 | """ 416 | headers = kwargs.pop("headers", {}) 417 | headers.update(self.get_interview_id_headers(interview_id=interview_id)) 418 | 419 | data = kwargs.pop("data", {}) 420 | data.update( 421 | self.get_diagnostic_data_dict( 422 | evidence=evidence, sex=sex, age=age, age_unit=age_unit, extras=extras 423 | ) 424 | ) 425 | 426 | return super().specialist_recommender(data=data, headers=headers, **kwargs) 427 | 428 | def condition_details( 429 | self, condition_id: str, age: int, age_unit: Optional[str] = None, **kwargs 430 | ) -> ConditionDetails: 431 | """ 432 | Makes an API request and returns condition details object. 433 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#conditions. 434 | 435 | :param condition_id: Condition id 436 | :param age: Age value 437 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 438 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 439 | 440 | :returns: A dict object with condition details 441 | """ 442 | params = kwargs.pop("params", {}) 443 | params.update(self.get_age_query_params(age=age, age_unit=age_unit)) 444 | 445 | return super().condition_details( 446 | condition_id=condition_id, params=params, **kwargs 447 | ) 448 | 449 | def condition_list( 450 | self, age: int, age_unit: Optional[str] = None, **kwargs 451 | ) -> List[ConditionDetails]: 452 | """ 453 | Makes an API request and returns list of condition details objects. 454 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#conditions. 455 | 456 | :param age: Age value 457 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 458 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 459 | 460 | :returns: A list of dict objects with condition details 461 | """ 462 | params = kwargs.pop("params", {}) 463 | params.update(self.get_age_query_params(age=age, age_unit=age_unit)) 464 | 465 | return super().condition_list(params=params, **kwargs) 466 | 467 | def symptom_details( 468 | self, symptom_id: str, age: int, age_unit: Optional[str] = None, **kwargs 469 | ) -> SymptomDetails: 470 | """ 471 | Makes an API request and returns symptom details object. 472 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#symptoms. 473 | 474 | :param symptom_id: Symptom id 475 | :param age: Age value 476 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 477 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 478 | 479 | :returns: A dict object with symptom details 480 | """ 481 | params = kwargs.pop("params", {}) 482 | params.update(self.get_age_query_params(age=age, age_unit=age_unit)) 483 | 484 | return super().symptom_details(symptom_id=symptom_id, params=params, **kwargs) 485 | 486 | def symptom_list( 487 | self, age: int, age_unit: Optional[str] = None, **kwargs 488 | ) -> List[SymptomDetails]: 489 | """ 490 | Makes an API request and returns list of symptom details objects. 491 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#symptoms. 492 | 493 | :param age: Age value 494 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 495 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 496 | 497 | :returns: A list of dict objects with symptom details 498 | """ 499 | params = kwargs.pop("params", {}) 500 | params.update(self.get_age_query_params(age=age, age_unit=age_unit)) 501 | 502 | return super().symptom_list(params=params, **kwargs) 503 | 504 | def risk_factor_details( 505 | self, risk_factor_id: str, age: int, age_unit: Optional[str] = None, **kwargs 506 | ) -> RiskFactorDetails: 507 | """ 508 | Makes an API request and returns risk factor details object. 509 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#risk-factors. 510 | 511 | :param risk_factor_id: risk factor id 512 | :param age: Age value 513 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 514 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 515 | 516 | :returns: A dict object with risk factor details 517 | """ 518 | params = kwargs.pop("params", {}) 519 | params.update(self.get_age_query_params(age=age, age_unit=age_unit)) 520 | 521 | return super().risk_factor_details( 522 | risk_factor_id=risk_factor_id, params=params, **kwargs 523 | ) 524 | 525 | def risk_factor_list( 526 | self, age: int, age_unit: Optional[str] = None, **kwargs 527 | ) -> List[RiskFactorDetails]: 528 | """ 529 | Makes an API request and returns list of risk factors details objects. 530 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#risk-factors. 531 | 532 | :param age: Age value 533 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 534 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 535 | 536 | :returns: A list of dict objects with risk factor details 537 | """ 538 | params = kwargs.pop("params", {}) 539 | params.update(self.get_age_query_params(age=age, age_unit=age_unit)) 540 | 541 | return super().risk_factor_list(params=params, **kwargs) 542 | 543 | def lab_test_details( 544 | self, lab_test_id: str, age: int, age_unit: Optional[str] = None, **kwargs 545 | ) -> LabTestDetails: 546 | """ 547 | Makes an API request and returns lab_test details object. 548 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#lab-tests-and-lab-test-results. 549 | 550 | :param lab_test_id: LabTest id 551 | :param age: Age value 552 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 553 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 554 | 555 | :returns: A dict object with lab test details 556 | """ 557 | params = kwargs.pop("params", {}) 558 | params.update(self.get_age_query_params(age=age, age_unit=age_unit)) 559 | 560 | return super().lab_test_details( 561 | lab_test_id=lab_test_id, params=params, **kwargs 562 | ) 563 | 564 | def lab_test_list( 565 | self, age: int, age_unit: Optional[str] = None, **kwargs 566 | ) -> List[LabTestDetails]: 567 | """ 568 | Makes an API request and returns list of lab test details objects. 569 | See the docs: https://developer.infermedica.com/docs/v3/medical-concepts#lab-tests-and-lab-test-results. 570 | 571 | :param age: Age value 572 | :param age_unit: (optional) Age unit, one of values 'year' or 'month' 573 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 574 | 575 | :returns: A list of dict objects with lab test details 576 | """ 577 | params = kwargs.pop("params", {}) 578 | params.update(self.get_age_query_params(age=age, age_unit=age_unit)) 579 | 580 | return super().lab_test_list(params=params, **kwargs) 581 | 582 | def concept_details(self, concept_id: str, **kwargs) -> ConceptDetails: 583 | """ 584 | Makes an API request and returns concept details object. 585 | See the docs: https://developer.infermedica.com/docs/v3/concepts. 586 | 587 | :param concept_id: Concept id 588 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 589 | 590 | :returns: A dict object with concept details 591 | """ 592 | return super().concept_details(concept_id=concept_id, **kwargs) 593 | 594 | def concept_list( 595 | self, 596 | ids: Optional[List[str]] = None, 597 | types: Optional[List[Union[ConceptType, str]]] = None, 598 | **kwargs, 599 | ) -> List[ConceptDetails]: 600 | """ 601 | Makes an API request and returns list of concept details objects. 602 | See the docs: https://developer.infermedica.com/docs/v3/concepts. 603 | 604 | :param ids: (optional) List of concept ids to fetch data only for selected ids 605 | :param types: (optional) List of concept types (enums ConceptType or str) to narrow the response 606 | :param kwargs: (optional) Keyword arguments passed to lower level parent :class:`BasicAPIv3Connector` method 607 | 608 | :returns: A list of dict objects with concept details 609 | """ 610 | params = kwargs.pop("params", {}) 611 | 612 | if ids: 613 | params["ids"] = ",".join(ids) 614 | 615 | if types: 616 | types_as_str_list = [ 617 | ( 618 | concept_type.value 619 | if isinstance(concept_type, ConceptType) 620 | else concept_type 621 | ) 622 | for concept_type in types 623 | ] 624 | for concept_type in types_as_str_list: 625 | if not ConceptType.has_value(concept_type): 626 | raise exceptions.InvalidConceptType(concept_type) 627 | params["types"] = ",".join(types_as_str_list) 628 | 629 | return super().concept_list(params=params, **kwargs) 630 | --------------------------------------------------------------------------------