Created
October 31, 2025 22:05
-
-
Save minrk/879d0b762db8aef120ddebb30464a144 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "code", | |
| "execution_count": 1, | |
| "id": "23f36510-9eca-4004-a553-363ac29108e2", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "patient_id = 40079\n", | |
| "study_id = 30017" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 2, | |
| "id": "ce72d503-b209-419e-a94c-61b199becd36", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "{'uuid': '0a5a5276-039d-4400-bd63-2f8413964032',\n", | |
| " 'schema_id': {'namespace': 'omh', 'name': 'blood-glucose', 'version': '4.0'},\n", | |
| " 'source_creation_date_time': '2025-10-29T20:16:53Z',\n", | |
| " 'modality': 'sensed',\n", | |
| " 'acquisition_rate': {'number_of_times': 1,\n", | |
| " 'time_window': {'value': 5, 'unit': 'min'}},\n", | |
| " 'external_datasheets': [{'datasheet_type': 'data source',\n", | |
| " 'datasheet_reference': 'https://www.libreview.com/'},\n", | |
| " {'datasheet_type': 'measuring device',\n", | |
| " 'datasheet_reference': 'https://www.freestyle.abbott/us-en/products/freestyle-libre-3.html'}]}" | |
| ] | |
| }, | |
| "execution_count": 2, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "import json\n", | |
| "\n", | |
| "with open(\"blood_glucose.json\") as f:\n", | |
| " cgm_records = json.load(f)\n", | |
| "cgm_records[\"header\"]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 3, | |
| "id": "311b7613-0511-40ef-bc5e-de9baddb888a", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "{'uuid': 'b27411f4-358b-44ed-8f0c-1b40a3120858',\n", | |
| " 'schema_id': {'namespace': 'ieee', 'name': 'food-entry', 'version': '0.1'},\n", | |
| " 'source_creation_date_time': '2025-10-29T20:16:53Z',\n", | |
| " 'modality': 'self-reported',\n", | |
| " 'external_datasheets': [{'datasheet_type': 'recording app',\n", | |
| " 'datasheet_reference': 'https://cronometer.com/index.html'}]}" | |
| ] | |
| }, | |
| "execution_count": 3, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "with open(\"food_entry.json\") as f:\n", | |
| " food_entry = json.load(f)\n", | |
| "food_entry['header']" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 4, | |
| "id": "d9b94b69-03da-4cf9-8512-1500c72966a9", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "{'uuid': '1c4e7a5d-2c1e-4fae-a7fc-b497c7595e94',\n", | |
| " 'schema_id': {'namespace': 'ieee',\n", | |
| " 'name': 'sleep-stage-summary',\n", | |
| " 'version': '1.0'},\n", | |
| " 'source_creation_date_time': '2025-10-29T20:16:53Z',\n", | |
| " 'modality': 'sensed',\n", | |
| " 'external_datasheets': [{'datasheet_type': 'data aggregator',\n", | |
| " 'datasheet_reference': 'https://www.fitabase.com/media/2126/fitabase-fitbit-data-dictionary-as-of-05162025.pdf'},\n", | |
| " {'datasheet_type': 'measuring device',\n", | |
| " 'datasheet_reference': 'https://store.google.com/us/product/fitbit_sense_2?hl=en-US&pli=1'},\n", | |
| " {'datasheet_type': 'processing algorithm', 'datasheet_reference': ''},\n", | |
| " {'datasheet_type': 'manufacturer',\n", | |
| " 'datasheet_reference': 'https://store.google.com/category/watches_trackers?hl=en-US'}]}" | |
| ] | |
| }, | |
| "execution_count": 4, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "with open(\"sleep_stage_summary.json\") as f:\n", | |
| " sleep_stage = json.load(f)\n", | |
| "sleep_stage['header']" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 5, | |
| "id": "772c9874-b7a5-4aae-96be-ecede9ecfbd5", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "'74bb39f0-5fa3-58c0-b88f-f96d03bf7513'" | |
| ] | |
| }, | |
| "execution_count": 5, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "import uuid\n", | |
| "from datetime import UTC, datetime\n", | |
| "\n", | |
| "epoch = datetime(1900, 1, 1, tzinfo=UTC)\n", | |
| "ns_uuid = uuid.UUID('d15785047be14c68b5a21c30e8e15c91')\n", | |
| "ns_uuid\n", | |
| "def uuid_for_reading(reading):\n", | |
| " # dt = datetime.fromisoformat(reading['effective_time_frame']['date_time'])\n", | |
| " # clock_seq = int((dt - epoch).total_seconds())\n", | |
| " return str(uuid.uuid5(ns_uuid, json.dumps(reading)))\n", | |
| "\n", | |
| "uuid_for_reading(cgm_records[\"body\"][2])" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 6, | |
| "id": "72839c14-4d70-4444-a03a-6d34a5645d5b", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from jupyterhealth_client import JupyterHealthClient\n", | |
| "\n", | |
| "jhc = JupyterHealthClient()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 43, | |
| "id": "249283f2-b7a9-4c6b-b95b-48dcafdf1162", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "{'studyScopeConsents': [{'id': 94,\n", | |
| " 'studyPatient': {'id': 78, 'study': 30017, 'patient': 40079},\n", | |
| " 'scopeCode': {'id': 50001,\n", | |
| " 'codingSystem': 'https://w3id.org/openmhealth',\n", | |
| " 'codingCode': 'omh:blood-glucose:4.0',\n", | |
| " 'text': 'Blood glucose'},\n", | |
| " 'consented': True,\n", | |
| " 'consentedTime': '2025-10-31T04:40:58.012671Z'},\n", | |
| " {'id': 95,\n", | |
| " 'studyPatient': {'id': 78, 'study': 30017, 'patient': 40079},\n", | |
| " 'scopeCode': {'id': 50006,\n", | |
| " 'codingSystem': 'https://w3id.org/openmhealth',\n", | |
| " 'codingCode': 'ieee:sleep-stage-summary:1.0',\n", | |
| " 'text': 'Sleep Stage Summary'},\n", | |
| " 'consented': True,\n", | |
| " 'consentedTime': '2025-10-31T04:40:58.012671Z'},\n", | |
| " {'id': 96,\n", | |
| " 'studyPatient': {'id': 78, 'study': 30017, 'patient': 40079},\n", | |
| " 'scopeCode': {'id': 50007,\n", | |
| " 'codingSystem': 'https://w3id.org/openmhealth',\n", | |
| " 'codingCode': 'ieee:food-entry:0.1',\n", | |
| " 'text': 'Food Entry'},\n", | |
| " 'consented': True,\n", | |
| " 'consentedTime': '2025-10-31T04:40:58.012671Z'}]}" | |
| ] | |
| }, | |
| "execution_count": 43, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "jhc._api_request(\n", | |
| " f\"patients/{patient_id}/consents\",\n", | |
| " method=\"POST\",\n", | |
| " json={\n", | |
| " \"studyScopeConsents\": [\n", | |
| " {\n", | |
| " \"studyId\": study_id,\n", | |
| " \"scopeConsents\": [\n", | |
| " {\n", | |
| " \"codingSystem\": \"https://w3id.org/openmhealth\",\n", | |
| " \"codingCode\": \"omh:blood-glucose:4.0\",\n", | |
| " \"consented\": True,\n", | |
| " },\n", | |
| " {\n", | |
| " \"codingSystem\": \"https://w3id.org/openmhealth\",\n", | |
| " \"codingCode\": \"ieee:sleep-stage-summary:1.0\",\n", | |
| " \"consented\": True,\n", | |
| " },\n", | |
| " {\n", | |
| " \"codingSystem\": \"https://w3id.org/openmhealth\",\n", | |
| " \"codingCode\": \"ieee:food-entry:0.1\",\n", | |
| " \"consented\": True,\n", | |
| " },\n", | |
| " ],\n", | |
| " }\n", | |
| " ]\n", | |
| " },\n", | |
| ")\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 44, | |
| "id": "c7fc40b9-1b56-4ed1-8872-b14c066461ab", | |
| "metadata": { | |
| "scrolled": true | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "import base64\n", | |
| "import json\n", | |
| "from tqdm.notebook import tqdm\n", | |
| "\n", | |
| "\n", | |
| "def upload_records(header, readings, device_id, patient_id, chunk_size=100):\n", | |
| " schema_id = header['schema_id']\n", | |
| " coding = [\n", | |
| " {\"system\": \"https://w3id.org/openmhealth\",\n", | |
| " \"code\": f\"{schema_id['namespace']}:{schema_id['name']}:{schema_id['version']}\",\n", | |
| " }\n", | |
| " ]\n", | |
| "\n", | |
| " for i in tqdm(range(0, len(readings), chunk_size)):\n", | |
| " readings_chunk = readings[i:i + chunk_size]\n", | |
| " entries = []\n", | |
| " for reading in readings[i:i + chunk_size]:\n", | |
| "\n", | |
| " reading_header = header.copy()\n", | |
| " reading_header['uuid'] = uuid_for_reading(reading)\n", | |
| " # reading_header['source_creation_date_time'] = reading['effective_time_frame']['date_time']\n", | |
| " record = dict(\n", | |
| " header=reading_header,\n", | |
| " body=reading,\n", | |
| " )\n", | |
| " entry = {\n", | |
| " \"resource\": {\n", | |
| " \"resourceType\": \"Observation\",\n", | |
| " \"status\": \"final\",\n", | |
| " \"code\": {\n", | |
| " \"coding\": coding,\n", | |
| " },\n", | |
| " \"subject\": {\"reference\": f\"Patient/{patient_id}\"},\n", | |
| " \"device\": {\"reference\": f\"Device/{device_id}\"},\n", | |
| " \"valueAttachment\": {\n", | |
| " \"contentType\": \"application/json\",\n", | |
| " \"data\": base64.b64encode(json.dumps(record).encode()).decode(),\n", | |
| " },\n", | |
| " },\n", | |
| " \"request\": {\"method\": \"POST\", \"url\": \"Observation\"},\n", | |
| " }\n", | |
| " entries.append(entry)\n", | |
| "\n", | |
| " request_payload = {\n", | |
| " \"resourceType\": \"Bundle\",\n", | |
| " \"type\": \"batch\",\n", | |
| " \"entry\": entries,\n", | |
| " }\n", | |
| "\n", | |
| " r = jhc._api_request(\"\", method=\"POST\", fhir=True, json=request_payload)\n", | |
| " for entry in r['entry']:\n", | |
| " if 'outcome' in entry['response']:\n", | |
| " for issue in entry['response']['outcome']['issue']:\n", | |
| " print(issue['diagnostics'])\n", | |
| " raise ValueError(\"error!\")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 45, | |
| "id": "f42921dc-5b53-406c-a482-066c0de335e5", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "application/vnd.jupyter.widget-view+json": { | |
| "model_id": "59be4e96e4174f93a6df68462fc9aba7", | |
| "version_major": 2, | |
| "version_minor": 0 | |
| }, | |
| "text/plain": [ | |
| " 0%| | 0/41 [00:00<?, ?it/s]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "upload_records(cgm_records[\"header\"], cgm_records[\"body\"], patient_id=patient_id, device_id=70005)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 46, | |
| "id": "6b633416-35cf-46a6-9006-3025e90fbb9f", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "application/vnd.jupyter.widget-view+json": { | |
| "model_id": "456a9849bc274987a497a8860335b73c", | |
| "version_major": 2, | |
| "version_minor": 0 | |
| }, | |
| "text/plain": [ | |
| " 0%| | 0/1 [00:00<?, ?it/s]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "upload_records(food_entry['header'], food_entry['body'], patient_id=patient_id, device_id=70003)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 47, | |
| "id": "2e682bcf-825f-4b19-a5e8-04e59b3f9208", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "application/vnd.jupyter.widget-view+json": { | |
| "model_id": "abc6569b3e3d41cf883b3b3136c639b6", | |
| "version_major": 2, | |
| "version_minor": 0 | |
| }, | |
| "text/plain": [ | |
| " 0%| | 0/1 [00:00<?, ?it/s]" | |
| ] | |
| }, | |
| "metadata": {}, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "upload_records(sleep_stage['header'], sleep_stage['body'], patient_id=patient_id, device_id=70004)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 8, | |
| "id": "6bccdad2-a12f-4b00-aef1-d62438834fcd", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from jupyterhealth_client import Code, JupyterHealthClient" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 9, | |
| "id": "8af976ce-e68a-4b17-a11e-53056d6a1658", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "<Code.SLEEP_STAGE_SUMMARY: 'ieee:sleep-stage-summary:1.0'>" | |
| ] | |
| }, | |
| "execution_count": 9, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "Code.SLEEP_STAGE_SUMMARY" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 10, | |
| "id": "cbeaa140-47c0-4746-9a54-7065f3b210ef", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "4018" | |
| ] | |
| }, | |
| "execution_count": 10, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "df = jhc.list_observations_df(patient_id=patient_id, study_id=study_id, code=Code.BLOOD_GLUCOSE.value, limit=10_000)\n", | |
| "len(df)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 11, | |
| "id": "ddd2f1ca-43cf-468f-b764-75d07e4d08a3", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "64" | |
| ] | |
| }, | |
| "execution_count": 11, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "df = jhc.list_observations_df(patient_id=patient_id, study_id=study_id, code=Code.FOOD_ENTRY.value)\n", | |
| "len(df)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 12, | |
| "id": "40ed59b0-d9a8-4d2d-834d-ca51c95200bc", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "14" | |
| ] | |
| }, | |
| "execution_count": 12, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "df = jhc.list_observations_df(patient_id=patient_id, study_id=study_id, code=Code.SLEEP_STAGE_SUMMARY.value)\n", | |
| "len(df)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "79a77c74-ecdd-4771-8bcb-739ab9e1bcc5", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "Python 3 (ipykernel)", | |
| "language": "python", | |
| "name": "python3" | |
| }, | |
| "language_info": { | |
| "codemirror_mode": { | |
| "name": "ipython", | |
| "version": 3 | |
| }, | |
| "file_extension": ".py", | |
| "mimetype": "text/x-python", | |
| "name": "python", | |
| "nbconvert_exporter": "python", | |
| "pygments_lexer": "ipython3", | |
| "version": "3.13.7" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 5 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment