Skip to content

marcodebe/ecg-web

Repository files navigation

ecg-web

Versione italiana

Web service for converting ECG files in DICOM format to graphic images. Exposes a REST API built with FastAPI and delegates rendering to the dicom-ecg-plot module, included as a git submodule.

Requirements

  • Python 3.9+
  • Git (with submodule support)

Installation

git clone --recurse-submodules <repo-url>
cd ecg-web

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

If the repository was already cloned without --recurse-submodules:

git submodule update --init

Configuration

Copy the example file and edit the values:

cp .env.example .env
Variable Default Description
AUTH_ENABLED true Enable/disable API key authentication
API_KEYS (empty) Comma-separated list of valid API keys
RATE_LIMIT 60/minute Per-IP request limit (N/second, N/minute, N/hour)

To disable authentication (e.g. for local development):

AUTH_ENABLED=false

Running

source .venv/bin/activate
uvicorn app.main:app --port 8001 --reload

Interactive API documentation is available at http://localhost:8001/docs.

A browser-based demo client is served at http://localhost:8001/.

Authentication

When AUTH_ENABLED=true, every request to /api/ecg/* endpoints must include the header:

X-API-Key: <key>

Requests without a key or with an invalid key receive 401 Unauthorized.

Web client

A minimal single-page client is served at GET /. It provides a form to upload a DICOM file, configure all conversion options, and view the result directly in the browser. For image formats (PNG, JPG, SVG) the output is displayed inline; for all formats a download link is provided.

No installation or separate server is needed — it is bundled with the API.

Endpoints

GET /api/ecg/formats

Returns all accepted values for conversion parameters.

curl -H "X-API-Key: mykey" http://localhost:8001/api/ecg/formats
{
  "layouts": ["3x4_1", "3x4", "6x2", "12x1"],
  "papers": ["a4", "letter"],
  "formats": ["png", "pdf", "svg", "svgz", "tiff", "jpg", "jpeg", "eps", "ps"],
  "defaults": {
    "layout": "3x4_1",
    "paper": "a4",
    "format": "png",
    "minor_grid": false,
    "interpretation": false,
    "mm_mv": 10.0
  }
}

POST /api/ecg/convert

Converts an uploaded DICOM ECG file via multipart/form-data.

Parameters

Field Type Default Description
file file DICOM ECG file (required)
layout string 3x4_1 Layout: 3x4_1, 3x4, 6x2, 12x1
paper string a4 Paper format: a4, letter
format string png Output format (see /api/ecg/formats)
minor_grid bool false Draw 1 mm minor grid lines
interpretation bool false Show automated ECG interpretation text
mm_mv float 10.0 Amplitude scale in mm/mV

Example

curl -X POST http://localhost:8001/api/ecg/convert \
  -H "X-API-Key: mykey" \
  -F "file=@ecg.dcm" \
  -F "layout=3x4_1" \
  -F "format=pdf" \
  -F "minor_grid=true" \
  -F "interpretation=true" \
  -o ecg.pdf

POST /api/ecg/convert-wado

Fetches an ECG from a PACS server via WADO and converts it. The WADO server must be configured in dicom-ecg-plot/ecgconfig.py.

Parameters

Same as /convert, but instead of a file:

Field Type Description
study_uid string Study Instance UID
series_uid string Series Instance UID
object_uid string SOP Instance UID

Example

curl -X POST http://localhost:8001/api/ecg/convert-wado \
  -H "X-API-Key: mykey" \
  -F "study_uid=1.2.3.4" \
  -F "series_uid=1.2.3.4.5" \
  -F "object_uid=1.2.3.4.5.6" \
  -F "format=png" \
  -o ecg.png

GET /health

Checks that the service is running. Does not require authentication.

curl http://localhost:8001/health
# {"status": "ok"}

Internationalisation (i18n)

API error messages are returned in the language requested by the client via the standard Accept-Language HTTP header.

Supported languages: it (Italian), en (English, default).

# Response in Italian
curl -H "Accept-Language: it" ...
# → {"detail": "Layout non valido. Valori accettati: ..."}

# Response in English
curl -H "Accept-Language: en-US,en;q=0.9" ...
# → {"detail": "Invalid layout. Accepted values: ..."}

If the header is absent or the requested language is not available, English is used.

Adding a new language: open app/i18n.py and add an entry to the MESSAGES dictionary with the same keys as en.

Rate limiting

The maximum number of requests per IP is configurable via RATE_LIMIT in .env. When the limit is exceeded the service responds with 429 Too Many Requests.

Format: N/second, N/minute, N/hour — e.g. 30/minute, 5/second, 500/hour.

Project structure

ecg-web/
├── app/
│   ├── auth.py          # FastAPI dependency for authentication
│   ├── config.py        # Configuration via pydantic-settings
│   ├── i18n.py          # Translations and Accept-Language detection
│   ├── limiter.py       # Global slowapi instance
│   ├── main.py          # FastAPI application
│   ├── routes/
│   │   ├── ecg.py       # /convert and /convert-wado endpoints
│   │   └── formats.py   # /formats endpoint
│   └── static/
│       └── index.html   # Browser demo client (served at /)
├── dicom-ecg-plot/      # Git submodule
├── .env                 # Local configuration (not versioned)
├── .env.example         # Configuration template
└── requirements.txt

Running as a systemd service

A ready-to-use unit file is provided at ecg-web.service.

1. Deploy the project (adjust the path if needed):

sudo cp -r . /opt/ecg-web
sudo chown -R debe:debe /opt/ecg-web

2. Install and enable the unit:

sudo cp /opt/ecg-web/ecg-web.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now ecg-web

3. Check status:

systemctl status ecg-web
journalctl -u ecg-web -f

The unit binds to 127.0.0.1:8001. To expose it externally, put a reverse proxy (nginx, Caddy, …) in front of it.

The unit runs as user ecg-web. Create it before starting the service:

sudo useradd --system --no-create-home ecg-web

If the deployment path differs from /opt/ecg-web, edit ecg-web.service before copying it to /etc/systemd/system/.

nginx configuration

A ready-to-use nginx server block is provided at ecg-web.nginx.conf. It handles HTTP → HTTPS redirect, SSL via Let's Encrypt, and proxies all traffic to uvicorn on 127.0.0.1:8001.

Replace ecg.example.com with your actual domain before installing.

1. Obtain an SSL certificate (if not already done):

certbot --nginx -d your.domain.com

2. Install the configuration:

sudo cp ecg-web.nginx.conf /etc/nginx/sites-available/ecg-web
sudo ln -s /etc/nginx/sites-available/ecg-web /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Updating the submodule

git submodule update --remote dicom-ecg-plot

License

MIT

About

Web service for converting ECG DICOM files to graphic images, built with FastAPI and dicom-ecg-plot

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors