808 lines
24 KiB
Markdown
808 lines
24 KiB
Markdown
# Consent Implementation Analysis Report
|
|
|
|
**Date:** October 30, 2025
|
|
**Project:** AgdarCentre - Tenhal Multidisciplinary Healthcare Platform
|
|
**Analysis Scope:** Consent enforcement across all clinical applications
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
The consent implementation in AgdarCentre is **NOT PERFECT**. While the core infrastructure is robust, there are critical gaps in enforcement across clinical applications. Consent verification is only enforced at appointment check-in, but NOT when creating clinical documentation (consultations, sessions, assessments).
|
|
|
|
**Risk Level:** 🔴 **HIGH** - Providers can document services without verifying patient consent, creating legal and compliance risks.
|
|
|
|
---
|
|
|
|
## Current Implementation Status
|
|
|
|
### ✅ Strengths
|
|
|
|
#### 1. Core Infrastructure (core app)
|
|
- **Models:**
|
|
- `Consent` model with comprehensive fields
|
|
- `ConsentTemplate` model for reusable templates
|
|
- E-signature support (drawn, typed, uploaded, external)
|
|
- Version control via `HistoricalRecords`
|
|
- Audit trail with IP address, user agent tracking
|
|
- Signature hash for integrity verification
|
|
|
|
- **Service Layer:**
|
|
- `ConsentService` class with verification methods
|
|
- `verify_consent_for_service()` - checks general + service-specific + photo/video consents
|
|
- `get_missing_consents()` - identifies what's missing
|
|
- `get_active_consents()` - retrieves patient's active consents
|
|
- `sign_consent()` - handles consent signing workflow
|
|
- `create_consent()` - creates new consent forms
|
|
|
|
- **API & Views:**
|
|
- RESTful API endpoints via `ConsentViewSet`
|
|
- CRUD views for consent templates
|
|
- CRUD views for patient consents
|
|
- Serializers for API responses
|
|
|
|
#### 2. Appointment Integration
|
|
- **Enforcement at Check-in:**
|
|
- `AppointmentService.mark_arrival()` enforces consent verification
|
|
- State machine prevents check-in without consent
|
|
- `check_arrival_prerequisites()` provides clear feedback
|
|
- Blocks patient arrival if consents missing
|
|
|
|
- **Prerequisites Checking:**
|
|
```python
|
|
# From appointments/services.py
|
|
consent_verified, consent_message = ConsentService.verify_consent_for_service(
|
|
appointment.patient,
|
|
appointment.service_type
|
|
)
|
|
|
|
if not consent_verified:
|
|
raise ValueError(f"Consent verification required: {consent_message}")
|
|
```
|
|
|
|
---
|
|
|
|
## ❌ Critical Gaps Identified
|
|
|
|
### 1. NO Consent Enforcement in Clinical Apps
|
|
|
|
#### ABA App (aba/)
|
|
**Files Examined:** `aba/views.py`, `aba/forms.py`, `aba/models.py`
|
|
|
|
**Findings:**
|
|
- ❌ `ABAConsultCreateView` - No consent verification
|
|
- ❌ `ABASessionCreateView` - No consent verification
|
|
- ❌ `ABAConsultUpdateView` - No consent verification
|
|
- ❌ `ABASessionUpdateView` - No consent verification
|
|
|
|
**Impact:** ABA therapists can create consultations and sessions without verifying:
|
|
- General treatment consent
|
|
- ABA-specific consent (if required)
|
|
- Photo/Video consent (ABA often involves recording)
|
|
|
|
#### Medical App (medical/)
|
|
**Files Examined:** `medical/views.py`, `medical/forms.py`, `medical/models.py`
|
|
|
|
**Findings:**
|
|
- ❌ No consent verification found in any views
|
|
- Medical consultations can be created without consent checks
|
|
|
|
#### OT App (ot/)
|
|
**Files Examined:** Search results showed no consent references
|
|
|
|
**Findings:**
|
|
- ❌ No consent verification implemented
|
|
- OT sessions can be documented without consent
|
|
|
|
#### SLP App (slp/)
|
|
**Files Examined:** Search results showed no consent references
|
|
|
|
**Findings:**
|
|
- ❌ No consent verification implemented
|
|
- SLP sessions can be documented without consent
|
|
|
|
#### Nursing App (nursing/)
|
|
**Status:** Not examined but likely missing based on pattern
|
|
|
|
---
|
|
|
|
### 2. Incomplete Service Type Coverage
|
|
|
|
**Current Implementation in `ConsentService`:**
|
|
|
|
```python
|
|
# From core/services.py
|
|
|
|
@staticmethod
|
|
def _requires_service_specific_consent(service_type: str) -> bool:
|
|
specific_consent_services = [
|
|
'SURGERY',
|
|
'PROCEDURE',
|
|
'ANESTHESIA',
|
|
'BLOOD_TRANSFUSION',
|
|
'EXPERIMENTAL_TREATMENT',
|
|
]
|
|
return service_type.upper() in specific_consent_services
|
|
|
|
@staticmethod
|
|
def _requires_photo_video_consent(service_type: str) -> bool:
|
|
recording_services = [
|
|
'ABA', # ABA therapy often involves video recording
|
|
'BEHAVIORAL_THERAPY',
|
|
'RESEARCH',
|
|
]
|
|
return service_type.upper() in recording_services
|
|
```
|
|
|
|
**Missing Service Types:**
|
|
- ❌ MEDICAL (general medical consultations)
|
|
- ❌ OT (occupational therapy)
|
|
- ❌ SLP (speech-language pathology)
|
|
- ❌ NURSING (nursing procedures)
|
|
- ❌ PHYSIOTHERAPY
|
|
- ❌ PSYCHOLOGY
|
|
- ❌ NUTRITION
|
|
|
|
**Problem:** The service type mapping is hardcoded and incomplete. Most clinical services are not recognized.
|
|
|
|
---
|
|
|
|
### 3. No Consent Checks Before Clinical Documentation
|
|
|
|
**Current Flow:**
|
|
1. Patient books appointment ✅
|
|
2. Patient arrives → Consent checked ✅
|
|
3. Provider creates consultation/session → **NO CONSENT CHECK** ❌
|
|
|
|
**Expected Flow:**
|
|
1. Patient books appointment ✅
|
|
2. Patient arrives → Consent checked ✅
|
|
3. Provider creates consultation/session → **CONSENT VERIFIED** ✅
|
|
|
|
**Gap:** Providers can bypass the appointment system and create documentation directly without consent verification.
|
|
|
|
---
|
|
|
|
### 4. Limited Consent Types
|
|
|
|
**Current Consent Types:**
|
|
```python
|
|
class ConsentType(models.TextChoices):
|
|
GENERAL_TREATMENT = 'GENERAL_TREATMENT', _('General Treatment')
|
|
SERVICE_SPECIFIC = 'SERVICE_SPECIFIC', _('Service Specific')
|
|
PHOTO_VIDEO = 'PHOTO_VIDEO', _('Photo/Video')
|
|
DATA_SHARING = 'DATA_SHARING', _('Data Sharing')
|
|
```
|
|
|
|
**Missing Consent Types:**
|
|
- ❌ ABA_THERAPY - Specific consent for ABA services
|
|
- ❌ OCCUPATIONAL_THERAPY - Specific consent for OT services
|
|
- ❌ SPEECH_THERAPY - Specific consent for SLP services
|
|
- ❌ MEDICAL_PROCEDURE - Specific consent for medical procedures
|
|
- ❌ TELEHEALTH - Consent for remote services
|
|
- ❌ RESEARCH_PARTICIPATION - Research consent
|
|
- ❌ STUDENT_OBSERVATION - Consent for student observers
|
|
- ❌ EMERGENCY_TREATMENT - Emergency consent
|
|
|
|
---
|
|
|
|
### 5. No Consent Expiration Logic
|
|
|
|
**Current Implementation:**
|
|
- Consents have no expiration date
|
|
- Once signed, valid indefinitely
|
|
- No mechanism to require renewal
|
|
|
|
**Missing Fields:**
|
|
- `expires_at` - When consent expires
|
|
- `renewal_required` - Flag for renewal
|
|
- `renewal_period_days` - How often to renew
|
|
|
|
**Risk:** Outdated consents remain valid, potentially violating regulations requiring periodic renewal.
|
|
|
|
---
|
|
|
|
### 6. No Consent Withdrawal Mechanism
|
|
|
|
**Current Implementation:**
|
|
- Patients cannot withdraw consent
|
|
- No workflow for consent revocation
|
|
- `is_active` flag exists but no withdrawal process
|
|
|
|
**Missing Features:**
|
|
- Consent withdrawal workflow
|
|
- `withdrawn_at` timestamp
|
|
- `withdrawn_by` user reference
|
|
- `withdrawal_reason` text field
|
|
- Audit trail for withdrawals
|
|
|
|
**Risk:** Violates patient rights to withdraw consent at any time.
|
|
|
|
---
|
|
|
|
### 7. No Consent Dependency Management
|
|
|
|
**Problem:** Some consents depend on others:
|
|
- Service-specific consent requires general treatment consent
|
|
- Photo/video consent may require general consent
|
|
- Research consent may require treatment consent
|
|
|
|
**Missing:**
|
|
- Dependency validation
|
|
- Cascade withdrawal (withdrawing general consent should affect specific consents)
|
|
- Prerequisite checking
|
|
|
|
---
|
|
|
|
### 8. No Consent Compliance Reporting
|
|
|
|
**Missing Features:**
|
|
- Dashboard showing consent compliance rates
|
|
- Alerts for missing consents
|
|
- Reports on unsigned consents
|
|
- Expiring consent notifications
|
|
- Consent coverage by service type
|
|
|
|
---
|
|
|
|
## Detailed Recommendations
|
|
|
|
### 🔴 HIGH PRIORITY (Immediate Action Required)
|
|
|
|
#### 1. Create ConsentRequiredMixin for Clinical Views
|
|
|
|
**File:** `core/mixins.py`
|
|
|
|
**Purpose:** Reusable mixin to enforce consent verification in all clinical CreateViews
|
|
|
|
**Implementation:**
|
|
```python
|
|
class ConsentRequiredMixin:
|
|
"""
|
|
Mixin to enforce consent verification before creating clinical documentation.
|
|
|
|
Usage:
|
|
class MyConsultCreateView(ConsentRequiredMixin, CreateView):
|
|
consent_service_type = 'ABA' # Required
|
|
consent_types_required = ['GENERAL_TREATMENT', 'ABA_THERAPY'] # Optional
|
|
"""
|
|
consent_service_type = None # Must be set by subclass
|
|
consent_types_required = None # Optional specific consent types
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
# Get patient from form or URL
|
|
patient = self.get_patient()
|
|
|
|
if patient:
|
|
# Verify consent
|
|
from core.services import ConsentService
|
|
|
|
has_consent, message = ConsentService.verify_consent_for_service(
|
|
patient,
|
|
self.consent_service_type
|
|
)
|
|
|
|
if not has_consent:
|
|
messages.error(request, f"Cannot create documentation: {message}")
|
|
return redirect('core:consent_create', patient_id=patient.id)
|
|
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_patient(self):
|
|
"""Override to specify how to get patient."""
|
|
raise NotImplementedError("Subclass must implement get_patient()")
|
|
```
|
|
|
|
#### 2. Update ConsentService with Complete Service Type Coverage
|
|
|
|
**File:** `core/services.py`
|
|
|
|
**Changes:**
|
|
```python
|
|
# Service type configuration
|
|
SERVICE_CONSENT_REQUIREMENTS = {
|
|
'MEDICAL': {
|
|
'requires_specific': False,
|
|
'requires_photo_video': False,
|
|
},
|
|
'ABA': {
|
|
'requires_specific': True,
|
|
'requires_photo_video': True, # Often involves recording
|
|
},
|
|
'OT': {
|
|
'requires_specific': True,
|
|
'requires_photo_video': False,
|
|
},
|
|
'SLP': {
|
|
'requires_specific': True,
|
|
'requires_photo_video': False,
|
|
},
|
|
'NURSING': {
|
|
'requires_specific': False,
|
|
'requires_photo_video': False,
|
|
},
|
|
'SURGERY': {
|
|
'requires_specific': True,
|
|
'requires_photo_video': False,
|
|
},
|
|
'PROCEDURE': {
|
|
'requires_specific': True,
|
|
'requires_photo_video': False,
|
|
},
|
|
# ... add more as needed
|
|
}
|
|
|
|
@staticmethod
|
|
def _requires_service_specific_consent(service_type: str) -> bool:
|
|
"""Check if service requires service-specific consent."""
|
|
config = SERVICE_CONSENT_REQUIREMENTS.get(service_type.upper(), {})
|
|
return config.get('requires_specific', False)
|
|
|
|
@staticmethod
|
|
def _requires_photo_video_consent(service_type: str) -> bool:
|
|
"""Check if service requires photo/video consent."""
|
|
config = SERVICE_CONSENT_REQUIREMENTS.get(service_type.upper(), {})
|
|
return config.get('requires_photo_video', False)
|
|
```
|
|
|
|
#### 3. Add Consent Verification to ABA Views
|
|
|
|
**File:** `aba/views.py`
|
|
|
|
**Changes:**
|
|
```python
|
|
class ABAConsultCreateView(ConsentRequiredMixin, LoginRequiredMixin,
|
|
RolePermissionMixin, AuditLogMixin,
|
|
SuccessMessageMixin, CreateView):
|
|
"""ABA consultation creation with consent enforcement."""
|
|
|
|
consent_service_type = 'ABA'
|
|
|
|
def get_patient(self):
|
|
"""Get patient from form or URL."""
|
|
patient_id = self.request.GET.get('patient')
|
|
if patient_id:
|
|
return Patient.objects.get(pk=patient_id, tenant=self.request.user.tenant)
|
|
return None
|
|
|
|
# ... rest of implementation
|
|
|
|
class ABASessionCreateView(ConsentRequiredMixin, LoginRequiredMixin,
|
|
RolePermissionMixin, AuditLogMixin,
|
|
SuccessMessageMixin, CreateView):
|
|
"""ABA session creation with consent enforcement."""
|
|
|
|
consent_service_type = 'ABA'
|
|
|
|
def get_patient(self):
|
|
"""Get patient from form or URL."""
|
|
patient_id = self.request.GET.get('patient')
|
|
if patient_id:
|
|
return Patient.objects.get(pk=patient_id, tenant=self.request.user.tenant)
|
|
return None
|
|
|
|
# ... rest of implementation
|
|
```
|
|
|
|
#### 4. Apply Same Pattern to Medical, OT, SLP Apps
|
|
|
|
**Files:** `medical/views.py`, `ot/views.py`, `slp/views.py`
|
|
|
|
**Pattern:** Add `ConsentRequiredMixin` to all consultation/session CreateViews with appropriate `consent_service_type`.
|
|
|
|
---
|
|
|
|
### 🟡 MEDIUM PRIORITY (Next Sprint)
|
|
|
|
#### 5. Add Consent Expiration Support
|
|
|
|
**File:** `core/models.py`
|
|
|
|
**Migration Required:** Yes
|
|
|
|
**Changes to Consent Model:**
|
|
```python
|
|
class Consent(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin):
|
|
# ... existing fields ...
|
|
|
|
# NEW FIELDS
|
|
expires_at = models.DateField(
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_("Expires At"),
|
|
help_text=_("Date when this consent expires and requires renewal")
|
|
)
|
|
renewal_required = models.BooleanField(
|
|
default=False,
|
|
verbose_name=_("Renewal Required"),
|
|
help_text=_("Whether this consent requires periodic renewal")
|
|
)
|
|
renewal_period_days = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_("Renewal Period (Days)"),
|
|
help_text=_("Number of days before consent expires and requires renewal")
|
|
)
|
|
|
|
def is_expired(self):
|
|
"""Check if consent has expired."""
|
|
if self.expires_at:
|
|
from django.utils import timezone
|
|
return timezone.now().date() > self.expires_at
|
|
return False
|
|
|
|
def days_until_expiration(self):
|
|
"""Get days until expiration."""
|
|
if self.expires_at:
|
|
from django.utils import timezone
|
|
delta = self.expires_at - timezone.now().date()
|
|
return delta.days
|
|
return None
|
|
```
|
|
|
|
**Update ConsentService:**
|
|
```python
|
|
@staticmethod
|
|
def verify_consent_for_service(patient: Patient, service_type: str) -> Tuple[bool, str]:
|
|
"""Check if patient has valid (non-expired) consent for service."""
|
|
|
|
# Check general consent
|
|
general_consent = Consent.objects.filter(
|
|
patient=patient,
|
|
consent_type='GENERAL_TREATMENT',
|
|
is_active=True,
|
|
signed_at__isnull=False
|
|
).first()
|
|
|
|
if not general_consent:
|
|
return False, "General treatment consent required."
|
|
|
|
# Check if expired
|
|
if general_consent.is_expired():
|
|
return False, "General treatment consent has expired. Please renew."
|
|
|
|
# ... rest of verification logic
|
|
```
|
|
|
|
#### 6. Add Consent Withdrawal Workflow
|
|
|
|
**File:** `core/models.py`
|
|
|
|
**Migration Required:** Yes
|
|
|
|
**Changes to Consent Model:**
|
|
```python
|
|
class Consent(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin):
|
|
# ... existing fields ...
|
|
|
|
# NEW FIELDS
|
|
withdrawn_at = models.DateTimeField(
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_("Withdrawn At")
|
|
)
|
|
withdrawn_by = models.ForeignKey(
|
|
User,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='withdrawn_consents',
|
|
verbose_name=_("Withdrawn By")
|
|
)
|
|
withdrawal_reason = models.TextField(
|
|
blank=True,
|
|
verbose_name=_("Withdrawal Reason")
|
|
)
|
|
|
|
def withdraw(self, user, reason):
|
|
"""Withdraw this consent."""
|
|
from django.utils import timezone
|
|
self.withdrawn_at = timezone.now()
|
|
self.withdrawn_by = user
|
|
self.withdrawal_reason = reason
|
|
self.is_active = False
|
|
self.save()
|
|
```
|
|
|
|
**Add Withdrawal View:**
|
|
```python
|
|
class ConsentWithdrawView(LoginRequiredMixin, RolePermissionMixin, UpdateView):
|
|
"""Withdraw a consent."""
|
|
model = Consent
|
|
template_name = 'clinic/consent_withdraw.html'
|
|
fields = ['withdrawal_reason']
|
|
|
|
def form_valid(self, form):
|
|
self.object.withdraw(
|
|
user=self.request.user,
|
|
reason=form.cleaned_data['withdrawal_reason']
|
|
)
|
|
messages.success(self.request, "Consent withdrawn successfully.")
|
|
return redirect('core:patient_detail', pk=self.object.patient.id)
|
|
```
|
|
|
|
#### 7. Expand Consent Types
|
|
|
|
**File:** `core/models.py`
|
|
|
|
**Migration Required:** Yes
|
|
|
|
**Changes:**
|
|
```python
|
|
class ConsentType(models.TextChoices):
|
|
GENERAL_TREATMENT = 'GENERAL_TREATMENT', _('General Treatment')
|
|
SERVICE_SPECIFIC = 'SERVICE_SPECIFIC', _('Service Specific')
|
|
PHOTO_VIDEO = 'PHOTO_VIDEO', _('Photo/Video')
|
|
DATA_SHARING = 'DATA_SHARING', _('Data Sharing')
|
|
|
|
# NEW TYPES
|
|
ABA_THERAPY = 'ABA_THERAPY', _('ABA Therapy')
|
|
OCCUPATIONAL_THERAPY = 'OCCUPATIONAL_THERAPY', _('Occupational Therapy')
|
|
SPEECH_THERAPY = 'SPEECH_THERAPY', _('Speech-Language Therapy')
|
|
MEDICAL_PROCEDURE = 'MEDICAL_PROCEDURE', _('Medical Procedure')
|
|
TELEHEALTH = 'TELEHEALTH', _('Telehealth Services')
|
|
RESEARCH = 'RESEARCH', _('Research Participation')
|
|
STUDENT_OBSERVATION = 'STUDENT_OBSERVATION', _('Student Observation')
|
|
EMERGENCY_TREATMENT = 'EMERGENCY_TREATMENT', _('Emergency Treatment')
|
|
```
|
|
|
|
---
|
|
|
|
### 🟢 LOW PRIORITY (Future Enhancement)
|
|
|
|
#### 8. Consent Compliance Dashboard
|
|
|
|
**Features:**
|
|
- Overall consent compliance rate
|
|
- Missing consents by patient
|
|
- Expiring consents (next 30 days)
|
|
- Consent coverage by service type
|
|
- Withdrawal statistics
|
|
|
|
#### 9. Automated Consent Reminders
|
|
|
|
**Features:**
|
|
- Email/SMS reminders for expiring consents
|
|
- Notifications for missing consents
|
|
- Scheduled tasks to check compliance
|
|
|
|
#### 10. Bulk Consent Operations
|
|
|
|
**Features:**
|
|
- Sign multiple consents at once
|
|
- Family consent management
|
|
- Batch consent renewal
|
|
|
|
#### 11. Consent Dependency Management
|
|
|
|
**Features:**
|
|
- Define consent prerequisites
|
|
- Cascade withdrawal logic
|
|
- Dependency validation
|
|
|
|
---
|
|
|
|
## Implementation Timeline
|
|
|
|
### Week 1: Critical Fixes
|
|
- [ ] Create `ConsentRequiredMixin`
|
|
- [ ] Update `ConsentService` with complete service type coverage
|
|
- [ ] Add consent verification to ABA views
|
|
- [ ] Add consent verification to Medical views
|
|
|
|
### Week 2: Remaining Apps
|
|
- [ ] Add consent verification to OT views
|
|
- [ ] Add consent verification to SLP views
|
|
- [ ] Add consent verification to Nursing views
|
|
- [ ] Testing and bug fixes
|
|
|
|
### Week 3: Expiration & Withdrawal
|
|
- [ ] Add expiration fields to Consent model
|
|
- [ ] Implement expiration checking logic
|
|
- [ ] Add withdrawal workflow
|
|
- [ ] Create withdrawal views and templates
|
|
|
|
### Week 4: Enhanced Types & Reporting
|
|
- [ ] Expand consent types
|
|
- [ ] Create consent compliance dashboard
|
|
- [ ] Add automated reminders
|
|
- [ ] Documentation and training
|
|
|
|
---
|
|
|
|
## Testing Requirements
|
|
|
|
### Unit Tests
|
|
- [ ] Test `ConsentRequiredMixin` blocks unauthorized access
|
|
- [ ] Test `ConsentService.verify_consent_for_service()` with all service types
|
|
- [ ] Test consent expiration logic
|
|
- [ ] Test consent withdrawal workflow
|
|
|
|
### Integration Tests
|
|
- [ ] Test ABA consultation creation with/without consent
|
|
- [ ] Test Medical consultation creation with/without consent
|
|
- [ ] Test appointment check-in with expired consent
|
|
- [ ] Test consent withdrawal cascade effects
|
|
|
|
### User Acceptance Tests
|
|
- [ ] Provider attempts to create consultation without consent
|
|
- [ ] Patient signs consent and provider creates consultation
|
|
- [ ] Patient withdraws consent and provider cannot create documentation
|
|
- [ ] Admin views consent compliance dashboard
|
|
|
|
---
|
|
|
|
## Compliance & Legal Considerations
|
|
|
|
### Regulatory Requirements
|
|
- **HIPAA:** Consent for treatment and data sharing
|
|
- **Saudi MOH:** Informed consent requirements
|
|
- **GDPR (if applicable):** Right to withdraw consent
|
|
|
|
### Audit Trail
|
|
- All consent actions must be logged
|
|
- IP address and user agent tracking
|
|
- Historical records via `simple-history`
|
|
|
|
### Documentation
|
|
- Consent templates must be reviewed by legal
|
|
- Multi-language support required
|
|
- Clear withdrawal process documented
|
|
|
|
---
|
|
|
|
## Conclusion
|
|
|
|
The consent implementation has a solid foundation but critical enforcement gaps. The highest priority is adding `ConsentRequiredMixin` to all clinical CreateViews to prevent documentation without consent. This can be accomplished in 1-2 weeks with proper testing.
|
|
|
|
**Recommendation:** Implement HIGH PRIORITY items immediately to close legal and compliance gaps.
|
|
|
|
---
|
|
|
|
## Appendix: Code Examples
|
|
|
|
### Example: Complete ConsentRequiredMixin Implementation
|
|
|
|
```python
|
|
# core/mixins.py
|
|
|
|
from django.contrib import messages
|
|
from django.shortcuts import redirect
|
|
from django.urls import reverse
|
|
from core.services import ConsentService
|
|
|
|
|
|
class ConsentRequiredMixin:
|
|
"""
|
|
Mixin to enforce consent verification before creating clinical documentation.
|
|
|
|
Attributes:
|
|
consent_service_type (str): Required. The service type to check consent for.
|
|
consent_redirect_url (str): Optional. URL to redirect to if consent missing.
|
|
consent_error_message (str): Optional. Custom error message.
|
|
|
|
Usage:
|
|
class ABAConsultCreateView(ConsentRequiredMixin, CreateView):
|
|
consent_service_type = 'ABA'
|
|
|
|
def get_patient(self):
|
|
patient_id = self.request.GET.get('patient')
|
|
return Patient.objects.get(pk=patient_id)
|
|
"""
|
|
|
|
consent_service_type = None
|
|
consent_redirect_url = None
|
|
consent_error_message = None
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
"""Check consent before allowing access."""
|
|
|
|
# Validate configuration
|
|
if not self.consent_service_type:
|
|
raise ImproperlyConfigured(
|
|
f"{self.__class__.__name__} must define consent_service_type"
|
|
)
|
|
|
|
# Get patient
|
|
try:
|
|
patient = self.get_patient()
|
|
except Exception as e:
|
|
messages.error(request, f"Error retrieving patient: {e}")
|
|
return redirect('core:patient_list')
|
|
|
|
if patient:
|
|
# Verify consent
|
|
has_consent, message = ConsentService.verify_consent_for_service(
|
|
patient,
|
|
self.consent_service_type
|
|
)
|
|
|
|
if not has_consent:
|
|
# Get missing consents
|
|
missing = ConsentService.get_missing_consents(
|
|
patient,
|
|
self.consent_service_type
|
|
)
|
|
|
|
# Custom or default error message
|
|
error_msg = self.consent_error_message or (
|
|
f"Cannot create {self.consent_service_type} documentation: {message}. "
|
|
f"Missing consents: {', '.join(missing)}"
|
|
)
|
|
|
|
messages.error(request, error_msg)
|
|
|
|
# Redirect to consent creation or custom URL
|
|
redirect_url = self.consent_redirect_url or reverse(
|
|
'core:consent_create'
|
|
) + f'?patient={patient.id}&service_type={self.consent_service_type}'
|
|
|
|
return redirect(redirect_url)
|
|
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_patient(self):
|
|
"""
|
|
Get the patient for consent verification.
|
|
|
|
Must be implemented by subclass.
|
|
|
|
Returns:
|
|
Patient: The patient instance
|
|
|
|
Raises:
|
|
NotImplementedError: If not implemented by subclass
|
|
"""
|
|
raise NotImplementedError(
|
|
f"{self.__class__.__name__} must implement get_patient()"
|
|
)
|
|
```
|
|
|
|
### Example: Updated ABA Views
|
|
|
|
```python
|
|
# aba/views.py
|
|
|
|
class ABAConsultCreateView(ConsentRequiredMixin, LoginRequiredMixin,
|
|
RolePermissionMixin, AuditLogMixin,
|
|
SuccessMessageMixin, CreateView):
|
|
"""ABA consultation creation with consent enforcement."""
|
|
|
|
model = ABAConsult
|
|
form_class = ABAConsultForm
|
|
template_name = 'aba/consult_form.html'
|
|
success_message = "ABA consultation recorded successfully!"
|
|
allowed_roles = [User.Role.ADMIN, User.Role.ABA]
|
|
|
|
# Consent enforcement
|
|
consent_service_type = 'ABA'
|
|
consent_error_message = (
|
|
"Patient must sign ABA therapy consent before consultation can be documented."
|
|
)
|
|
|
|
def get_patient(self):
|
|
"""Get patient from URL parameter or appointment."""
|
|
patient_id = self.request.GET.get('patient')
|
|
appointment_id = self.request.GET.get('appointment_id')
|
|
|
|
if patient_id:
|
|
return Patient.objects.get(
|
|
pk=patient_id,
|
|
tenant=self.request.user.tenant
|
|
)
|
|
elif appointment_id:
|
|
appointment = Appointment.objects.get(
|
|
pk=appointment_id,
|
|
tenant=self.request.user.tenant
|
|
)
|
|
return appointment.patient
|
|
|
|
return None
|
|
|
|
# ... rest of implementation
|
|
```
|
|
|
|
---
|
|
|
|
**End of Report**
|