# Ambulatory glucose profile analysis tool

[![Tests](https://github.com/daedalus/agp_tool/actions/workflows/tests.yml/badge.svg)](https://github.com/daedalus/agp_tool/actions/workflows/tests.yml)[![Codacy Badge](https://app.codacy.com/project/badge/Grade/f1bfcd04a9954c6792af145397b437fc)](https://app.codacy.com/gh/daedalus/agp_tool/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)![License](https://img.shields.io/badge/license-MIT-green)

## DISCLAIMER

This tool is for research and educational purposes only. It is NOT a medical device and has NOT been validated for clinical diagnosis or treatment decisions.

Do not adjust medications, change diet, or make health decisions based solely on this output. Exercise caution and always consult a qualified healthcare professional for interpretation of glucose data and any treatment adjustments.

## Overview

This tool generates a comprehensive Ambulatory Glucose Profile (AGP) with extended clinical metrics, including Time in Tight Range (TITR) metric. It processes continuous glucose monitoring (CGM) data and produces both a visual AGP plot and detailed statistical analysis.

This application should work with data from any CGM, however it was only tested with Sibionics GS1. For other models we need more data.

## Features

### General

- Full AGP visualization with percentile curves (5-95%, IQR)
- Rate of Change (ROC) profile
- Circadian binning for time-of-day patterns
- Circadian glucose heatmap
- Daily overlay graph (each day as a separate colored line)

### Time in Range metrics with level breakdowns

- TIR (70-180 mg/dL)
- TITR (70-140 mg/dL) - Tight target
- TAR with Level 1 (181-250) and Level 2 (>250)
- TBR with Level 1 (54-69) and Level 2 (<54)

### Advanced variability metrics

- MAGE (Mean Amplitude of Glycemic Excursions)
- MODD (Mean of Daily Differences)
- CONGA (Continuous Overall Net Glycemic Action)
- MAG (Mean Absolute Glucose rate of change)
- Multi-lag CONGA (1h, 2h, 4h, 24h)
- GVP (Glucose Variability Percentage)
- Lability Index
- CVrate (CV of rate of change)

### Risk indices

- LBGI (Low Blood Glucose Index)
- HBGI (High Blood Glucose Index)
- ADRR (Average Daily Risk Range)
- GRI (Glycemia Risk Indicator)
- Hypo Index / Hyper Index (Rodbard exposure indices)
- M-Value (Schlichtkrull)

### Composite indices

- GRADE score with hypo/eu/hyper % breakdown
- eA1c (Nathan/DCCT formula, distinct from GMI)
- Percentile profile scalars (p5, p25, p50, p75, p95, IQR)
- Hourly TIR breakdown (00–23)

### AUC analysis

- total, above range, below range

### Data quality assessment

- wear time, reading frequency

## Installation

Install from PyPI:

```
pip install agp_tool
```

Install from source (editable mode, recommended for development):

```
pip install -e .
```

Or install the dependencies only:

```
pip install -r requirements.txt
```

## Building and Publishing

### Building locally

Install the `build` tool and build sdist + wheel:

```
pip install build
python -m build
```

The artifacts will be placed in the `dist/` directory.  Run `twine check dist/*`
to verify the distribution metadata before publishing.

### Publishing to PyPI

Releases are published to PyPI automatically via GitHub Actions using
[Trusted Publishing (OIDC)](https://docs.pypi.org/trusted-publishers/).  To
trigger a release:

1. Create and push a git tag (e.g. `v1.0.0`).
2. Create a GitHub Release from that tag.
3. The `Publish to PyPI` workflow will build and upload the distribution
   artifacts automatically — no API token required.

## Library usage

`agp` is designed to be used both as a CLI tool and as an importable Python
library.  It exposes two complementary APIs:

* **`ReportGenerator` class** – instantiable object that accepts an input
  file at construction time, computes all metrics immediately, and exposes
  them as instance attributes.  Graph generation and export are available
  as methods so they can be invoked on demand.
* **`generate_report` function** – a thin wrapper around `ReportGenerator`
  that provides full backward compatibility.

### Class-based API (recommended)

```python
import matplotlib
matplotlib.use("Agg")  # use non-interactive backend when no display is available

from agp import ReportGenerator

# Instantiate with an input file (all config params are keyword-only)
report = ReportGenerator("data.csv", patient_name="Jane Doe", patient_id="P-001")

# ── Access metrics ─────────────────────────────────────────────────────────
# Individual metric attributes (computed once at construction time)
print(f"Time in Range: {report.tir:.1f}%")
print(f"Mean glucose:  {report.mean_glucose:.1f} mg/dL")
print(f"GMI:           {report.gmi:.2f}%")
print(f"GRI:           {report.gri:.1f} ({report.gri_txt})")

# Full metrics dict
m = report.metrics          # property
m = report.get_metrics()    # equivalent callable form

# ── Generate graphs ────────────────────────────────────────────────────────
fig_agp   = report.plot_agp(output="agp.png")       # main AGP figure
fig_daily = report.plot_daily(output="daily.png")   # daily overlay figure

# Figures are standard matplotlib Figure objects
fig_agp.savefig("agp_hires.png", dpi=300, bbox_inches="tight")

# Omit output path to use the default (set at construction time)
fig = report.plot_agp()   # saves to "ambulatory_glucose_profile.png"

# ── Print clinical summary ─────────────────────────────────────────────────
report.print_summary()

# ── Export metrics to file ─────────────────────────────────────────────────
report.export("metrics.json")   # JSON
report.export("metrics.csv")    # CSV
report.export("metrics.xlsx")   # Excel

# ── Custom thresholds and options ──────────────────────────────────────────
report = ReportGenerator(
    "data.xlsx",
    output="my_report.png",
    low_threshold=65,
    high_threshold=200,
    patient_name="Jane Doe",
    patient_id="P-001",
    doctor="Dr. Smith",
    heatmap=True,
    heatmap_cmap="coolwarm",
)
fig = report.plot_agp()
fig_daily = report.plot_daily()
```

#### `ReportGenerator` parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `input_file` | str | *(required)* | Path to glucose data file |
| `output` | str | `"ambulatory_glucose_profile.png"` | Default output PNG path used by `plot_agp()` |
| `very_low_threshold` | int | 54 | Very low glucose threshold (mg/dL) |
| `low_threshold` | int | 70 | Low glucose threshold (mg/dL) |
| `high_threshold` | int | 180 | High glucose threshold (mg/dL) |
| `very_high_threshold` | int | 250 | Very high glucose threshold (mg/dL) |
| `tight_low` | int | 70 | Tight range lower limit (mg/dL) |
| `tight_high` | int | 140 | Tight range upper limit (mg/dL) |
| `bin_minutes` | int | 5 | Circadian bin size in minutes |
| `sensor_interval` | int | 5 | CGM sensor interval in minutes |
| `min_samples` | int | 5 | Minimum samples per bin |
| `verbose` | bool | `False` | Print detailed progress |
| `config` | str\|None | `None` | JSON config file path |
| `patient_name` | str | `"Unknown"` | Patient name for report header |
| `patient_id` | str | `"N/A"` | Patient ID for report header |
| `doctor` | str | `""` | Doctor name for report header |
| `notes` | str | `""` | Additional notes for report header |
| `heatmap` | bool | `False` | Enable circadian glucose heatmap in `plot_agp()` |
| `heatmap_cmap` | str | `"RdYlGn_r"` | Colormap for the heatmap |
| `dark_mode` | bool | `False` | Use dark background theme for all plots |

#### `ReportGenerator` methods and properties

| Member | Description |
|--------|-------------|
| `report.metrics` | Full computed metrics dict (property) |
| `report.get_metrics()` | Same as above (callable form) |
| `report.<metric_name>` | Any individual metric, e.g. `report.tir`, `report.mean_glucose` |
| `report.plot_agp(output, show, close)` | Generate and return the main AGP figure |
| `report.plot_daily(output, show, close)` | Generate and return the daily overlay figure |
| `report.print_summary()` | Print the clinical summary to stdout |
| `report.export(path)` | Export metrics to `.json`, `.csv`, or `.xlsx` |

### Function-based API (backward compatible)

`generate_report` is retained for backward compatibility as a thin wrapper
around `ReportGenerator`.  The public entry-point is `generate_report`,
which accepts every option available in the CLI and returns a
`matplotlib.figure.Figure`.

```python
import matplotlib
matplotlib.use("Agg")          # use non-interactive backend when no display is available

from agp import generate_report

# Basic call – returns a Figure and saves ambulatory_glucose_profile.png
fig = generate_report("data.csv")

# Custom thresholds, patient info, and export
fig = generate_report(
    "data.xlsx",
    output="my_report.png",
    low_threshold=65,
    high_threshold=200,
    patient_name="Jane Doe",
    patient_id="P-001",
    doctor="Dr. Smith",
    export="metrics.json",
)

# The returned Figure can be used directly with matplotlib
fig.savefig("report.png", dpi=150, bbox_inches="tight")

# Skip plot generation (returns None)
result = generate_report("data.csv", no_plot=True)
assert result is None

# Enable the circadian heatmap
fig = generate_report("data.csv", heatmap=True, heatmap_cmap="coolwarm")

# Generate the main AGP report and a daily overlay graph
fig = generate_report("data.csv", daily_plot=True)

# Show interactively (e.g. in a Jupyter notebook)
fig = generate_report("data.csv", show=True)
```

`generate_report` does **not** call `plt.show()` or `plt.close()` by default,
so it is safe to use inside automated pipelines and Jupyter notebooks without
popping up GUI windows.

### Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `input_file` | str | *(required)* | Path to glucose data file |
| `output` | str | `"ambulatory_glucose_profile.png"` | Output PNG path |
| `very_low_threshold` | int | 54 | Very low glucose threshold (mg/dL) |
| `low_threshold` | int | 70 | Low glucose threshold (mg/dL) |
| `high_threshold` | int | 180 | High glucose threshold (mg/dL) |
| `very_high_threshold` | int | 250 | Very high glucose threshold (mg/dL) |
| `tight_low` | int | 70 | Tight range lower limit (mg/dL) |
| `tight_high` | int | 140 | Tight range upper limit (mg/dL) |
| `bin_minutes` | int | 5 | Circadian bin size in minutes |
| `sensor_interval` | int | 5 | CGM sensor interval in minutes |
| `min_samples` | int | 5 | Minimum samples per bin |
| `no_plot` | bool | `False` | Skip plot; return `None` |
| `verbose` | bool | `False` | Print detailed progress |
| `export` | str | `""` | Export metrics to `.json` / `.csv` path |
| `config` | str\|None | `None` | JSON config file path (same as CLI `--config`) |
| `patient_name` | str | `"Unknown"` | Patient name for report header |
| `patient_id` | str | `"N/A"` | Patient ID for report header |
| `doctor` | str | `""` | Doctor name for report header |
| `notes` | str | `""` | Additional notes for report header |
| `heatmap` | bool | `False` | Enable circadian glucose heatmap |
| `heatmap_cmap` | str | `"RdYlGn_r"` | Colormap for the heatmap |
| `pdf` | bool | `False` | Also produce a PDF alongside the PNG |
| `daily_plot` | bool | `False` | Generate a daily overlay plot (each day as a separate colored line, saved as `<stem>_daily.<ext>`) |
| `show` | bool | `False` | Call `plt.show()` (interactive use only) |
| `close` | bool | `False` | Call `plt.close()` after building figure |
| `dark_mode` | bool | `False` | Use dark background theme for all plots |

## Requirements

### Packages

`pip install -r requirements.txt`

Dependencies include `pandas`, `numpy`, `matplotlib`, `openpyxl` (xlsx), `xlrd` (xls), `odfpy` (ods), `Pillow` (image reading), and `fpdf2` (PDF generation).

## Input Data Format

Supported file formats:

| Extension | Format |
|-----------|--------|
| `.xlsx` | Excel 2007+ |
| `.xls` | Excel 97-2003 |
| `.csv` | Comma-separated values |
| `.ods` | OpenDocument Spreadsheet |

## Required columns

- **Date time** 
- **Sensor Reading(mg/dL)**: numeric glucose values

## Usage

```
usage: agp_tool [-h] [--output OUTPUT] [--very-low-threshold VERY_LOW_THRESHOLD] [--low-threshold LOW_THRESHOLD] [--high-threshold HIGH_THRESHOLD]
               [--very-high-threshold VERY_HIGH_THRESHOLD] [--tight-low TIGHT_LOW] [--tight-high TIGHT_HIGH] [--bin-minutes BIN_MINUTES]
               [--sensor-interval SENSOR_INTERVAL] [--min-samples MIN_SAMPLES] [--no-plot] [--verbose] [--export EXPORT] [--config CONFIG] [--version]
               [--patient-name PATIENT_NAME] [--patient-id PATIENT_ID] [--doctor DOCTOR] [--notes NOTES] [--heatmap] [--heatmap-cmap HEATMAP_CMAP]
               [--pdf]
               input_file

Generate Ambulatory Glucose Profile from sensor data

positional arguments:
  input_file            Path to glucose data file (.xlsx, .xls, .csv, .ods)

options:
  -h, --help            show this help message and exit
  --output, -o OUTPUT   Output PNG filename (default: ambulatory_glucose_profile.png)
  --very-low-threshold VERY_LOW_THRESHOLD
                        Very low glucose threshold in mg/dL (default: 54)
  --low-threshold LOW_THRESHOLD
                        Low glucose threshold in mg/dL (default: 70)
  --high-threshold HIGH_THRESHOLD
                        High glucose threshold in mg/dL (default: 180)
  --very-high-threshold VERY_HIGH_THRESHOLD
                        Very high glucose threshold in mg/dL (default: 250)
  --tight-low TIGHT_LOW
                        Tight range lower limit in mg/dL (default: 70)
  --tight-high TIGHT_HIGH
                        Tight range upper limit in mg/dL (default: 140)
  --bin-minutes BIN_MINUTES
                        Time bin size in minutes for AGP (default: 5)
  --sensor-interval SENSOR_INTERVAL
                        CGM Sensor interval (default: 5)
  --min-samples MIN_SAMPLES
                        Minimum samples per bin (default: 5)
  --no-plot             Calculate metrics only, do not generate plot
  --verbose, -v         Print detailed metrics during execution
  --export, -e EXPORT   Export metrics to file. Use .csv or .json extension (e.g. metrics.json)
  --config, -c CONFIG   Configuration file with parameters
  --version             show program's version number and exit
  --patient-name, -n PATIENT_NAME
                        Patient name for report header
  --patient-id, -id PATIENT_ID
                        Patient ID for report header
  --doctor, -d DOCTOR   Doctor name for report header
  --notes, -note NOTES  Additional notes for report header
  --heatmap             Enable the circadian glucose heatmap (disabled by default)
  --heatmap-cmap HEATMAP_CMAP
                        Colormap for circadian heatmap (default: RdYlGn_r, requires --heatmap)
  --pdf                 Also produce a PDF file with the PNG embedded as an image and metadata copied from the PNG. The PDF page size matches the source PNG dimensions exactly (derived from the PNG pHYs DPI metadata, defaulting to 72 DPI), with no margins, so the image is never cropped.
  --daily-plot          Generate an additional daily overlay plot where each day is shown as a separate colored line
  --dark-mode           Use a dark background theme for all generated plots
```

### Examples

Basic usage

`agp_tool data.xlsx`

Or with CSV/ODS input

`agp_tool data.csv`

`agp_tool data.ods`

Custom output file and thresholds

`agp_tool data.xlsx -o my_agp.png --low-threshold 65 --high-threshold 200`

Custom tight range and bin size

`agp_tool data.xlsx --tight-low 80 --tight-high 150 --bin-minutes 10`

Calculate only metrics, no plot

`agp_tool data.xlsx --no-plot --verbose`

Generate PNG and also export a PDF

`agp_tool data.xlsx --pdf`

Generate the main AGP report **and** a daily overlay graph

`agp_tool data.xlsx --daily-plot`

The daily overlay is saved as `<output-stem>_daily.<ext>` (e.g. `ambulatory_glucose_profile_daily.png`).  Each calendar day in the dataset is drawn as a separate colored line so that day-to-day glucose patterns can be compared at a glance.

With config file

`agp_tool data.xlsx --config my_settings.json`

See all options

`agp_tool -h`

### Example output

![Example](examples/ambulatory_glucose_profile.png)

## Metrics Explained

### Core Statistics

| Metric | Description | Clinical Target |
|--------|-------------|-----------------|
| TIR | Time in Range (70–180 mg/dL) | ≥70% |
| TITR | Time in Tight Range (70–140 mg/dL) | ≥50% |
| TBR | Time Below Range (<70 mg/dL) | <4% |
| TAR | Time Above Range (>180 mg/dL) | <25% |
| CV | Coefficient of Variation | <36% |
| GMI | Glucose Management Indicator (estimated HbA1c from CGM mean) | <7% |
| eA1c | Estimated A1c — Nathan/DCCT formula: (mean + 46.7) / 28.7 | <7% |
| J-Index | Combined mean + variability index: 0.001 × (mean + SD)² | <20 |
| GRI | Glycemia Risk Indicator — weighted composite of hypo/hyper exposure | 0–100 (lower better) |

### Percentile Profile

| Metric | Description |
|--------|-------------|
| p5, p25, p50, p75, p95 | 5th, 25th, 50th (median), 75th, 95th percentile of all glucose readings |
| IQR | Interquartile range = p75 − p25; core spread of the AGP profile |

### Variability Metrics

| Metric | Description |
|--------|-------------|
| MAGE | Mean Amplitude of Glycemic Excursions — average of oscillations exceeding 1 SD |
| MODD | Mean of Daily Differences — day-to-day glucose reproducibility |
| CONGA(1h/2h/4h/24h) | Continuous Overall Net Glycemic Action at 1-, 2-, 4-, and 24-hour lags |
| MAG | Mean Absolute Glucose rate of change — mean \|ΔG/Δt\| in mg/dL/hr |
| GVP | Glucose Variability Percentage — extra trace length vs. a flat baseline, expressed as % |
| Lability Index | Sum of squared successive glucose differences divided by time; sensitive to rapid swings |
| CVrate | Coefficient of variation of the glucose rate-of-change series |
| ADRR | Average Daily Risk Range — daily maximum risk excursion (Kovatchev) |

### Risk Indices

| Metric | Description |
|--------|-------------|
| LBGI | Low Blood Glucose Index — weighted hypoglycaemia risk score |
| HBGI | High Blood Glucose Index — weighted hyperglycaemia risk score |
| Hypo Index | Rodbard hypoglycaemia exposure index: mean ((LOW − g) / LOW)² × 100 for g < LOW |
| Hyper Index | Rodbard hyperglycaemia exposure index: mean ((g − HIGH) / HIGH)² × 100 for g > HIGH |
| M-Value | Schlichtkrull M-value — mean \|10 × log₁₀(g / 120)\|³; penalises deviations from 120 mg/dL |

### GRADE

| Metric | Description |
|--------|-------------|
| GRADE | Glycaemic Risk Assessment in Diabetes Equation — 0–50 composite quality score |
| GRADE Hypo % | % contribution from readings below 70 mg/dL |
| GRADE Euglycaemia % | % contribution from readings 70–140 mg/dL |
| GRADE Hyper % | % contribution from readings above 140 mg/dL |

### AUC Metrics

| Metric | Description |
|--------|-------------|
| Time-weighted avg | AUC_total / duration — equivalent to mean glucose weighted by time |
| Hyperglycaemia exposure | AUC above HIGH as % of total AUC |
| Hypoglycaemia exposure | AUC below LOW as % of total AUC |
| Severe hypo exposure | AUC below VERY_LOW as % of total AUC |

### Hourly TIR

| Metric | Description |
|--------|-------------|
| TIR by hour | Percentage of readings within LOW–HIGH for each clock hour (00–23) |

### Data Quality

| Metric | Description | Warning Threshold |
|--------|-------------|-------------------|
| Readings/day | Average daily CGM readings | <24/day |
| Wear time | % of possible readings captured | <70% |
| Severe hypo/week | Events <40 mg/dL per week | Any |

## Configuration

Key parameters at script top:

- LOW = 70            # Lower bound standard range
- HIGH = 180            # Upper bound standard range
- TIGHT_LOW = 70        # Lower bound tight range
- TIGHT_HIGH = 140        # Upper bound tight range
- BIN_MINUTES = 5        # Time bin size for AGP
- ROC_CLIP = 10        # Rate of change physiological limit

## Limitations & Warnings

- Minimum data: AGP typically requires ≥5 days for reliability
- Data gaps: Long gaps (>2 hours) may affect MODD and ADRR calculations
- Sensor accuracy: Assumes CGM-grade data; fingerstick data may have limitations
- MAGE calculation: Uses smoothed data; may differ from manual calculation

## Interpretation Tips

- TITR >50% suggests acceptable glycemic control
- TBR >4% indicates need for hypoglycemia prevention
- CV >36% suggests unstable glucose, consider variability-reducing therapy
- Nighttime patterns: Shaded area helps identify nocturnal hypoglycemia; however, a sharp "V-shaped" drop with a rapid recovery may indicate sensor compression artifact rather than true hypoglycemia
- ROC spikes indicate rapid changes; correlate with meals/exercise
- GMI < 7.0%: Suggests acceptable glycemic control (Note: GMI estimates A1c from CGM data, but may differ from lab A1c).

## References

Continuous Glucose Monitoring (CGM) Metrics – Verified References

### Ambulatory Glucose Profile (AGP)

1. Mazze RS, Lucido D, Langer O, Hartmann K, Rodbard D.  
   *Ambulatory glucose profile: representation of verified self-monitored blood glucose data.*  
   **Diabetes Care.** 1987;10(1):111–117.  
   DOI: 10.2337/diacare.10.1.111

2. Mazze RS, Strock E, Wesley D, Borgman S, Morgan B, Bergenstal R, Cuddihy R.  
   *Characterizing glucose exposure for individuals with normal glucose tolerance using continuous glucose monitoring and ambulatory glucose profile analysis.*  
   **Diabetes Technology & Therapeutics.** 2008;10(3):149–159.  
   DOI: 10.1089/dia.2007.0293

3. Battelino T, Danne T, Bergenstal RM, et al.  
   *Clinical Targets for Continuous Glucose Monitoring Data Interpretation: Recommendations From the International Consensus on Time in Range.*  
   **Diabetes Care.** 2019;42(8):1593–1603.  
   DOI: 10.2337/dci19-0028


### Time in Range (TIR, TBR, TAR)

4. Battelino T, Danne T, Bergenstal RM, et al.  
   *Clinical Targets for Continuous Glucose Monitoring Data Interpretation: Recommendations From the International Consensus on Time in Range.*  
   **Diabetes Care.** 2019;42(8):1593–1603.  
   DOI: 10.2337/dci19-0028


### Coefficient of Variation (CV)

5. Monnier L, Colette C, Wojtusciszyn A, et al.  
   *Glycemic variability: should we and can we prevent it?*  
   **Diabetes Care.** 2008;31(Suppl 2):S150–S154.  
   DOI: 10.2337/dc08-s241


### Glucose Management Indicator (GMI)

6. Bergenstal RM, Beck RW, Close KL, et al.  
   *Glucose Management Indicator (GMI): A New Term for Estimating A1C From Continuous Glucose Monitoring.*  
   **Diabetes Care.** 2018;41(11):2275–2280.  
   DOI: 10.2337/dc18-0734


### J-Index

7. Wojcicki JM.  
   *J-Index: a new proposition of the assessment of current glucose control in diabetic patients.*  
   **Hormone and Metabolic Research.** 1995;27(1):41–42.  
   DOI: 10.1055/s-2007-979927


### MAGE (Mean Amplitude of Glycemic Excursions)

8. Service FJ, Molnar GD, Rosevear JW, Ackerman E, Gatewood LC, Taylor WF.  
   *Mean amplitude of glycemic excursions, a measure of diabetic instability.*  
   **Diabetes.** 1970;19(9):644–655.  
   DOI: 10.2337/diab.19.9.644


### MODD (Mean of Daily Differences)

9. Molnar GD, Taylor WF, Ho MM.  
   *Day-to-day variation of continuously monitored glycaemia: a further measure of diabetic instability.*  
   **Diabetologia.** 1972;8(5):342–348.  
   DOI: 10.1007/BF01218495


### CONGA (Continuous Overall Net Glycemic Action)

10. McDonnell CM, Donath SM, Vidmar SI, Werther GA, Cameron FJ.  
    *A novel approach to continuous glucose analysis utilizing glycemic variation.*  
    **Diabetes Technology & Therapeutics.** 2005;7(2):253–263.  
    DOI: 10.1089/dia.2005.7.253


### LBGI & HBGI (Low/High Blood Glucose Index)

11. Kovatchev BP, Cox DJ, Gonder-Frederick LA, Clarke WL.  
    *Assessment of risk for severe hypoglycemia among patients with type 1 and type 2 diabetes using self-monitoring blood glucose data.*  
    **Diabetes Care.** 2001;24(11):1870–1875.  
    DOI: 10.2337/diacare.24.11.1870


### ADRR (Average Daily Risk Range)

12. Kovatchev BP, Otto E, Cox D, et al.  
    *The average daily risk range: a new measure of glycemic variability.*  
    **Diabetes Care.** 2006;29(11):2272–2277.  
    DOI: 10.2337/dc06-1085


### GRADE (Glycaemic Risk Assessment in Diabetes Equation)

13. Hill NR, Hindmarsh PC, Stevens RJ, Stratton IM, Levy JC, Matthews DR.  
    *A method for assessing quality of control from glucose profiles.*  
    **Diabetic Medicine.** 2007;24(7):753–758.  
    DOI: 10.1111/j.1464-5491.2007.02119.x


### MAG (Mean Absolute Glucose)

14. Hermanides J, Vriesendorp TM, Bosman RJ, Zandstra DF, Hoekstra JB, Devries JH.  
    *Glucose variability is associated with intensive care unit mortality.*  
    **Critical Care Medicine.** 2010;38(3):838–842.  
    DOI: 10.1097/CCM.0b013e3181cc4be9


### GVP (Glucose Variability Percentage)

15. Peyser TA, Balo AK, Buckingham BA, Hirsch IB, Garcia A.  
    *Glycemic variability percentage: a novel method for assessing glycemic variability from continuous glucose monitor data.*  
    **Diabetes Technology & Therapeutics.** 2018;20(1):6–16.  
    DOI: 10.1089/dia.2017.0187


### M-Value

16. Schlichtkrull J, Munck O, Jersild M.  
    *The M-value, an index of blood-sugar control in diabetics.*  
    **Acta Medica Scandinavica.** 1965;177(1):95–102.  
    DOI: 10.1111/j.0954-6820.1965.tb01810.x


### Lability Index

17. Ryan EA, Shandro T, Green K, et al.  
    *Assessment of the severity of hypoglycemia and glycemic lability in type 1 diabetic subjects undergoing islet transplantation.*  
    **Diabetes.** 2004;53(4):955–962.  
    DOI: 10.2337/diabetes.53.4.955


### Hypoglycaemic / Hyperglycaemic Index (Rodbard)

18. Rodbard D.  
    *Characterizing accuracy of a continuous glucose monitoring system during hypoglycemia.*  
    **Diabetes Technology & Therapeutics.** 2014;16(10):652–657.  
    DOI: 10.1089/dia.2014.0030


### eA1c (Nathan formula)

19. Nathan DM, Kuenen J, Borg R, Zheng H, Schoenfeld D, Heine RJ; A1c-Derived Average Glucose (ADAG) Study Group.  
    *Translating the A1C assay into estimated average glucose values.*  
    **Diabetes Care.** 2008;31(8):1473–1478.  
    DOI: 10.2337/dc08-0545


## Contributing

### Code style

This project uses [Black](https://black.readthedocs.io/) for formatting and [Ruff](https://docs.astral.sh/ruff/) for linting.

Install the development tools:

```
pip install -e ".[dev]"
```

Check and apply formatting:

```
black .
```

Run the linter (and auto-fix safe issues):

```
ruff check .
ruff check --fix .
```

Both tools are enforced in CI – PRs must pass `black --check .` and `ruff check .` before merging.

## License

This project is licensed under the MIT License – see the [LICENSE](LICENSE) file for details.
