iints-sdk-python35 0.0.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iints/__init__.py +183 -0
- iints/analysis/__init__.py +12 -0
- iints/analysis/algorithm_xray.py +387 -0
- iints/analysis/baseline.py +92 -0
- iints/analysis/clinical_benchmark.py +198 -0
- iints/analysis/clinical_metrics.py +551 -0
- iints/analysis/clinical_tir_analyzer.py +136 -0
- iints/analysis/diabetes_metrics.py +43 -0
- iints/analysis/edge_efficiency.py +33 -0
- iints/analysis/edge_performance_monitor.py +315 -0
- iints/analysis/explainability.py +94 -0
- iints/analysis/explainable_ai.py +232 -0
- iints/analysis/hardware_benchmark.py +221 -0
- iints/analysis/metrics.py +117 -0
- iints/analysis/population_report.py +188 -0
- iints/analysis/reporting.py +345 -0
- iints/analysis/safety_index.py +311 -0
- iints/analysis/sensor_filtering.py +54 -0
- iints/analysis/validator.py +273 -0
- iints/api/__init__.py +0 -0
- iints/api/base_algorithm.py +307 -0
- iints/api/registry.py +103 -0
- iints/api/template_algorithm.py +195 -0
- iints/assets/iints_logo.png +0 -0
- iints/cli/__init__.py +0 -0
- iints/cli/cli.py +2598 -0
- iints/core/__init__.py +1 -0
- iints/core/algorithms/__init__.py +0 -0
- iints/core/algorithms/battle_runner.py +138 -0
- iints/core/algorithms/correction_bolus.py +95 -0
- iints/core/algorithms/discovery.py +92 -0
- iints/core/algorithms/fixed_basal_bolus.py +58 -0
- iints/core/algorithms/hybrid_algorithm.py +92 -0
- iints/core/algorithms/lstm_algorithm.py +138 -0
- iints/core/algorithms/mock_algorithms.py +162 -0
- iints/core/algorithms/pid_controller.py +88 -0
- iints/core/algorithms/standard_pump_algo.py +64 -0
- iints/core/device.py +0 -0
- iints/core/device_manager.py +64 -0
- iints/core/devices/__init__.py +3 -0
- iints/core/devices/models.py +160 -0
- iints/core/patient/__init__.py +9 -0
- iints/core/patient/bergman_model.py +341 -0
- iints/core/patient/models.py +285 -0
- iints/core/patient/patient_factory.py +117 -0
- iints/core/patient/profile.py +41 -0
- iints/core/safety/__init__.py +12 -0
- iints/core/safety/config.py +37 -0
- iints/core/safety/input_validator.py +95 -0
- iints/core/safety/supervisor.py +39 -0
- iints/core/simulation/__init__.py +0 -0
- iints/core/simulation/scenario_parser.py +61 -0
- iints/core/simulator.py +874 -0
- iints/core/supervisor.py +367 -0
- iints/data/__init__.py +53 -0
- iints/data/adapter.py +142 -0
- iints/data/column_mapper.py +398 -0
- iints/data/datasets.json +132 -0
- iints/data/demo/__init__.py +1 -0
- iints/data/demo/demo_cgm.csv +289 -0
- iints/data/importer.py +275 -0
- iints/data/ingestor.py +162 -0
- iints/data/nightscout.py +128 -0
- iints/data/quality_checker.py +550 -0
- iints/data/registry.py +166 -0
- iints/data/tidepool.py +38 -0
- iints/data/universal_parser.py +813 -0
- iints/data/virtual_patients/clinic_safe_baseline.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hyper_challenge.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hypo_prone.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_midnight.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_pizza.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_stress_meal.yaml +9 -0
- iints/data/virtual_patients/default_patient.yaml +11 -0
- iints/data/virtual_patients/patient_559_config.yaml +11 -0
- iints/emulation/__init__.py +80 -0
- iints/emulation/legacy_base.py +414 -0
- iints/emulation/medtronic_780g.py +337 -0
- iints/emulation/omnipod_5.py +367 -0
- iints/emulation/tandem_controliq.py +393 -0
- iints/highlevel.py +451 -0
- iints/learning/__init__.py +3 -0
- iints/learning/autonomous_optimizer.py +194 -0
- iints/learning/learning_system.py +122 -0
- iints/metrics.py +34 -0
- iints/population/__init__.py +11 -0
- iints/population/generator.py +131 -0
- iints/population/runner.py +327 -0
- iints/presets/__init__.py +28 -0
- iints/presets/presets.json +114 -0
- iints/research/__init__.py +30 -0
- iints/research/config.py +68 -0
- iints/research/dataset.py +319 -0
- iints/research/losses.py +73 -0
- iints/research/predictor.py +329 -0
- iints/scenarios/__init__.py +3 -0
- iints/scenarios/generator.py +92 -0
- iints/templates/__init__.py +0 -0
- iints/templates/default_algorithm.py +91 -0
- iints/templates/scenarios/__init__.py +0 -0
- iints/templates/scenarios/chaos_insulin_stacking.json +29 -0
- iints/templates/scenarios/chaos_runaway_ai.json +25 -0
- iints/templates/scenarios/example_scenario.json +35 -0
- iints/templates/scenarios/exercise_stress.json +30 -0
- iints/utils/__init__.py +3 -0
- iints/utils/plotting.py +50 -0
- iints/utils/run_io.py +152 -0
- iints/validation/__init__.py +133 -0
- iints/validation/schemas.py +94 -0
- iints/visualization/__init__.py +34 -0
- iints/visualization/cockpit.py +691 -0
- iints/visualization/uncertainty_cloud.py +612 -0
- iints_sdk_python35-0.0.18.dist-info/METADATA +225 -0
- iints_sdk_python35-0.0.18.dist-info/RECORD +118 -0
- iints_sdk_python35-0.0.18.dist-info/WHEEL +5 -0
- iints_sdk_python35-0.0.18.dist-info/entry_points.txt +10 -0
- iints_sdk_python35-0.0.18.dist-info/licenses/LICENSE +28 -0
- iints_sdk_python35-0.0.18.dist-info/top_level.txt +1 -0
iints/data/registry.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import urllib.request
|
|
5
|
+
import zipfile
|
|
6
|
+
import hashlib
|
|
7
|
+
import shutil
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, List, Optional, IO, cast
|
|
10
|
+
|
|
11
|
+
try: # Python 3.9+
|
|
12
|
+
from importlib.resources import files
|
|
13
|
+
except Exception: # pragma: no cover
|
|
14
|
+
files = None # type: ignore
|
|
15
|
+
from importlib import resources
|
|
16
|
+
else:
|
|
17
|
+
from importlib import resources
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DatasetRegistryError(RuntimeError):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DatasetFetchError(RuntimeError):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _read_registry_text() -> str:
|
|
29
|
+
try:
|
|
30
|
+
if files is not None:
|
|
31
|
+
return files("iints.data").joinpath("datasets.json").read_text() # type: ignore[call-arg]
|
|
32
|
+
return resources.read_text("iints.data", "datasets.json")
|
|
33
|
+
except Exception as exc:
|
|
34
|
+
raise DatasetRegistryError(f"Unable to locate datasets.json: {exc}") from exc
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_dataset_registry() -> List[Dict[str, Any]]:
|
|
38
|
+
return json.loads(_read_registry_text())
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_dataset(dataset_id: str) -> Dict[str, Any]:
|
|
42
|
+
for entry in load_dataset_registry():
|
|
43
|
+
if entry.get("id") == dataset_id:
|
|
44
|
+
return entry
|
|
45
|
+
raise DatasetRegistryError(f"Unknown dataset '{dataset_id}'. Run 'iints data list' to see options.")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def list_dataset_ids() -> List[str]:
|
|
49
|
+
ids: List[str] = []
|
|
50
|
+
for entry in load_dataset_registry():
|
|
51
|
+
dataset_id = entry.get("id")
|
|
52
|
+
if isinstance(dataset_id, str) and dataset_id:
|
|
53
|
+
ids.append(dataset_id)
|
|
54
|
+
return ids
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _download_file(url: str, output_path: Path) -> Path:
|
|
58
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
try:
|
|
60
|
+
urllib.request.urlretrieve(url, output_path)
|
|
61
|
+
except Exception as exc:
|
|
62
|
+
raise DatasetFetchError(f"Failed to download {url}: {exc}") from exc
|
|
63
|
+
return output_path
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _sha256(path: Path) -> str:
|
|
67
|
+
digest = hashlib.sha256()
|
|
68
|
+
with path.open("rb") as handle:
|
|
69
|
+
for chunk in iter(lambda: handle.read(8192), b""):
|
|
70
|
+
digest.update(chunk)
|
|
71
|
+
return digest.hexdigest()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _append_checksum(output_dir: Path, filename: str, digest: str) -> None:
|
|
75
|
+
checksum_path = output_dir / "SHA256SUMS.txt"
|
|
76
|
+
with checksum_path.open("a") as handle:
|
|
77
|
+
handle.write(f"{digest} {filename}\n")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _get_expected_hash(dataset: Dict[str, Any], index: int = 0) -> Optional[str]:
|
|
81
|
+
expected = dataset.get("sha256")
|
|
82
|
+
if isinstance(expected, list):
|
|
83
|
+
if index < len(expected):
|
|
84
|
+
return expected[index] or None
|
|
85
|
+
return None
|
|
86
|
+
if isinstance(expected, str):
|
|
87
|
+
return expected or None
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _maybe_extract_zip(path: Path, output_dir: Path) -> None:
|
|
92
|
+
if path.suffix.lower() != ".zip":
|
|
93
|
+
return
|
|
94
|
+
try:
|
|
95
|
+
with zipfile.ZipFile(path, "r") as zip_ref:
|
|
96
|
+
zip_ref.extractall(output_dir)
|
|
97
|
+
except Exception as exc:
|
|
98
|
+
raise DatasetFetchError(f"Failed to extract {path.name}: {exc}") from exc
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _open_bundled_binary(bundled_path: str) -> IO[bytes]:
|
|
102
|
+
if files is not None:
|
|
103
|
+
return cast(IO[bytes], files("iints.data").joinpath(bundled_path).open("rb")) # type: ignore[call-arg]
|
|
104
|
+
parts = bundled_path.split("/")
|
|
105
|
+
if len(parts) > 1:
|
|
106
|
+
package = ".".join(["iints", "data", *parts[:-1]])
|
|
107
|
+
resource = parts[-1]
|
|
108
|
+
return cast(IO[bytes], resources.open_binary(package, resource))
|
|
109
|
+
return cast(IO[bytes], resources.open_binary("iints.data", bundled_path))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def fetch_dataset(
|
|
113
|
+
dataset_id: str,
|
|
114
|
+
output_dir: Path,
|
|
115
|
+
extract: bool = True,
|
|
116
|
+
verify: bool = True,
|
|
117
|
+
) -> List[Path]:
|
|
118
|
+
dataset = get_dataset(dataset_id)
|
|
119
|
+
urls = dataset.get("download_urls") or []
|
|
120
|
+
access = dataset.get("access", "manual")
|
|
121
|
+
|
|
122
|
+
if access == "bundled":
|
|
123
|
+
bundled_path = dataset.get("bundled_path")
|
|
124
|
+
if not bundled_path:
|
|
125
|
+
raise DatasetFetchError("Bundled dataset missing bundled_path entry.")
|
|
126
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
127
|
+
target = output_dir / Path(bundled_path).name
|
|
128
|
+
# Traversable may not be a real filesystem path; stream bytes instead.
|
|
129
|
+
try:
|
|
130
|
+
with _open_bundled_binary(bundled_path) as src, target.open("wb") as dst:
|
|
131
|
+
shutil.copyfileobj(src, dst)
|
|
132
|
+
except Exception as exc:
|
|
133
|
+
raise DatasetFetchError(f"Unable to locate bundled dataset: {exc}") from exc
|
|
134
|
+
expected = _get_expected_hash(dataset, 0)
|
|
135
|
+
if verify and expected:
|
|
136
|
+
actual = _sha256(target)
|
|
137
|
+
if actual != expected:
|
|
138
|
+
raise DatasetFetchError(f"SHA-256 mismatch for {target.name}. Expected {expected}, got {actual}.")
|
|
139
|
+
_append_checksum(output_dir, target.name, actual)
|
|
140
|
+
elif verify and expected is None:
|
|
141
|
+
actual = _sha256(target)
|
|
142
|
+
_append_checksum(output_dir, target.name, actual)
|
|
143
|
+
return [target]
|
|
144
|
+
|
|
145
|
+
if not urls:
|
|
146
|
+
raise DatasetFetchError(
|
|
147
|
+
"This dataset requires manual download or approval. Use 'iints data info' for instructions."
|
|
148
|
+
)
|
|
149
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
150
|
+
downloaded: List[Path] = []
|
|
151
|
+
for idx, url in enumerate(urls):
|
|
152
|
+
filename = url.split("/")[-1]
|
|
153
|
+
target = output_dir / filename
|
|
154
|
+
downloaded.append(_download_file(url, target))
|
|
155
|
+
expected = _get_expected_hash(dataset, idx)
|
|
156
|
+
if verify and expected:
|
|
157
|
+
actual = _sha256(target)
|
|
158
|
+
if actual != expected:
|
|
159
|
+
raise DatasetFetchError(f"SHA-256 mismatch for {target.name}. Expected {expected}, got {actual}.")
|
|
160
|
+
_append_checksum(output_dir, target.name, actual)
|
|
161
|
+
elif verify and expected is None:
|
|
162
|
+
actual = _sha256(target)
|
|
163
|
+
_append_checksum(output_dir, target.name, actual)
|
|
164
|
+
if extract:
|
|
165
|
+
_maybe_extract_zip(target, output_dir)
|
|
166
|
+
return downloaded
|
iints/data/tidepool.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
|
+
import json
|
|
6
|
+
import urllib.request
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class TidepoolClient:
|
|
11
|
+
"""
|
|
12
|
+
Lightweight client skeleton based on the Tidepool OpenAPI spec.
|
|
13
|
+
Authentication and full endpoint coverage are intentionally left open
|
|
14
|
+
so teams can wire in their preferred auth flow.
|
|
15
|
+
"""
|
|
16
|
+
base_url: str = "https://api.tidepool.org"
|
|
17
|
+
token: Optional[str] = None
|
|
18
|
+
|
|
19
|
+
def _headers(self) -> Dict[str, str]:
|
|
20
|
+
headers = {"Accept": "application/json"}
|
|
21
|
+
if self.token:
|
|
22
|
+
headers["Authorization"] = f"Bearer {self.token}"
|
|
23
|
+
return headers
|
|
24
|
+
|
|
25
|
+
def get_json(self, path: str) -> Dict[str, Any]:
|
|
26
|
+
if not self.token:
|
|
27
|
+
raise RuntimeError("TidepoolClient requires an auth token. Provide one before calling.")
|
|
28
|
+
url = self.base_url.rstrip("/") + "/" + path.lstrip("/")
|
|
29
|
+
req = urllib.request.Request(url, headers=self._headers(), method="GET")
|
|
30
|
+
with urllib.request.urlopen(req) as response: # nosec - thin skeleton
|
|
31
|
+
payload = response.read().decode("utf-8")
|
|
32
|
+
return json.loads(payload)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def load_openapi_spec(path: str) -> Dict[str, Any]:
|
|
36
|
+
"""Load a local Tidepool OpenAPI JSON spec for reference tooling."""
|
|
37
|
+
with open(path, "r", encoding="utf-8") as handle:
|
|
38
|
+
return json.load(handle)
|