Metadata-Version: 2.4
Name: ehvd
Version: 0.1.0
Summary: Python client for the Austrian eHealth Verzeichnisdienst (eHVD) SOAP webservice
Project-URL: Homepage, https://github.com/nerdocs/ehvd
Project-URL: Documentation, https://github.com/nerdocs/ehvd#readme
Author-email: Christian Gonzalez <office@nerdocs.at>
License: MIT
License-File: LICENSE
Keywords: austria,ehealth,ehvd,gda,healthcare,soap
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Healthcare Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: requests>=2.28
Requires-Dist: zeep>=4.2
Provides-Extra: dev
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest-mock; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: responses; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Provides-Extra: django
Requires-Dist: django>=4.2; extra == 'django'
Description-Content-Type: text/markdown

# ehvd

Python client for the **eHealth Verzeichnisdienst (eHVD)** — the public SOAP
webservice of Austria's BRZ that lists healthcare providers ("GDA") registered
under § 9/10 GTelG 2012.

The client wraps the official SOAP interface (`GetGdaDescriptors`,
`GetGdaSearch`) with a pythonic API, returns typed dataclasses, maps the
documented fault codes 6001–6006 to exceptions, and ships an optional Django
integration.

## Status

Alpha. The public endpoint requires registration at BMSGPK *and* per-IP
firewall whitelisting at BRZ — without both, every request is blocked at
the WAF. See [`docs/access.md`](docs/access.md) for the full process
(ID Austria, application contents, source IP, support contacts). During
development most users work against a locally cached WSDL or a mocked
zeep client.

## Install

```bash
pip install ehvd               # core
pip install "ehvd[django]"     # with Django factory
```

Requirements: Python ≥ 3.10, `zeep`, `requests`.

## Quick start

```python
from ehvd import EhvdClient

client = EhvdClient()  # uses the public production WSDL

# 1) Lookup by OID
gda = client.get_by_id("1.2.40.0.34.99.10.1.1.2.27095")

# 2) Lookup by GDA-specific identifier
gda = client.get_by_arzt_nr("12345")              # ÖÄK-Arztnummer
gda = client.get_by_hospital_nr("987")            # KA-Nr
gda = client.get_by_ambulatory_nr("321")          # Ambu-Nr
gda = client.get_by_vpnr("555")                   # VPNR
gda = client.get_by_gbr_id("ABC1")                # GBR-ID

if gda:
    print(gda.display_name)
    for addr in gda.addresses:
        print(addr.zip, addr.city, addr.street_name, addr.street_number)

# 3) Search
hits = client.search(
    surname="Mueller*",
    city="Wien",
    max_results=50,
)
for hit in hits:
    print(hit.id.id, hit.display_name, hit.status.ehvd_status)
```

`get_by_id` returns `None` when the service reports 6002 (no compliance found).
`search` raises [`EhvdTooManyResultsError`](src/ehvd/exceptions.py) when the
result set would exceed `max_results`.

## Errors

The service returns SOAP 1.2 faults with the numeric codes from Servicehandbuch
4.1; the client maps them onto typed exceptions:

| Code | Exception |
|------|-----------|
| 6001 | `EhvdInvalidContentError` |
| 6002 | `EhvdNoComplianceError` (converted to `None` for single-record lookups) |
| 6003 | `EhvdGeneralError` |
| 6004 | `EhvdAttributeNotFoundError` |
| 6005 | `EhvdTooManyResultsError` |
| 6006 | `EhvdNotAllowedError` |

Local input validation (length limits, illegal wildcard, missing surname,
`max_results` range) raises `EhvdValidationError` *before* hitting the wire.

## Django integration

```python
# settings.py
INSTALLED_APPS = [..., "ehvd.django"]

EHVD = {
    "WSDL_URL": "https://ehvdws.gesundheit.gv.at/ehvd.wsdl",
    "TIMEOUT": 10,
    # optional mutual TLS:
    "CLIENT_CERT": ("/path/cert.pem", "/path/key.pem"),
    "CA_BUNDLE": "/path/ca.pem",
}
```

```python
from ehvd.django import get_client

def my_view(request):
    gda = get_client().get_by_arzt_nr(request.GET["arzt_nr"])
    ...
```

`get_client()` caches a single client per process. Call
`ehvd.django.reset_client()` after changing settings (e.g. in tests).

## Transport configuration

For mutual TLS, custom proxies, timeouts or session re-use, instantiate
`zeep.Client` yourself and pass it in:

```python
from requests import Session
from zeep import Client as ZeepClient
from zeep.transports import Transport

session = Session()
session.cert = ("/path/cert.pem", "/path/key.pem")
zeep_client = ZeepClient(
    "https://ehvdws.gesundheit.gv.at/ehvd.wsdl",
    transport=Transport(session=session, timeout=10),
)

client = EhvdClient(zeep_client=zeep_client)
```

## Development

```bash
git clone git@gitlab.com:nerdocs/ehvd.git
cd ehvd
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,django]"
pytest
ruff check .
```

The package uses a **src-layout** (`src/ehvd/`) so that tests always exercise
the installed package rather than the working copy.

## Layout

See [`docs/architecture.md`](docs/architecture.md) for the structural
overview. Reference material is in [`docs/`](docs/):

- [`docs/architecture.md`](docs/architecture.md) — module map, src-layout rationale
- [`docs/usage.md`](docs/usage.md) — end-to-end usage walkthrough
- [`docs/protocol.md`](docs/protocol.md) — SOAP fields & error codes (from Servicehandbuch)
- [`docs/access.md`](docs/access.md) — Antrag, ID Austria & IP-Freischaltung beim BRZ
- [`docs/django.md`](docs/django.md) — Django integration details

For AI agents working in this repository: read [`CLAUDE.md`](CLAUDE.md) first.

## License

MIT — see [LICENSE](LICENSE).

## Sources

- *eHVD Webservice Handbuch* (Servicehandbuch), BRZ, v2.2, 2023-11-14
- [§§ 9, 10 GTelG 2012](https://www.ris.bka.gv.at)
- [IHE HPD profile](https://www.ihe.net/)
