HH/docs/JOURNEY_ENGINE.md
2025-12-24 12:42:31 +03:00

545 lines
14 KiB
Markdown

# PX360 Journey & Survey Engine
## Overview
The Journey & Survey Engine is the core of PX360's patient experience tracking system. It implements an event-driven architecture where external systems (HIS, Lab, Radiology, Pharmacy) send integration events that trigger journey stage completions, which in turn automatically send stage-specific surveys to patients.
## Architecture
### Components
1. **Journey Templates** - Define the stages for each journey type (EMS/Inpatient/OPD)
2. **Journey Instances** - Track actual patient encounters
3. **Stage Templates** - Define individual stages with trigger events
4. **Stage Instances** - Track completion of each stage
5. **Inbound Events** - Integration events from external systems
6. **Survey Templates** - Define surveys for each stage
7. **Survey Instances** - Actual surveys sent to patients
### Flow Diagram
```
External System (HIS/Lab/etc.)
POST /api/integrations/events/
InboundEvent (status: PENDING)
Celery Task: process_inbound_event
Find Journey Instance (by encounter_id)
Find Matching Stage (by trigger_event_code)
Complete Stage → Update timestamps
If auto_send_survey=True
Create SurveyInstance
Send Survey (SMS/WhatsApp/Email)
Patient Completes Survey
If score < threshold → Create PXAction
```
## Journey Types
### 1. EMS (Emergency Medical Services)
Tracks emergency patient pathway from arrival to discharge.
**Example Stages:**
- Triage
- Emergency Physician Consultation
- Diagnostic Tests
- Treatment
- Discharge
### 2. Inpatient
Tracks admitted patient journey from admission to discharge.
**Example Stages:**
- Admission
- Ward Assignment
- Physician Rounds
- Nursing Care
- Diagnostic Services
- Treatment
- Discharge
### 3. OPD (Outpatient Department)
Tracks outpatient visit from registration to checkout.
**Example Stages:**
- Registration
- MD Consultation
- Lab Tests
- Radiology
- Pharmacy
- Checkout
## Integration Event Contracts
### Event Structure
All integration events must follow this structure:
```json
{
"source_system": "his|lab|radiology|pharmacy|moh|chi|other",
"event_code": "EVENT_CODE",
"encounter_id": "UNIQUE_ENCOUNTER_ID",
"patient_identifier": "MRN_OR_PATIENT_ID",
"payload_json": {
"physician_license": "PHY001",
"department_code": "DEPT_OPD",
"timestamp": "2025-12-14T10:30:00Z",
"additional_data": {}
}
}
```
### Standard Event Codes
#### OPD Events
- `OPD_REGISTRATION_COMPLETED` - Patient registered
- `OPD_VISIT_COMPLETED` - MD consultation completed
- `OPD_CHECKOUT_COMPLETED` - Patient checked out
#### Lab Events
- `LAB_ORDER_CREATED` - Lab order placed
- `LAB_SAMPLE_COLLECTED` - Sample collected
- `LAB_ORDER_COMPLETED` - Results ready
#### Radiology Events
- `RADIOLOGY_ORDER_CREATED` - Radiology order placed
- `RADIOLOGY_EXAM_COMPLETED` - Exam performed
- `RADIOLOGY_REPORT_FINALIZED` - Report signed off
#### Pharmacy Events
- `PHARMACY_ORDER_RECEIVED` - Prescription received
- `PHARMACY_DISPENSED` - Medication dispensed
#### Inpatient Events
- `ADMISSION_COMPLETED` - Patient admitted
- `WARD_TRANSFER` - Patient transferred to ward
- `DISCHARGE_COMPLETED` - Patient discharged
#### EMS Events
- `EMS_ARRIVAL` - Patient arrived via EMS
- `TRIAGE_COMPLETED` - Triage assessment done
- `ER_PHYSICIAN_CONSULT` - ER physician saw patient
### Example Event Payloads
#### OPD Visit Completed
```json
{
"source_system": "his",
"event_code": "OPD_VISIT_COMPLETED",
"encounter_id": "ENC20251214001",
"patient_identifier": "MRN123456",
"payload_json": {
"physician_license": "PHY001",
"department_code": "DEPT_OPD",
"visit_date": "2025-12-14",
"visit_time": "10:30:00",
"chief_complaint": "Follow-up",
"diagnosis_codes": ["Z00.00"],
"timestamp": "2025-12-14T10:45:00Z"
}
}
```
#### Lab Order Completed
```json
{
"source_system": "lab",
"event_code": "LAB_ORDER_COMPLETED",
"encounter_id": "ENC20251214001",
"patient_identifier": "MRN123456",
"payload_json": {
"department_code": "DEPT_LAB",
"order_id": "LAB789",
"test_codes": ["CBC", "BMP"],
"collection_time": "2025-12-14T11:00:00Z",
"result_time": "2025-12-14T14:30:00Z",
"timestamp": "2025-12-14T14:30:00Z"
}
}
```
#### Pharmacy Dispensed
```json
{
"source_system": "pharmacy",
"event_code": "PHARMACY_DISPENSED",
"encounter_id": "ENC20251214001",
"patient_identifier": "MRN123456",
"payload_json": {
"department_code": "DEPT_PHARMACY",
"prescription_id": "RX456",
"medications": [
{"name": "Medication A", "quantity": 30},
{"name": "Medication B", "quantity": 60}
],
"dispensed_time": "2025-12-14T15:00:00Z",
"timestamp": "2025-12-14T15:00:00Z"
}
}
```
## Journey Configuration
### Creating a Journey Template
**Step 1: Create Journey Template**
```python
from apps.journeys.models import PatientJourneyTemplate, JourneyType
from apps.organizations.models import Hospital
hospital = Hospital.objects.get(code='HOSP001')
journey_template = PatientJourneyTemplate.objects.create(
name='Standard OPD Journey',
name_ar='رحلة العيادات الخارجية القياسية',
journey_type=JourneyType.OPD,
hospital=hospital,
is_active=True,
is_default=True,
description='Standard outpatient journey with MD, Lab, Radiology, and Pharmacy stages'
)
```
**Step 2: Create Stage Templates**
```python
from apps.journeys.models import PatientJourneyStageTemplate
from apps.surveys.models import SurveyTemplate
# Get survey templates
md_survey = SurveyTemplate.objects.get(name='MD Consultation Survey')
lab_survey = SurveyTemplate.objects.get(name='Lab Experience Survey')
# Stage 1: MD Consultation
PatientJourneyStageTemplate.objects.create(
journey_template=journey_template,
name='MD Consultation',
name_ar='استشارة الطبيب',
code='OPD_MD_CONSULT',
order=1,
trigger_event_code='OPD_VISIT_COMPLETED',
survey_template=md_survey,
auto_send_survey=True,
survey_delay_hours=2, # Send 2 hours after visit
requires_physician=True,
requires_department=True,
is_optional=False,
is_active=True
)
# Stage 2: Lab
PatientJourneyStageTemplate.objects.create(
journey_template=journey_template,
name='Laboratory',
name_ar='المختبر',
code='LAB',
order=2,
trigger_event_code='LAB_ORDER_COMPLETED',
survey_template=lab_survey,
auto_send_survey=True,
survey_delay_hours=1,
requires_physician=False,
requires_department=True,
is_optional=True, # Not all patients need lab
is_active=True
)
# Add more stages as needed...
```
### Creating a Journey Instance
**When a patient encounter starts:**
```python
from apps.journeys.models import PatientJourneyInstance
from apps.organizations.models import Patient
patient = Patient.objects.get(mrn='MRN123456')
journey_instance = PatientJourneyInstance.objects.create(
journey_template=journey_template,
patient=patient,
encounter_id='ENC20251214001',
hospital=hospital,
department=opd_department
)
# Stage instances are automatically created for all active stages
```
## Event Processing
### Processing Logic
When an event is received:
1. **Event Receipt**
- POST to `/api/integrations/events/`
- Event stored with `status=PENDING`
- Audit log created
- Celery task queued
2. **Event Processing** (Celery Task)
- Find journey instance by `encounter_id`
- Find matching stage where:
- `stage_template.trigger_event_code == event.event_code`
- `stage_instance.status IN [PENDING, IN_PROGRESS]`
- Extract physician and department from payload
- Complete the stage:
- Set `status=COMPLETED`
- Set `completed_at=now()`
- Attach `physician`, `department`, `event`
- Store event payload in metadata
3. **Survey Triggering**
- If `stage_template.auto_send_survey=True`
- If `stage_template.survey_template` exists
- Create `SurveyInstance`
- Queue survey send task with delay
4. **Journey Completion**
- Check if all required stages completed
- If yes, mark journey as `COMPLETED`
### Error Handling
**Event Not Found:**
- Status: `IGNORED`
- Reason: "No journey instance found for encounter {encounter_id}"
**Stage Not Found:**
- Status: `IGNORED`
- Reason: "No pending stage found with trigger {event_code}"
**Processing Error:**
- Status: `FAILED`
- Error message stored
- Retry 3 times with exponential backoff
- Can be manually reprocessed by PX Admin
## API Endpoints
### Journey Templates
```
GET /api/journeys/templates/ # List templates
POST /api/journeys/templates/ # Create template
GET /api/journeys/templates/{id}/ # Get template
PUT /api/journeys/templates/{id}/ # Update template
DELETE /api/journeys/templates/{id}/ # Delete template
```
### Journey Instances
```
GET /api/journeys/instances/ # List instances
POST /api/journeys/instances/ # Create instance
GET /api/journeys/instances/{id}/ # Get instance
GET /api/journeys/instances/{id}/progress/ # Get progress summary
```
### Integration Events
```
POST /api/integrations/events/ # Create event (external systems)
POST /api/integrations/events/bulk_create/ # Bulk create events
GET /api/integrations/events/ # List events (PX Admin)
GET /api/integrations/events/{id}/ # Get event details
POST /api/integrations/events/{id}/reprocess/ # Reprocess failed event
```
## Testing
### Test Scenario: OPD Journey
**1. Create Journey Instance**
```bash
curl -X POST http://localhost:8000/api/journeys/instances/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"journey_template": "TEMPLATE_UUID",
"patient": "PATIENT_UUID",
"encounter_id": "ENC20251214001",
"hospital": "HOSPITAL_UUID"
}'
```
**2. Send MD Visit Completed Event**
```bash
curl -X POST http://localhost:8000/api/integrations/events/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_system": "his",
"event_code": "OPD_VISIT_COMPLETED",
"encounter_id": "ENC20251214001",
"patient_identifier": "MRN123456",
"payload_json": {
"physician_license": "PHY001",
"department_code": "DEPT_OPD",
"timestamp": "2025-12-14T10:45:00Z"
}
}'
```
**3. Verify Stage Completion**
```bash
curl http://localhost:8000/api/journeys/instances/JOURNEY_UUID/progress/ \
-H "Authorization: Bearer YOUR_TOKEN"
```
**Expected Response:**
```json
{
"journey_id": "...",
"encounter_id": "ENC20251214001",
"patient": "John Doe",
"journey_type": "opd",
"status": "active",
"completion_percentage": 25,
"is_complete": false,
"stages": [
{
"name": "MD Consultation",
"order": 1,
"status": "completed",
"completed_at": "2025-12-14T10:45:00Z",
"survey_sent": true
},
{
"name": "Laboratory",
"order": 2,
"status": "pending",
"completed_at": null,
"survey_sent": false
}
]
}
```
## Configuration Best Practices
### 1. Survey Delay
- **Immediate (0 hours)**: For time-sensitive feedback (ER, urgent care)
- **2-4 hours**: For outpatient visits (allows patient to leave facility)
- **24 hours**: For inpatient daily surveys
- **48-72 hours**: For discharge surveys
### 2. Optional vs Required Stages
- **Required**: Core pathway stages (MD consultation, admission, discharge)
- **Optional**: Ancillary services (lab, radiology, pharmacy) - not all patients need these
### 3. Trigger Event Codes
- Use consistent naming: `{SERVICE}_{ACTION}_COMPLETED`
- Examples: `OPD_VISIT_COMPLETED`, `LAB_ORDER_COMPLETED`
- Document all codes in integration contracts
### 4. Survey Templates
- Create specific surveys for each stage
- Keep surveys short (5-7 questions max)
- Use NPS or Likert scales
- Include open-ended feedback field
## Monitoring & Troubleshooting
### Check Event Processing Status
```bash
# List pending events
curl http://localhost:8000/api/integrations/events/?status=pending \
-H "Authorization: Bearer YOUR_TOKEN"
# List failed events
curl http://localhost:8000/api/integrations/events/?status=failed \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Reprocess Failed Event
```bash
curl -X POST http://localhost:8000/api/integrations/events/EVENT_UUID/reprocess/ \
-H "Authorization: Bearer YOUR_TOKEN"
```
### View Journey Progress
```bash
curl http://localhost:8000/api/journeys/instances/?encounter_id=ENC123 \
-H "Authorization: Bearer YOUR_TOKEN"
```
## Audit Trail
All events are logged:
- Event receipt → `integration_event`
- Stage completion → `stage_completed`
- Journey completion → `journey_completed`
- Survey sent → `survey_sent`
View audit logs in Django admin or via API.
## Performance Considerations
### Database Indexes
- `encounter_id` - Fast journey lookup
- `trigger_event_code` - Fast stage matching
- `status` + `received_at` - Efficient pending event queries
### Celery Configuration
- Events processed asynchronously
- Retry logic with exponential backoff
- Max 100 events per batch in periodic task
- Separate queue for event processing (optional)
### Optimization Tips
1. Use `select_related` for foreign keys
2. Use `prefetch_related` for reverse relations
3. Batch process events in periodic task
4. Monitor Celery queue length
5. Scale Celery workers as needed
## Security
### API Authentication
- JWT tokens for internal systems
- API keys for external systems (TODO: implement)
- Rate limiting on event endpoint (TODO: implement)
### Data Validation
- All events validated before storage
- Payload JSON validated
- Encounter ID format validation
- Source system whitelist
## Troubleshooting
### Event Not Processing
1. Check event status in admin
2. Check Celery worker logs
3. Verify journey instance exists
4. Verify stage template has matching trigger code
5. Check stage instance status (must be PENDING or IN_PROGRESS)
### Survey Not Sent
1. Verify `auto_send_survey=True` on stage template
2. Verify `survey_template` is set
3. Check survey instance created
4. Check notification logs (Phase 4)
### Stage Not Completing
1. Verify event code matches `trigger_event_code`
2. Check stage instance status
3. Verify physician/department codes if required
4. Check event processing logs
---
**For API documentation, see:** `/api/docs/`
**For implementation status, see:** `IMPLEMENTATION_STATUS.md`