Metadata-Version: 2.4
Name: netiob
Version: 0.1.37
Summary: NET IOB
Requires-Python: <3.13,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.22.0
Requires-Dist: pandas>=1.3.0
Requires-Dist: requests>=2.25.0
Requires-Dist: python-decouple>=3.6
Requires-Dist: plotly>=5.9.0
Dynamic: license-file

# NetIOB (`netiob`) Calculation and Blood Glucose Prediction Library

netiob is an advanced Python package for **automated blood glucose prediction and insulin-on-board (IOB) calculation** built upon the OpenAPS oref0 reference algorithm. This toolkit enables researchers, data scientists, and developers to preprocess diabetes management data, compute net IOB, forecast glucose, and visually analyze prediction scenarios.

## Core Features

- **Blood Glucose (BG) Prediction**: End-to-end pipeline using the OpenAPS oref0 determine-basal algorithm.
- **Net Insulin On Board (IOB)**: Robust IOB calculation incorporating basal rate variations, boluses, and autosensitivity analysis.
- **Data Preprocessing Utilities**: Transform raw pump, CGM, and nutrition logs into SDTM-compliant, validated formats.
- **Prediction Graphs**: Inbuilt support (via Matplotlib/Plotly) for visualizing BG forecasting results and insulin activity.
- **Core Utilities**: Comprehensive API bridge, datetime handling, profile assembly, autosensitivity computation, and more.

## Requirements

- Python >= 3.12, < 3.13
- NumPy >= 1.22.0
- Pandas >= 1.3.0
- Requests >= 2.25.0
- python-decouple >= 3.6
- Plotly >= 5.9.0
- A running OpenAPS oref0 HTTP API server (remote endpoint for the core IOB / determine-basal calculations)

_The single source of truth for dependencies is `pyproject.toml`; resolved versions are pinned in `uv.lock`._

- **Runtime configuration** (read by `netiob/settings.py` via `python-decouple`):
  - `OREF0_API_SERVER_URL` — base URL of the oref0 HTTP server (default `http://localhost:3000`).
  - `MAX_WORKERS` — thread-pool size for the parallel net-IOB calculator (default `4`).

- **oref0 dependency:**
`netiob` does not implement the IOB / determine-basal math itself — it preprocesses data into oref0-compatible payloads and calls a running oref0 server, which performs the core JavaScript computations. A reachable oref0 server is therefore a hard runtime dependency. Get the oref0 repo here: https://github.com/openaps/oref0/tree/master

## Installation

### PyPI
```shell
pip install netiob
```

