diff --git a/.gitignore b/.gitignore index b6e4761..ea1b9bf 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,9 @@ dmypy.json # Pyre type checker .pyre/ + +# IDEs +.idea/ + +# Custom +cache/ diff --git a/CodeSystem.ipynb b/CodeSystem.ipynb index 4ddde4a..21b5f72 100644 --- a/CodeSystem.ipynb +++ b/CodeSystem.ipynb @@ -5,377 +5,104 @@ "id": "266cc6d5", "metadata": {}, "source": [ - "# Demo Notebook - CodeSystem" + "# Demo Notebook - CodeSystem\n", + "Some code to set up the notebook:" ] }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 1, "id": "3c144a36-117c-42a0-9109-df73433e8267", "metadata": { "scrolled": false }, - "outputs": [], - "source": [ - "PHENOP=\"http://localhost:8088/fhir\" # Jupyter uses 8888!!\n", - "\n", - "import httpx\n", - "import json\n", - "from IPython.display import display, HTML\n", - "\n", - "client = httpx.Client()\n", - "request_headers = {\n", - " \"Content-Type\" : \"application/fhir+json; charset=UTF-8\",\n", - " \"Accept-Charset\" : \"utf-8\",\n", - " \"User-Agent\" : \"HAPI-FHIR/5.0.0 (FHIR Client; FHIR 4.0.1/R4; apache)\" }\n", - "\n", - "def do_hapi_request(internal_path, in_params):\n", - " rq = httpx.Request(\"GET\", PHENOP + internal_path,\n", - " params = in_params, headers=request_headers)\n", - " print(rq.url)\n", - " response = client.send(rq)\n", - " return response\n", - " \n", - "def decode_status(response):\n", - " if response is None:\n", - " print(\"NO RESPONSE\")\n", - " return(True)\n", - " else:\n", - " if (response.status_code >= 200 and response.status_code < 300):\n", - " return(False)\n", - " elif (response.status_code >= 400 and response.status_code < 500):\n", - " return(True)\n", - " elif (response.status_code >= 500) :\n", - " return(True)\n", - "\n", - "def decode_code_system_json(response):\n", - " if not decode_status(response) :\n", - " response_dict = json.loads(response.text)\n", - " if ('entry' in response_dict):\n", - " print(\"title:\" + response_dict['entry'][0]['resource']['title'])\n", - " print(\"resourceType:\", response_dict['entry'][0]['resource']['resourceType'],\n", - " end=\" \")\n", - " print(\"id: \" + response_dict['entry'][0]['resource']['id'])\n", - " print(\"url: \" + response_dict['entry'][0]['resource']['url'])\n", - " print(\"full url: \" + response_dict['entry'][0]['fullUrl'])\n", - " if len(response_dict['entry']) > 1 :\n", - " print(\"identifier system:\\\"\" + response_dict['entry'][1]['resource']['identifier'][0]['system'],\n", - " end=\"\\\", \")\n", - " print(\"value: \\\"\" + response_dict['entry'][1]['resource']['identifier'][0]['value'] + \"\\\"\")\n", - " else:\n", - " print(\"?? no 2nd entry for resource identifier\")\n", - " else:\n", - " print(\"Not Present\")\n", - " print(\"\") \n", - "\n", - "def decode_validate_response(repsonse) :\n", - " print(response)\n", - " if False:\n", - " print(response.keys())\n", - " print(response['issue']['severity'])\n", - " print(response['issue']['response'])\n", - " display(HTML(response['text']['div']))\n", - " print(\"\")\n", - " \n", - "def decode_lookup_response(response):\n", - " if (not decode_status(response)) :\n", - " response_dict = json.loads(response.text)\n", - " for p in response_dict['parameter']:\n", - " if p['name'] != 'property' and p['name'] != 'designation':\n", - " if 'valueString' in p:\n", - " print(p['name'], \":\", p['valueString'], sep=\"\", end=\", \")\n", - " if 'valueBoolean' in p:\n", - " print(p['name'], \":\", p['valueBoolean'], sep=\"\", end=\", \")\n", - " print(\"\")\n", - " else:\n", - " print(\"error\")\n", - " print(\"\")" - ] - }, - { - "cell_type": "markdown", - "id": "a9ebc889", - "metadata": {}, - "source": [ - "## CodeSystem Search\n", - "List code systems \n", - "(where's Loinc?)" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "8add4061", - "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "http://localhost:8088/fhir/CodeSystem/\n", - "American Dental Association Area of Oral Cavity System\n", - "ADAAreaOralCavitySystem\n", - "http://localhost:8088/fhir/CodeSystem/ADAAreaOralCavitySystem\n", - "http://terminology.hl7.org/CodeSystem/ADAAreaOralCavitySystem\n", - "\n", - "ADA Universal Tooth Designation System\n", - "ADAUniversalToothDesignationSystem\n", - "http://localhost:8088/fhir/CodeSystem/ADAUniversalToothDesignationSystem\n", - "http://terminology.hl7.org/CodeSystem/ADAUniversalToothDesignationSystem\n", - "\n", - "AHA NUBC Patient Discharge Status Codes\n", - "AHANUBCPatientDischargeStatus\n", - "http://localhost:8088/fhir/CodeSystem/AHANUBCPatientDischargeStatus\n", - "https://www.nubc.org/CodeSystem/PatDischargeStatus\n", - "\n", - "AHA NUBC Point of Origin for Newborn\n", - "AHANUBCPointOfOriginNewborn\n", - "http://localhost:8088/fhir/CodeSystem/AHANUBCPointOfOriginNewborn\n", - "https://www.nubc.org/CodeSystem/PointOfOriginNewborn\n", - "\n", - "AHA NUBC Point of Origin for Non-newborn\n", - "AHANUBCPointOfOriginNonnewborn\n", - "http://localhost:8088/fhir/CodeSystem/AHANUBCPointOfOriginNonnewborn\n", - "https://www.nubc.org/CodeSystem/PointOfOrigin\n", - "\n", - "AHA NUBC Priority (Type) of Admission or Visit\n", - "AHANUBCPriorityTypeOfAdmitOrVisit\n", - "http://localhost:8088/fhir/CodeSystem/AHANUBCPriorityTypeOfAdmitOrVisit\n", - "https://www.nubc.org/CodeSystem/PriorityTypeOfAdmitOrVisit\n", - "\n", - "AHA NUBC Revenue Codes\n", - "AHANUBCRevenueCodes\n", - "http://localhost:8088/fhir/CodeSystem/AHANUBCRevenueCodes\n", - "https://www.nubc.org/CodeSystem/RevenueCodes\n", - "\n", - "AHA NUBC Type Of Bill Codes\n", - "AHANUBCTypeOfBill\n", - "http://localhost:8088/fhir/CodeSystem/AHANUBCTypeOfBill\n", - "https://www.nubc.org/CodeSystem/TypeOfBill\n", - "\n", - "All Patient Diagnosis Related Groups (AP DRGs)\n", - "APDRG\n", - "http://localhost:8088/fhir/CodeSystem/APDRG\n", - "http://uri.hddaccess.com/cs/apdrg\n", - "\n", - "All Patient Refined Diagnosis Related Groups (APR DRGs)\n", - "APRDRG\n", - "http://localhost:8088/fhir/CodeSystem/APRDRG\n", - "http://uri.hddaccess.com/cs/aprdrg\n", - "\n", - "College of American Pathologists (CAP) eCC (electronic Cancer Checklists)\n", - "CAPeCC\n", - "http://localhost:8088/fhir/CodeSystem/CAPeCC\n", - "http://cap.org/eCC\n", - "\n", - "Clinical Care Classification System\n", - "CCC\n", - "http://localhost:8088/fhir/CodeSystem/CCC\n", - "http://terminology.hl7.org/CodeSystem/CCC\n", - "\n", - "NHSN Surveillance System Codes\n", - "CDCNHSN\n", - "http://localhost:8088/fhir/CodeSystem/CDCNHSN\n", - "https://www.cdc.gov/nhsn/cdaportal/terminology/codesystem/cdcnhsn.html\n", - "\n", - "CDC Race and Ethnicity\n", - "CDCREC\n", - "http://localhost:8088/fhir/CodeSystem/CDCREC\n", - "urn:oid:2.16.840.1.113883.6.238\n", - "\n", - "Code on Dental Procedures and Nomenclature\n", - "CDT\n", - "http://localhost:8088/fhir/CodeSystem/CDT\n", - "http://www.ada.org/cdt\n", - "\n", - "CMS Place of Service Codes (POS)\n", - "CMSPlaceofServiceCodes\n", - "http://localhost:8088/fhir/CodeSystem/CMSPlaceofServiceCodes\n", - "https://www.cms.gov/Medicare/Coding/place-of-service-codes/Place_of_Service_Code_Set\n", - "\n", - "Current Procedural Terminology (CPT®)\n", - "CPT\n", - "http://localhost:8088/fhir/CodeSystem/CPT\n", - "http://www.ama-assn.org/go/cpt\n", - "\n", - "Vaccine Administered Code Set (CVX)\n", - "CVX\n", - "http://localhost:8088/fhir/CodeSystem/CVX\n", - "http://hl7.org/fhir/sid/cvx\n", - "\n", - "ClinVar Variant ID\n", - "ClinVarV\n", - "http://localhost:8088/fhir/CodeSystem/ClinVarV\n", - "http://www.ncbi.nlm.nih.gov/clinvar\n", - "\n", - "Global Medical Device Nomenclature\n", - "GMDN\n", - "http://localhost:8088/fhir/CodeSystem/GMDN\n", - "http://terminology.hl7.org/CodeSystem/GMDN\n", - "\n" + "title: LOINC Code System\n", + "resourceType: CodeSystem id: loinc\n", + "url: http://loinc.org\n", + "full url: http://20.119.216.32:8000/r4/CodeSystem/loinc\n", + "identifier system: \"urn:ietf:rfc:3986\", value: \"urn:oid:2.16.840.1.113883.6.1\"\n", + "name:LOINC, display:Pathogenic, abstract:False, \n" ] } ], "source": [ - "response = do_hapi_request(\"/CodeSystem/\", {}) \n", - "if (not decode_status(response)) :\n", - " response_dict = json.loads(response.text)\n", - " for vocab_entry in response_dict['entry']:\n", - " print(vocab_entry['resource']['title'])\n", - " print(vocab_entry['resource']['id'])\n", - " print(vocab_entry['fullUrl'])\n", - " print(vocab_entry['resource']['url'])\n", - " print(\"\")" + "# BASE_URL =\"http://localhost:8088/fhir\" # Jupyter uses 8888!!\n", + "BASE_URL = \"http://20.119.216.32:8000/r4/\" # TIMS server\n", + "USE_CACHE = True\n", + "\n", + "from imports import TimsClient, decode_response_status_not_ok as decode_status, decode_validate_response, decode_lookup_response\n", + "client = TimsClient(BASE_URL, USE_CACHE)\n", + "do_hapi_request = client.do_hapi_request" ] }, { "cell_type": "markdown", - "id": "ad5dd31c", + "id": "a9ebc889", "metadata": {}, "source": [ - "## CodeSystem search \n", + "## CodeSystem Search\n", "https://hl7.org/fhir/http.html#search " ] }, { - "cell_type": "code", - "execution_count": 21, - "id": "61627370", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/?system=http%3A%2F%2Floinc.org\n", - "title:Logical Observation Identifiers, Names and Codes (LOINC)\n", - "resourceType: CodeSystem id: v3-loinc\n", - "url: http://loinc.org\n", - "full url: http://localhost:8088/fhir/CodeSystem/v3-loinc\n", - "identifier system:\"urn:ietf:rfc:3986\", value: \"urn:oid:2.16.840.1.113883.6.1\"\n", - "\n" - ] - } - ], - "source": [ - "response = do_hapi_request(\"/CodeSystem/\", {\n", - " 'system' : 'http://loinc.org'}) \n", - "if (not decode_status(response)) :\n", - " decode_code_system_json(response)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "78d0fd89", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/?system=v3-loinc\n", - "Not Present\n", - "\n", - "\n" - ] - } - ], - "source": [ - "# fails\n", - "response = do_hapi_request(\"/CodeSystem/\", {\n", - " 'system' : 'v3-loinc'})\n", - "if (not decode_status(response)) :\n", - " decode_code_system_json(response)\n", - " print(response)" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "be12194e", + "cell_type": "markdown", + "id": "29e2c717", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/?system=http%3A%2F%2Fsnomed.org\n", - "Not Present\n", - "\n" - ] - } - ], "source": [ - "response = do_hapi_request(\"/CodeSystem/\", {'system' : 'http://snomed.org'}) \n", - "decode_code_system_json(response)" + "All code systems present:" ] }, { "cell_type": "code", - "execution_count": 25, - "id": "ac88b72c", + "execution_count": 2, + "id": "8add4061", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "http://localhost:8088/fhir/CodeSystem/?system=http%3A%2F%2Fwww.ama-assn.org%2Fgo%2Fcpt\n", - "title:Current Procedural Terminology (CPT®)\n", - "resourceType: CodeSystem id: CPT\n", - "url: http://www.ama-assn.org/go/cpt\n", - "full url: http://localhost:8088/fhir/CodeSystem/CPT\n", - "?? no 2nd entry for resource identifier\n", - "\n" + "SNOMED CT, LOINC, ICD-10-CM, CPT4, http://purl.obolibrary.org/obo/mondo.owl, HPO\n" ] } ], "source": [ - "response = do_hapi_request(\"/CodeSystem/\", {'system' : 'http://www.ama-assn.org/go/cpt'}) \n", - "decode_code_system_json(response)" + "client.summarize_code_systems()" ] }, { - "cell_type": "code", - "execution_count": 26, - "id": "7232505b", + "cell_type": "markdown", + "id": "ad5dd31c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/?system=http%3A%2F%2Fhpo.org\n", - "Not Present\n", - "\n" - ] - } - ], "source": [ - "response = do_hapi_request(\"/CodeSystem/\", {'system' : 'http://hpo.org'}) \n", - "decode_code_system_json(response)" + "Details of a single code system:" ] }, { "cell_type": "code", - "execution_count": 27, - "id": "99921509", + "execution_count": 3, + "id": "61627370", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "http://localhost:8088/fhir/CodeSystem/?system=http%3A%2F%2FICD10CM.org\n", - "Not Present\n", - "\n" + "title: LOINC Code System\n", + "resourceType: CodeSystem id: loinc\n", + "url: http://loinc.org\n", + "full url: http://20.119.216.32:8000/r4/CodeSystem/loinc\n", + "identifier system: \"urn:ietf:rfc:3986\", value: \"urn:oid:2.16.840.1.113883.6.1\"\n" ] } ], "source": [ - "response = do_hapi_request(\"/CodeSystem/\", {'system' : 'http://ICD10CM.org'}) \n", - "decode_code_system_json(response)" + "client.summarize_code_system_by_name('LOINC')" ] }, { @@ -383,12 +110,13 @@ "id": "1ec9f286", "metadata": {}, "source": [ - "## CodeSystem $validate-code " + "## CodeSystem $validate-code \n", + "Check if a code is in a code system." ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 4, "id": "a7f9ac11", "metadata": {}, "outputs": [ @@ -396,123 +124,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "http://localhost:8088/fhir/CodeSystem/v3-loinc/$validate-code?code=69551-0\n", - "\n" - ] - } - ], - "source": [ - "response = do_hapi_request(\"/CodeSystem/v3-loinc/$validate-code\", {\n", - " 'code' : '69551-0'})\n", - "if (not decode_status(response)) :\n", - " response_dict = json.loads(response.text)\n", - " decode_validate_response(response_dict)" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "id": "5031812c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/v3-loinc/$validate-code?codeSystem=http%3A%2F%2Floinc.org&code=69551-0\n", - "\n" + "Genomic alt allele [ID]\n" ] } ], "source": [ - "response = do_hapi_request(\"/CodeSystem/v3-loinc/$validate-code\", {\n", - " 'codeSystem' : 'http://loinc.org',\n", - " 'code' : '69551-0'})\n", - "if (not decode_status(response)) :\n", - " response_dict = json.loads(response.text)\n", - " decode_validate_response(response_dict)\n", - "else:\n", - " print(response, end=\"\\n\\n\")" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "id": "8c342b97", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/v3-loinc/$validate-code?code=LA6668-3\n", - "\n" - ] - } - ], - "source": [ - "response = do_hapi_request(\"/CodeSystem/v3-loinc/$validate-code\", {\n", - " 'code' : 'LA6668-3'})\n", - "if (not decode_status(response)) :\n", - " response_dict = json.loads(response.text)\n", - " decode_validate_response(response_dict)\n", - "else:\n", - " print(response, end=\"\\n\\n\")" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "id": "9d3f1ce3", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/$validate-code/?codeSystem=http%3A%2F%2Floinc.org&code=69551-0\n", - "\n", - "\n" - ] - } - ], - "source": [ - "response = do_hapi_request(\"/CodeSystem/$validate-code/\", {\n", - " 'codeSystem' : 'http://loinc.org',\n", - " 'code' : '69551-0'})\n", - "if (not decode_status(response)) :\n", - " response_dict = json.loads(response.text)\n", - " decode_validate_response(response_dict)\n", - "else:\n", - " print(response, end=\"\\n\\n\")" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "id": "6d4ee79f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/$validate-code/?codeSystem=v3-loinc&code=69551-0\n", - "\n", - "\n" - ] - } - ], - "source": [ - "# with codeSystem parameter only, fails\n", - "response = do_hapi_request(\"/CodeSystem/$validate-code/\", {\n", - " 'codeSystem' : 'v3-loinc',\n", - " 'code' : '69551-0'}) \n", - "if (not decode_status(response)) :\n", - " response_dict = json.loads(response.text)\n", - " decode_validate_response(response_dict)\n", - "else:\n", - " print(response, end=\"\\n\\n\")" + "client.validate_code(system_id='loinc', code='69551-0')" ] }, { @@ -520,58 +137,13 @@ "id": "7a19ef0f", "metadata": {}, "source": [ - "## CodeSystem $lookup " - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "id": "bd6f2b99", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/$lookup?system=http%3A%2F%2Fbogus.org&code=LA6668-3\n", - "error\n", - "\n" - ] - } - ], - "source": [ - "response = do_hapi_request(\"/CodeSystem/$lookup\", {\n", - " 'system' : 'http://bogus.org',\n", - " 'code' : 'LA6668-3'}) \n", - "decode_lookup_response(response)" + "## CodeSystem $lookup \n", + "Details of a single term:" ] }, { "cell_type": "code", - "execution_count": 53, - "id": "e2d02642", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/$lookup?system=http%3A%2F%2Floinc.org&code=bogus\n", - "error\n", - "\n" - ] - } - ], - "source": [ - "response = do_hapi_request(\"/CodeSystem/$lookup\", {\n", - " 'system' : 'http://loinc.org',\n", - " 'code' : 'bogus'}) \n", - "decode_lookup_response(response)" - ] - }, - { - "cell_type": "code", - "execution_count": 54, + "execution_count": 5, "id": "e87aea97", "metadata": {}, "outputs": [ @@ -579,86 +151,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "http://localhost:8088/fhir/CodeSystem/$lookup?system=http%3A%2F%2Floinc.org&code=LA6668-3\n", - "name:LOINC, display:Pathogenic, abstract:False, \n", - "\n" - ] - } - ], - "source": [ - "response = do_hapi_request(\"/CodeSystem/$lookup\", {\n", - " 'system' : 'http://loinc.org',\n", - " 'code' : 'LA6668-3'}) \n", - "decode_lookup_response(response)" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "id": "1693eec5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/$lookup?system=http%3A%2F%2Floinc.org&code=LA9633-4\n", - "name:LOINC, display:Present, abstract:False, \n", - "\n" + "name:LOINC, display:Pathogenic, abstract:False, \n" ] } ], "source": [ - "response = do_hapi_request(\"/CodeSystem/$lookup\", {\n", - " 'system' : 'http://loinc.org',\n", - " 'code' : 'LA9633-4'}) \n", - "decode_lookup_response(response)" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "id": "2455e14e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/$lookup?system=http%3A%2F%2Floinc.org&code=69547-8\n", - "name:LOINC, display:Genomic ref allele [ID], abstract:False, \n", - "\n" - ] - } - ], - "source": [ - "response = do_hapi_request(\"/CodeSystem/$lookup\", {\n", - " 'system' : 'http://loinc.org',\n", - " 'code' : '69547-8'})\n", - "decode_lookup_response(response)" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "id": "818f2944", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "http://localhost:8088/fhir/CodeSystem/$lookup?system=http%3A%2F%2Floinc.org&code=69551-0\n", - "name:LOINC, display:Genomic alt allele [ID], abstract:False, \n", - "\n" - ] - } - ], - "source": [ - "response = do_hapi_request(\"/CodeSystem/$lookup\", {\n", - " 'system' : 'http://loinc.org',\n", - " 'code' : '69551-0'}) \n", - "decode_lookup_response(response)" + "client.lookup_code(system='http://loinc.org', code='LA6668-3')" ] }, { @@ -666,18 +164,18 @@ "id": "1585aa46", "metadata": {}, "source": [ - "## CodeSystem $subsumes " + "## CodeSystem $subsumes\n", + "Check whether a term is subsumed by another term." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "3bcf8d5b", "metadata": {}, "outputs": [], "source": [ - "\n", - "# T B D\n" + "# TODO" ] }, { @@ -709,7 +207,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.6" + "version": "3.9.12" } }, "nbformat": 4, diff --git a/README.md b/README.md index 58d1ba7..010692c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,13 @@ # TSDemoBoard Jupyter notebooks for showing the evolving set of services configured and implemented in a HAPI-FHIR server. + +## Setup +1. Clone repository +2. Make a virtual environment (optional) +3. Run: `pip install -r requirements.txt` + +## Running +1. From command line, change directory to where you cloned this repository. +2. Run: `jupyter notebook` +3. A new tab will open up in your web browser, allowing you to select local notebooks. Select **CodeSystem.ipynb** to open the notebook. +4. You can run cells one at a time by clicking the "Run" button from the toolbar. Alternatively, you can run them all by opening the "Cell" menu and selecting "Run all". diff --git a/imports.py b/imports.py new file mode 100644 index 0000000..2e32ec7 --- /dev/null +++ b/imports.py @@ -0,0 +1,204 @@ +"""Imports for Jupyter Notebook + +TODO's + 1. Remove `if not decode_response_status_not_ok(response)` comments if Chris R OK w/ my new style. - Joe +""" +import os +import json +from typing import Dict, Union + +import httpx +from IPython.display import display, HTML +from httpx import Response + + +BASE_URL = "http://20.119.216.32:8000/r4/" # TIMS server +REQUEST_HEADERS = { + "Accept-Charset": "utf-8", + "Content-Type": "application/fhir+json; charset=UTF-8", + "User-Agent": "HAPI-FHIR/5.0.0 (FHIR Client; FHIR 4.0.1/R4; apache)"} +PROJECT_DIR = os.path.dirname(__file__) +CACHE_DIR = os.path.join(PROJECT_DIR, 'cache') +URI = str + + +# Utils +def encode_filename_str(x): + """Convert such that can create friendly system filename""" + delim = '*' + return x.replace('/', f'{delim}slash') + + +def decode_filename_str(x): + """Convert from friendly system filename""" + delim = '*' + return x.replace(f'{delim}slash', '/') + + +def url_and_params_to_filename(url: str, params: Dict) -> str: + """Convert to filename""" + x = url + if params: + x += '___params_' + for k, v in params.items(): + x += '__' + k + '_' + v + filename = encode_filename_str(x) + '.json' + return filename + + +def cache_write(url: str, params: Dict, response: Dict): + """Write to cache""" + filename = url_and_params_to_filename(url, params) + if not os.path.exists(CACHE_DIR): + os.makedirs(CACHE_DIR) + path = os.path.join(CACHE_DIR, filename) + with open(path, 'w') as f: + json.dump(response, f, indent=4) + + +def cache_read(url: str, params: Dict) -> Union[Dict, None]: + """Read from cache""" + filename = url_and_params_to_filename(url, params) + path = os.path.join(CACHE_DIR, filename) + if os.path.exists(path): + with open(path, 'r') as f: + data: Dict = json.load(f) + return data + return None + + +# Proper funcs / classes +def decode_response_status_not_ok(response: Response) -> bool: + """Decode status""" + if response is None: + print("NO RESPONSE") + return True + else: + if 200 <= response.status_code < 300: + return False + elif 400 <= response.status_code < 500: + return True + elif response.status_code >= 500: + return True + + +def decode_code_system_json(response: Dict): + """Decode CodeSystem response""" + # if not decode_response_status_not_ok(response): + # response_dict = json.loads(response.text) + if 'entry' in response: + print("title: " + response['entry'][0]['resource']['title']) + print("resourceType: ", response['entry'][0]['resource']['resourceType'], end=" ") + print("id: " + response['entry'][0]['resource']['id']) + print("url: " + response['entry'][0]['resource']['url']) + print("full url: " + response['entry'][0]['fullUrl']) + id_idx = 1 if len(response['entry']) > 1 else 0 + id_sys = response['entry'][id_idx]['resource']['identifier'][0]['system'] + id_sys_val = response['entry'][id_idx]['resource']['identifier'][0]['value'] + print("identifier system: \"" + id_sys, end="\", ") + print("value: \"" + id_sys_val + "\"") + else: + print("Not Present") + + +def decode_validate_response(response, optional_prints=False): + """Decode validate response""" + print(response) + if optional_prints: + print(response.keys()) + print(response['issue']['severity']) + print(response['issue']['response']) + display(HTML(response['text']['div'])) + print("") + + +def decode_lookup_response(response: Dict): + """Deocde lookup response""" + # if not decode_response_status_not_ok(response): + # response_dict = json.loads(response.text) + if 'issue' in response: + print("error") + else: + for p in response['parameter']: + if p['name'] != 'property' and p['name'] != 'designation': + if 'valueString' in p: + print(p['name'], ":", p['valueString'], sep="", end=", ") + if 'valueBoolean' in p: + print(p['name'], ":", p['valueBoolean'], sep="", end=", ") + print("") + + +class TimsClient: + """TIMS Client""" + http_client = httpx.Client() + + def __init__(self, base_url: str, use_cache=False): + self.base_url = base_url + self.use_cache = use_cache + + def do_hapi_request(self, internal_path: str, in_params: Dict = None) -> Dict: + """GET request""" + url = os.path.join(self.base_url, internal_path) + if self.use_cache: + response_dict = cache_read(url, in_params) + if response_dict: + return response_dict + rq = httpx.Request("GET", url, params=in_params, headers=REQUEST_HEADERS) + response: Response = TimsClient.http_client.send(rq) + response_dict: Dict = json.loads(response.text) + + cache_write(url, in_params, response_dict) + return response_dict + + def summarize_code_systems(self, verbose=False): + """Summarize code systems""" + response_dict = self.do_hapi_request("CodeSystem?_summary=true", {}) + # if not decode_response_status_ok(response): + # response_dict = json.loads(response.text) + if verbose: + for vocab_entry in response_dict['entry']: + print(vocab_entry['resource']['name']) + print(vocab_entry['resource']['id']) + print(vocab_entry['fullUrl']) + print(vocab_entry['resource']['url']) + print('') + else: + sys_names = [x['resource']['name'] for x in response_dict['entry']] + print(', '.join(sys_names)) + + def summarize_code_system_by_name(self, name: str): + """Summarize a code system by name param""" + response: Dict = self.do_hapi_request("CodeSystem", {'name': name}) + # if not decode_response_status_not_ok(response): + decode_code_system_json(response) + + def summarize_code_system_by_system(self, system: str): + """Summarize a code system by system param""" + response: Dict = self.do_hapi_request("CodeSystem", {'system': system}) + # if not decode_response_status_not_ok(response): + decode_code_system_json(response) + + def validate_code(self, system_id: str, code: str): + """Validate that code is in code system""" + response: Dict = self.do_hapi_request(f'CodeSystem/{system_id}/$validate-code?code={code}') + valid = [x['valueBoolean'] for x in response['parameter'] if x['name'] == 'result'][0] + if valid: + label = [x['valueString'] for x in response['parameter'] if x['name'] == 'display'][0] + print(label) + else: + print('Invalid code') + + def lookup_code(self, system: URI, code: str): + """Lookup code within code system""" + response: Dict = self.do_hapi_request(f'CodeSystem/$lookup?system={system}&code={code}') + decode_lookup_response(response) + + +# Set true and run this file directly for troubleshooting / ease of development +DEBUG = False +if __name__ == '__main__': + if DEBUG: + client = TimsClient(base_url=BASE_URL, use_cache=True) + # examples: + client.summarize_code_system_by_name('LOINC') + client.lookup_code(system='http://loinc.org', code='LA6668-3') diff --git a/requirements-unlocked.txt b/requirements-unlocked.txt new file mode 100644 index 0000000..7628a8d --- /dev/null +++ b/requirements-unlocked.txt @@ -0,0 +1,3 @@ +httpx +jupyter +requests diff --git a/start-jupyter.sh b/start-jupyter.sh deleted file mode 100755 index c61b86e..0000000 --- a/start-jupyter.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -# becuase you're going to forget something this simple -# jupyter-lab - -jupyter notebook - -