Migration Guide¶
Migrating from the legacy python-qgenda library (by JP Jorissen) to qgendapy.
Option 1: Drop-in Replacement (Fastest)¶
Change one import line and everything else works:
# Before
from qgenda.api.client import QGendaClient
# After
from qgendapy.compat import QGendaClient
The compat client accepts the same constructor signature. With QGENDA_CONF_FILE set, no arguments are needed:
# Credentials loaded from INI file via QGENDA_CONF_FILE
client = QGendaClient()
client.authenticate() # still works (no-op -- auth is now automatic)
resp = client.get_schedule(start_date="2024-01-15")
data = resp.json() # returns list[dict], same as before
The response objects mimic requests.Response with .status_code, .json(), .text, and .headers.
What's different in compat mode¶
authenticate()is a no-op -- the modern client auto-authenticatesheadersparameter on methods is ignored (httpx manages headers internally)use_cachingandleaderconstructor params are accepted but ignored- Error handling uses
qgendapy.exceptions.APIErrorinstead ofqgenda.api.exceptions.HTTPError
Your INI config still works¶
If you use QGENDA_CONF_FILE and QGENDA_CONF_REGION, those are supported natively. No config changes needed.
Option 2: Modern Client (Recommended)¶
The modern client gives you typed models, OData builder, async support, and full API coverage.
Constructor Changes¶
# Before
from qgenda.api.client import QGendaClient
client = QGendaClient(
username="you@hospital.org",
password="secret",
company_key="abc",
api_url="https://api.qgenda.com/",
api_version="v2",
)
client.authenticate()
# After -- credentials from QGENDA_CONF_FILE
from qgendapy import QGendaClient
client = QGendaClient()
# No authenticate() needed
Or with environment variables:
export QGENDA_EMAIL="you@hospital.org"
export QGENDA_PASSWORD="secret"
export QGENDA_COMPANY_KEY="abc"
Or with explicit arguments:
from qgendapy import QGendaClient
client = QGendaClient(
email="you@hospital.org", # "username" -> "email"
password="secret",
company_key="abc",
base_url="https://api.qgenda.com/v2", # combined URL
)
Method Changes¶
| Legacy | Modern | Notes |
|---|---|---|
client.get_schedule(start_date=..., end_date=..., odata_kwargs=...) |
client.schedule.list(start_date=..., end_date=..., odata=...) |
OData uses builder |
client.get_staff(odata_kwargs=...) |
client.staff.list(odata=...) |
|
client.get_facility(odata_kwargs=...) |
client.facility.list(odata=...) |
|
client.get_organization(organization_key=...) |
client.organization.list(odata=...) |
|
client.get_task(odata_kwargs=...) |
client.task.list(odata=...) |
|
client.get_timeevent(start_date=..., odata_kwargs=...) |
client.time_event.list(start_date=..., odata=...) |
|
client.get_dailycase(start_date=..., odata_kwargs=...) |
client.daily_case.list(start_date=..., odata=...) |
OData Changes¶
# Before -- raw dict
resp = client.get_staff(odata_kwargs={
"$select": "FirstName,LastName",
"$filter": "IsActive eq true",
})
# After -- OData builder (or pass dict to OData.from_kwargs)
from qgendapy import OData
# Option A: Builder
odata = OData().select("FirstName", "LastName").filter("IsActive eq true")
resp = client.staff.list(odata=odata)
# Option B: From legacy dict (minimal changes)
odata = OData.from_kwargs({"$select": "FirstName,LastName", "$filter": "IsActive eq true"})
resp = client.staff.list(odata=odata)
Response Changes¶
# Before -- requests.Response
resp = client.get_schedule(start_date="2024-01-15")
data = resp.json() # list[dict]
status = resp.status_code
# After -- QGendaResponse[ScheduleEntry]
resp = client.schedule.list(start_date="2024-01-15")
data = resp.data # list[dict] -- raw JSON, same as before
status = resp.status_code # same
# NEW: typed model access
for entry in resp:
print(entry.task_name) # attribute access instead of dict["TaskName"]
print(entry.staff_f_name) # PascalCase auto-mapped to snake_case
print(entry._extra) # any fields not in the model
Error Handling Changes¶
# Before
from qgenda.api.exceptions import HTTPError, APICallError
try:
resp = client.get_schedule(start_date="2024-01-15")
except HTTPError:
...
# After
from qgendapy.exceptions import APIError, AuthenticationError
try:
resp = client.schedule.list(start_date="2024-01-15")
except APIError as e:
print(e.status_code, e.message, e.response_body)
What You Gain¶
The modern client covers the entire QGenda API -- not just the 7 GET endpoints in the legacy library:
- 23 resource groups with 159 methods (sync) + 159 methods (async)
- Full CRUD: create, read, update, delete staff, tasks, facilities, time events, etc.
- Sub-resources: staff tags, skillsets, profiles, pay modifiers, badge IDs, etc.
- Credentialing: 34 methods for contacts, locations, providers, privileges, records, workflows
- Daily operations: configurations, rooms, patient encounters, capacity
- Notification lists, pay codes/rates/pools, staff targets, credit allocations, and more
- Auto-chunking: Schedule requests over 100 days are automatically split into batches (the old library silently truncated results)