### From source (with uv)
This project is managed with [uv](https://docs.astral.sh/uv/). Clone the repo and sync the managed virtualenv to `uv.lock`:

```shell
git clone https://gitlab.com/CeADARIreland/UCD/con/netiob-package.git
cd netiob
uv sync                            # create/update .venv to match uv.lock
uv run python -c "import netiob"   # run anything inside the managed env
```

### From source (with pip)
```shell
git clone https://gitlab.com/CeADARIreland/UCD/con/netiob-package.git
cd netiob
pip install .
```

## Makefile commands

The project ships a `Makefile` wrapping the common `uv` workflows.

| Command | Description |
|---|---|
| `make setup-local` | Sync the managed virtualenv to `uv.lock` (`uv sync`). |
| `make run-tests-all` | Run the full deterministic suite (unit + contract + cross-cutting). Integration tests are deselected so the run needs no external services. |
| `make run-tests-integration` | Run only the integration tier against a live `oref0` server. Tests self-skip if `OREF0_API_SERVER_URL` is unreachable. |
| `make build-dist` | Bump the version (`BUMP=patch` by default) and build a wheel. |
| `make build-dist-publish` | Clean `dist/`, rebuild, and upload to PyPI via `twine`. |

```shell
make run-tests-all                 # fast, no network — use as the default gate
make run-tests-integration         # requires a reachable oref0 server
make build-dist BUMP=minor         # override the version bump segment
```

Test tiers and pass/fail criteria are described in `tests/TEST_PLAN.md`; worked
input/output vectors for the computational core are in `tests/TEST_DATA.md`.

## Import
```python
# Core modules
from netiob.utils.coreutils import *
from netiob.utils.calculators import calculate_net_iob
from netiob.utils.openapsprediction import OpenapsPredictor
from netiob.utils.preprocessors import preprocess_basal_data, preprocess_bolus_data, preprocess_carbs_data, preprocess_cgm_data, preprocess_insulin_data
from netiob.utils.cgmdataprocessor import CGMDataProcessor
from netiob.utils.bgpredgraph import bg_prediction_graph
```
## Functionalities

### 1. Core Utilities (`coreutils.py`)
- **API Communication**: `call_api(endpoint, payload)`
- **Datetime Handling**: `parse_iso_utc`, `to_iso_z`, etc.
- **Profile Construction**: `get_profile_inputs`, `get_profile_settings`
- **Pump History Utilities**: `get_pump_history`, `get_carb_history`
- **Autosensitivity Calculation**: `get_autosens`
- **Data Serialization**: `make_json_serializable()`

### 2. NetIOB Calculator (`calculators.py`)
- Parallel net IOB calculation at 5-min intervals via OpenAPS oref0 API
- Handles both bolus and basal variabilities (including temp basals vs. scheduled)
- Example usage:
```python
from netiob.utils.calculators import calculatenetiob
iob_series = calculate_net_iob(basal_df, bolus_df, cgm_df, profile_df, autosens)
```
- Returns a list of dicts for each time point, including keys: `iob, activity, basaliob, bolusiob, time, lastTemp`

### 3. Preprocessors (`preprocessors.py`)
- Preprocesses CGM, basal, bolus, and meal data into standardized DataFrames
- Chunks long basal records; distributes extended boluses; aligns with OpenAPS SDTM data conventions
- Entry points: `preprocess_basal_data(basal_df)`, `preprocess_bolus_data(bolus_df)`, `preprocess_cgm_data(cgm_df)`, `preprocess_carbs_data(carbs_df)`
- Entry point to process insulin data ready for netiob calculation and prediction: `preprocess_insulin_data(processed_basal, processed_bolus)`
- Entry point to process and access all processed data at once: `preprocess_user_data(basal_df, bolus_df, carbs_df, cgm_df)`. Returns a tuple of 5 (objects) processed data.

### 4. CGM Data Processor (`cgmdataprocessor.py`)
- Orchestrates the transformation of raw user data into oref0-compatible input using the `preprocessor.py`
- Access processed DataFrames and prediction-ready structures:
- Pools and provides netiob and prediction required data as objects
```python
processor = CGMDataProcessor(basal_df, bolus_df, carbs_df, cgm_df, profile_df)
processed_glucose_data = processor.glucose_data  # ready for API call
processed_basal = processor.proc_basal
```

### 5. Blood Glucose Prediction (`openapsprediction.py`)
- **OpenapsPredictor**: Automated pipeline for forecasting BG, carbs-on-board (COB), IOB, insulin needs, and clinical recommendations using oref0 APIs
```python
from netiob.utils.openapsprediction import OpenapsPredictor
predictor = OpenapsPredictor(basal_df, bolus_df, carbs_df, cgm_df, profile_df)
result = predictor.predict_bg(currenttemp, clock, simcarb)
```
- Returns comprehensive prediction dict: `eventualBG`, `predBGs`, `rate`, `reason`, and more

### 6. Graph Plotting (`bgpredgraph.py`)
- **Plot BG prediction scenarios**:
```python
from netiob.utils.bgpredgraph import bg_prediction_graph
fig = bg_prediction_graph(result, scenario_label="BG Prediction", fig_show=True)
```
- Generates interactive or static charts using Plotly/Matplotlib, highlighting:
    - IOB activity
    - COB/UAM scenarios
    - Target BG lines
    - Safest prediction curves

### Example Pipeline Usage
- **Import and Load Dataframes**
```python
import pandas as pd
from netiob.utils.cgmdataprocessor import CGMDataProcessor
from netiob.utils.openapsprediction import OpenapsPredictor
from netiob.utils.bgpredgraph import bg_prediction_graph
from netiob.utils.calculators import calculate_net_iob

# Load user data into DataFrames
# Note: Data can be transformed from any data sources. Only ensure they are in DataFrame format
basal_df = pd.read_csv("basal.csv")
bolus_df = pd.read_csv("bolus.csv")
carbs_df = pd.read_csv("carbs.csv")
cgm_df = pd.read_csv("cgm.csv")
profile_df = pd.read_csv("profile.csv")
```
- **Preprocess**
```python
# Create an instance of CGMDataProcessor.
# See code file for full documentation on all objects of CGMDataProcessor
dataprocessor = CGMDataProcessor(basal_df, bolus_df, carbs_df, cgm_df, profile_df=profile)

# Get the objects of dataprocessor (Below are all the objects of CGMDataProcessor)
dataprocessor.proc_basal        # Processed basal data 
dataprocessor.proc_bolus        # Processed bolus data
dataprocessor.proc_carbs        # Processed carbs data
dataprocessor.proc_glucose      # Processed glucose data
dataprocessor.proc_insulin      # processed insulin data
dataprocessor.glucose_data      # Glucosed data structured in the required dict for OpenAPS prediction
dataprocessor.clock             # CGM last data event timestamp (for prediction)
dataprocessor.pumphistory       # Insulin data structured in the required dict (for netiob calculation and prediction)
dataprocessor.pump_clock        # Last insulin data event timestamp
dataprocessor.settings          # Useer settings in structured dict for prediction
dataprocessor.bg_targets        # BG target dict extracted from settings
dataprocessor.basal_profile     # 24-hours (hourly) baseline user basal profile dict extrapolated from user profile settings
dataprocessor.sensitivities     # Sensitivities data extracted from profile settings
dataprocessor.profile_carbs     # Carbs dict based on user profile
dataprocessor.profile           # Entire user profile structure dict needed for netiob calculation and prediction
datprocessor.autosens           # autosensitivity ratio dict calculated using oref0 API
```
- **Prediction**
```python
predictor = OpenapsPredictor(proc_basal, proc_bolus, proc_carbs, proc_cgm, profile_df)
prediction_result = predictor.predict_bg(currenttemp={}, clock="2025-11-07T16:00:00Z")
    
# Note: Synthetic carbs entry (in grams) can be simulated for prediction. In such case, pass (e.g., 5g) carbs as thus:
prediction_result = predictor.predict_bg(currenttemp={}, clock="2025-11-07T16:00:00Z", sim_carb=5)
```
- **Visualization**
```python
bg_prediction_graph(prediction_result, scenario_label="BG Prediction After Meal")
```
- **Calculate NetIOB**
```python
# Note, profile_df and autosens can be None. 
# In such case, a default baseline basal profile will be calculated based on insulin history and default auto sensitivity ratio will be used.
calculate_net_iob(basal_df, bolus_df, carbs_df, cgm_df, profile_df, austosens={})
```

## Graph Output

- The `bg_prediction_graph` function visualizes predicted curves (IOB, COB/UAM, ZT) and overlays "eventual BG" markers and target ranges.

## Contributing

- Create issues or merge requests on GitLab for improvements.
- Follow coding conventions and update docstrings.

## License

This project is licensed under the Apache License 2.0. For more details, please see the LICENSE file in the repository.

---

### Maintainers
CeADAR - Ireland's Centre for AI
