pylantir 0.2.2__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pylantir-0.2.2 → pylantir-0.3.0}/PKG-INFO +305 -23
- {pylantir-0.2.2 → pylantir-0.3.0}/README.md +301 -22
- {pylantir-0.2.2 → pylantir-0.3.0}/pyproject.toml +5 -2
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/api_server.py +13 -9
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/cli/run.py +307 -41
- pylantir-0.3.0/src/pylantir/config/calpendo_config_example.json +65 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/config/mwl_config.json +3 -1
- pylantir-0.3.0/src/pylantir/data_sources/__init__.py +84 -0
- pylantir-0.3.0/src/pylantir/data_sources/base.py +117 -0
- pylantir-0.3.0/src/pylantir/data_sources/calpendo_plugin.py +702 -0
- pylantir-0.3.0/src/pylantir/data_sources/redcap_plugin.py +367 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/db_setup.py +3 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/models.py +3 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/populate_db.py +6 -3
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/redcap_to_db.py +128 -78
- {pylantir-0.2.2 → pylantir-0.3.0}/LICENSE +0 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/__init__.py +0 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/auth_db_setup.py +0 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/auth_models.py +0 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/auth_utils.py +0 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/cli/__init__.py +0 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/config/config_example_with_cors.json +0 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/db_concurrency.py +0 -0
- {pylantir-0.2.2 → pylantir-0.3.0}/src/pylantir/mwl_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pylantir
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Python - DICOM Modality WorkList with Optional API
|
|
5
5
|
Author-email: Milton Camacho <miltoncamachoicc@gmail.com>
|
|
6
6
|
Requires-Python: >=3.11.1
|
|
@@ -21,6 +21,8 @@ Requires-Dist: uuid
|
|
|
21
21
|
Requires-Dist: coloredlogs
|
|
22
22
|
Requires-Dist: python-dotenv
|
|
23
23
|
Requires-Dist: pandas
|
|
24
|
+
Requires-Dist: requests
|
|
25
|
+
Requires-Dist: pytz
|
|
24
26
|
Requires-Dist: fastapi>=0.104.1 ; extra == "api"
|
|
25
27
|
Requires-Dist: uvicorn[standard]>=0.24.0 ; extra == "api"
|
|
26
28
|
Requires-Dist: passlib[bcrypt]==1.7.4 ; extra == "api"
|
|
@@ -46,6 +48,7 @@ Requires-Dist: pytest-runner ; extra == "test"
|
|
|
46
48
|
Requires-Dist: pytest==7.3.1 ; extra == "test"
|
|
47
49
|
Requires-Dist: pytest-github-actions-annotate-failures ; extra == "test"
|
|
48
50
|
Requires-Dist: shellcheck-py==0.9.0.2 ; extra == "test"
|
|
51
|
+
Requires-Dist: responses>=0.23.0 ; extra == "test"
|
|
49
52
|
Project-URL: Documentation, https://github.com/miltoncamacho/pylantir/tree/main#readme
|
|
50
53
|
Project-URL: Source, https://github.com/miltoncamacho/pylantir
|
|
51
54
|
Project-URL: Tracker, https://github.com/miltoncamacho/pylantir/issues
|
|
@@ -197,14 +200,16 @@ usage: pylantir [-h] [--AEtitle AETITLE] [--ip IP] [--port PORT] [--pylantir_con
|
|
|
197
200
|
- **--ip IP**: IP/host address for the server
|
|
198
201
|
- **--port PORT**: Port for the server
|
|
199
202
|
- **--pylantir_config PYLANTIR_CONFIG**: Path to the configuration JSON file containing pylantir configs:
|
|
203
|
+
- **data_sources**: Array of data source configurations (recommended new format)
|
|
204
|
+
- Each source has: `name`, `type`, `enabled`, `sync_interval`, `operation_interval`, `config`, `field_mapping`
|
|
205
|
+
- **redcap2wl**: Legacy field mapping (deprecated, auto-converts to data_sources)
|
|
200
206
|
- **allowed_aet**: List of allowed AE titles e.g. `["MRI_SCANNER", "MRI_SCANNER_2"]`
|
|
201
|
-
- **site**: Site ID
|
|
207
|
+
- **site**: Site ID (legacy format, deprecated)
|
|
202
208
|
- **protocol**: `{"site": "protocol_name", "mapping": "HIS/RIS mapping"}`
|
|
203
|
-
- **redcap2wl**: Dictionary of REDCap fields to worklist fields mapping e.g., `{"redcap_field": "worklist_field"}`
|
|
204
209
|
- **db_path**: Path to main worklist database e.g., `"/path/to/worklist.db"`
|
|
205
210
|
- **users_db_path**: Optional path to users authentication database e.g., `"/path/to/users.db"`
|
|
206
|
-
- **db_update_interval**:
|
|
207
|
-
- **operation_interval**:
|
|
211
|
+
- **db_update_interval**: Legacy sync interval (deprecated, use sync_interval in data_sources)
|
|
212
|
+
- **operation_interval**: Legacy operation window (deprecated, use operation_interval in data_sources)
|
|
208
213
|
- **--mpps_action {create,set}**: Action to perform for MPPS either create or set
|
|
209
214
|
- **--mpps_status {COMPLETED,DISCONTINUED}**: Status to set for MPPS either COMPLETED or DISCONTINUED
|
|
210
215
|
- **--callingAEtitle CALLINGAETITLE**: Calling AE Title for MPPS, it helps when the MWL is limited to only accept certain AE titles
|
|
@@ -213,40 +218,317 @@ usage: pylantir [-h] [--AEtitle AETITLE] [--ip IP] [--port PORT] [--pylantir_con
|
|
|
213
218
|
|
|
214
219
|
## Configuration JSON file
|
|
215
220
|
|
|
216
|
-
|
|
221
|
+
Pylantir supports a modular data sources configuration that allows you to connect to multiple data sources simultaneously.
|
|
222
|
+
|
|
223
|
+
### New Data Sources Format (Recommended)
|
|
224
|
+
|
|
225
|
+
The new configuration format uses a `data_sources` array to define one or more data sources:
|
|
217
226
|
|
|
218
227
|
```json
|
|
219
228
|
{
|
|
220
229
|
"db_path": "/path/to/worklist.db",
|
|
221
230
|
"users_db_path": "/path/to/users.db",
|
|
222
231
|
"db_echo": "False",
|
|
223
|
-
"db_update_interval": 60,
|
|
224
|
-
"operation_interval": {"start_time": [0,0],"end_time": [23,59]},
|
|
225
232
|
"allowed_aet": [],
|
|
233
|
+
"data_sources": [
|
|
234
|
+
{
|
|
235
|
+
"name": "main_redcap",
|
|
236
|
+
"type": "redcap",
|
|
237
|
+
"enabled": true,
|
|
238
|
+
"sync_interval": 60,
|
|
239
|
+
"operation_interval": {
|
|
240
|
+
"start_time": [0, 0],
|
|
241
|
+
"end_time": [23, 59]
|
|
242
|
+
},
|
|
243
|
+
"config": {
|
|
244
|
+
"site_id": "792",
|
|
245
|
+
"protocol": "BRAIN_MRI_3T"
|
|
246
|
+
},
|
|
247
|
+
"field_mapping": {
|
|
248
|
+
"study_id": "study_id",
|
|
249
|
+
"instrument": "redcap_repeat_instrument",
|
|
250
|
+
"session_id": "mri_instance",
|
|
251
|
+
"family_id": "family_id",
|
|
252
|
+
"youth_dob_y": "youth_dob_y",
|
|
253
|
+
"t1_date": "t1_date",
|
|
254
|
+
"demo_sex": "demo_sex",
|
|
255
|
+
"scheduled_date": "mri_date",
|
|
256
|
+
"scheduled_time": "mri_time",
|
|
257
|
+
"mri_wt_lbs": "patient_weight_lb",
|
|
258
|
+
"referring_physician": "referring_physician_name",
|
|
259
|
+
"performing_physician": "performing_physician",
|
|
260
|
+
"station_name": "station_name",
|
|
261
|
+
"status": "performed_procedure_step_status"
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
],
|
|
265
|
+
"protocol": {
|
|
266
|
+
"792": "BRAIN_MRI_3T",
|
|
267
|
+
"mapping": "GEHC"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Data Source Configuration Fields:**
|
|
273
|
+
|
|
274
|
+
- **`name`**: Unique identifier for this data source (used in logs and database tracking)
|
|
275
|
+
- **`type`**: Data source type (currently supports `"redcap"`; extensible for future sources)
|
|
276
|
+
- **`enabled`**: Boolean to enable/disable this source without removing its configuration
|
|
277
|
+
- **`sync_interval`**: How often to sync data (in seconds)
|
|
278
|
+
- **`operation_interval`**: Time window when sync should occur (24-hour format)
|
|
279
|
+
- `start_time`: `[hours, minutes]` - Start of operation window
|
|
280
|
+
- `end_time`: `[hours, minutes]` - End of operation window
|
|
281
|
+
- **`config`**: Source-specific configuration
|
|
282
|
+
- For REDCap: `site_id`, `protocol`, and optional API credentials
|
|
283
|
+
- **`field_mapping`**: Maps source fields to DICOM worklist fields
|
|
284
|
+
- **`window_mode`** (Calpendo optional): `rolling` (default) or `daily`
|
|
285
|
+
- **`daily_window`** (Calpendo optional): `{"start_time": [h, m], "end_time": [h, m]}`
|
|
286
|
+
|
|
287
|
+
### Multiple Data Sources Example
|
|
288
|
+
|
|
289
|
+
You can configure multiple data sources to sync simultaneously:
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
{
|
|
293
|
+
"db_path": "/path/to/worklist.db",
|
|
294
|
+
"data_sources": [
|
|
295
|
+
{
|
|
296
|
+
"name": "site_792_redcap",
|
|
297
|
+
"type": "redcap",
|
|
298
|
+
"enabled": true,
|
|
299
|
+
"sync_interval": 60,
|
|
300
|
+
"config": {
|
|
301
|
+
"site_id": "792",
|
|
302
|
+
"protocol": "BRAIN_MRI_3T"
|
|
303
|
+
},
|
|
304
|
+
"field_mapping": { "study_id": "study_id" }
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"name": "site_793_redcap",
|
|
308
|
+
"type": "redcap",
|
|
309
|
+
"enabled": true,
|
|
310
|
+
"sync_interval": 120,
|
|
311
|
+
"config": {
|
|
312
|
+
"site_id": "793",
|
|
313
|
+
"protocol": "CARDIAC_MRI"
|
|
314
|
+
},
|
|
315
|
+
"field_mapping": { "patient_id": "patient_id" }
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Calpendo Data Source Integration
|
|
322
|
+
|
|
323
|
+
Pylantir supports integration with Calpendo booking systems to automatically sync MRI/EEG scanner bookings into the DICOM worklist. This allows scanner operators to see scheduled bookings directly on their imaging equipment.
|
|
324
|
+
|
|
325
|
+
### Prerequisites
|
|
326
|
+
|
|
327
|
+
Set up environment variables for Calpendo authentication:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
export CALPENDO_USERNAME=<your_calpendo_username>
|
|
331
|
+
export CALPENDO_PASSWORD=<your_calpendo_password>
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Minimal Configuration
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"db_path": "/path/to/worklist.db",
|
|
339
|
+
"data_sources": [
|
|
340
|
+
{
|
|
341
|
+
"name": "calpendo_mri",
|
|
342
|
+
"type": "calpendo",
|
|
343
|
+
"enabled": true,
|
|
344
|
+
"sync_interval": 60,
|
|
345
|
+
"config": {
|
|
346
|
+
"base_url": "https://your-institution.calpendo.com",
|
|
347
|
+
"resources": ["3T Diagnostic", "EEG Lab"]
|
|
348
|
+
},
|
|
349
|
+
"field_mapping": {
|
|
350
|
+
"patient_id": {
|
|
351
|
+
"source_field": "title",
|
|
352
|
+
"_extract": {
|
|
353
|
+
"pattern": "^([A-Z0-9]+)_.*",
|
|
354
|
+
"group": 1
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
"patient_name": {
|
|
358
|
+
"source_field": "title",
|
|
359
|
+
"_extract": {
|
|
360
|
+
"pattern": "^[A-Z0-9]+_(.+)$",
|
|
361
|
+
"group": 1
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
"study_description": {
|
|
365
|
+
"source_field": "properties.project.formattedName",
|
|
366
|
+
"_extract": {
|
|
367
|
+
"pattern": "^([^(]+)",
|
|
368
|
+
"group": 1
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
"accession_number": "id"
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
]
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Full Configuration with All Options
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
{
|
|
382
|
+
"name": "calpendo_scanners",
|
|
383
|
+
"type": "calpendo",
|
|
384
|
+
"enabled": true,
|
|
385
|
+
"sync_interval": 60,
|
|
386
|
+
"config": {
|
|
387
|
+
"base_url": "https://your-institution.calpendo.com",
|
|
388
|
+
"resources": ["3T Diagnostic", "EEG Lab", "Mock Scanner"],
|
|
389
|
+
"status_filter": "Approved",
|
|
390
|
+
"lookback_multiplier": 2,
|
|
391
|
+
"timezone": "America/Edmonton",
|
|
392
|
+
"resource_modality_mapping": {
|
|
393
|
+
"3T": "MR",
|
|
394
|
+
"EEG": "EEG",
|
|
395
|
+
"Mock": "OT"
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
"field_mapping": {
|
|
399
|
+
"patient_id": {
|
|
400
|
+
"source_field": "title",
|
|
401
|
+
"_extract": {
|
|
402
|
+
"pattern": "^([A-Z0-9]+)_.*",
|
|
403
|
+
"group": 1
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
"patient_name": {
|
|
407
|
+
"source_field": "title",
|
|
408
|
+
"_extract": {
|
|
409
|
+
"pattern": "^[A-Z0-9]+_(.+)$",
|
|
410
|
+
"group": 1
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
"study_description": {
|
|
414
|
+
"source_field": "properties.project.formattedName",
|
|
415
|
+
"_extract": {
|
|
416
|
+
"pattern": "^([^(]+)",
|
|
417
|
+
"group": 1
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
"accession_number": "id",
|
|
421
|
+
"study_instance_uid": "id"
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
**Calpendo Configuration Fields:**
|
|
427
|
+
|
|
428
|
+
- **`base_url`**: Calpendo server URL (e.g., `https://your-institution.calpendo.com`)
|
|
429
|
+
- **`resources`**: Array of resource names to sync (e.g., scanner names)
|
|
430
|
+
- **`status_filter`** (optional): Only sync bookings with this status (e.g., `"Approved"`)
|
|
431
|
+
- **`lookback_multiplier`** (optional, default: 2): Rolling window multiplier for incremental sync
|
|
432
|
+
- **`timezone`** (optional, default: `"America/Edmonton"`): Timezone for booking timestamps
|
|
433
|
+
- **`resource_modality_mapping`** (optional): Map resource names to DICOM modality codes
|
|
434
|
+
- **`field_mapping`** (at data source root): Maps Calpendo fields to worklist fields
|
|
435
|
+
- **`window_mode`** (optional): `rolling` (default) or `daily`
|
|
436
|
+
- **`daily_window`** (optional): `{"start_time": [h, m], "end_time": [h, m]}`
|
|
437
|
+
- Use **`_extract`** for regex-based field extraction:
|
|
438
|
+
- **`pattern`**: Regular expression pattern (use `\\` for escaping in JSON)
|
|
439
|
+
- **`group`**: Capture group number (0 = full match, 1+ = capture groups)
|
|
440
|
+
|
|
441
|
+
### Regex Pattern Examples
|
|
442
|
+
|
|
443
|
+
Common patterns for extracting information from Calpendo booking titles:
|
|
444
|
+
|
|
445
|
+
```json
|
|
446
|
+
{
|
|
447
|
+
"patient_id": {
|
|
448
|
+
"source_field": "title",
|
|
449
|
+
"_extract": {
|
|
450
|
+
"pattern": "^([A-Z0-9]+)_.*",
|
|
451
|
+
"group": 1
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
"patient_name": {
|
|
455
|
+
"source_field": "title",
|
|
456
|
+
"_extract": {
|
|
457
|
+
"pattern": "^[A-Z0-9]+_(.+)$",
|
|
458
|
+
"group": 1
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
"study_description": {
|
|
462
|
+
"source_field": "properties.project.formattedName",
|
|
463
|
+
"_extract": {
|
|
464
|
+
"pattern": "^([^(]+)",
|
|
465
|
+
"group": 1
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Troubleshooting
|
|
472
|
+
|
|
473
|
+
**Authentication Errors:**
|
|
474
|
+
- Verify environment variables are set correctly
|
|
475
|
+
- Check Calpendo username/password are valid
|
|
476
|
+
- Ensure API access is enabled for your account
|
|
477
|
+
|
|
478
|
+
**No Bookings Synced:**
|
|
479
|
+
- Check `resources` list matches actual Calpendo resource names
|
|
480
|
+
- Verify `status_filter` isn't too restrictive
|
|
481
|
+
- Check booking dates are within the rolling window (current time ± lookback window)
|
|
482
|
+
|
|
483
|
+
**Regex Extraction Failures:**
|
|
484
|
+
- Test regex patterns with sample data before deployment
|
|
485
|
+
- Check JSON escaping (use `\\` for backslashes)
|
|
486
|
+
- Set logging to DEBUG to see extraction warnings
|
|
487
|
+
|
|
488
|
+
### Workflow Notes
|
|
489
|
+
|
|
490
|
+
- MPPS status updates are owned by N-CREATE/N-SET; sync operations do not overwrite existing `performed_procedure_step_status` values.
|
|
491
|
+
|
|
492
|
+
For more details, see the [Calpendo plugin quickstart](specs/002-calpendo-plugin/quickstart.md).
|
|
493
|
+
|
|
494
|
+
### Legacy Configuration Format (Deprecated)
|
|
495
|
+
|
|
496
|
+
⚠️ **The legacy configuration format is deprecated but still supported for backward compatibility.**
|
|
497
|
+
|
|
498
|
+
If you're using the old format, Pylantir will automatically convert it to the new format at runtime:
|
|
499
|
+
|
|
500
|
+
```json
|
|
501
|
+
{
|
|
502
|
+
"db_path": "/path/to/worklist.db",
|
|
503
|
+
"db_echo": "False",
|
|
504
|
+
"db_update_interval": 60,
|
|
505
|
+
"operation_interval": {"start_time": [0,0], "end_time": [23,59]},
|
|
226
506
|
"site": "792",
|
|
227
507
|
"redcap2wl": {
|
|
228
508
|
"study_id": "study_id",
|
|
229
|
-
"
|
|
230
|
-
"session_id" : "mri_instance",
|
|
231
|
-
"family_id": "family_id",
|
|
232
|
-
"youth_dob_y": "youth_dob_y",
|
|
233
|
-
"t1_date": "t1_date",
|
|
234
|
-
"demo_sex": "demo_sex",
|
|
235
|
-
"scheduled_date": "mri_date",
|
|
236
|
-
"scheduled_time": "mri_time",
|
|
237
|
-
"mri_wt_lbs": "patient_weight_lb",
|
|
238
|
-
"referring_physician": "referring_physician_name",
|
|
239
|
-
"performing_physician": "performing_physician",
|
|
240
|
-
"station_name": "station_name",
|
|
241
|
-
"status": "performed_procedure_step_status"
|
|
509
|
+
"demo_sex": "demo_sex"
|
|
242
510
|
},
|
|
243
511
|
"protocol": {
|
|
244
|
-
"792": "BRAIN_MRI_3T"
|
|
245
|
-
"mapping": "GEHC"
|
|
512
|
+
"792": "BRAIN_MRI_3T"
|
|
246
513
|
}
|
|
247
514
|
}
|
|
248
515
|
```
|
|
249
516
|
|
|
517
|
+
When using the legacy format, you'll see a deprecation warning:
|
|
518
|
+
```
|
|
519
|
+
WARNING: Legacy configuration format detected.
|
|
520
|
+
Consider migrating to 'data_sources' format for better flexibility.
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Migration Note**: To migrate from legacy to new format:
|
|
524
|
+
1. Rename `redcap2wl` → `field_mapping`
|
|
525
|
+
2. Move `site` → `config.site_id`
|
|
526
|
+
3. Move `protocol[site]` → `config.protocol`
|
|
527
|
+
4. Move `db_update_interval` → `sync_interval`
|
|
528
|
+
5. Wrap everything in a `data_sources` array with `name`, `type`, and `enabled` fields
|
|
529
|
+
|
|
530
|
+
See `config/mwl_config_multi_source_example.json` for a complete example.
|
|
531
|
+
|
|
250
532
|
### Memory Management (Optional)
|
|
251
533
|
|
|
252
534
|
When you install the `monitoring` optional dependency (`pip install pylantir[monitoring]`), Pylantir gains enhanced memory monitoring capabilities during REDCap synchronization:
|