HH/apps/rca/forms.py

365 lines
12 KiB
Python

"""
RCA (Root Cause Analysis) forms
"""
from django import forms
from django.utils import timezone
from apps.core.models import PriorityChoices
from .models import (
RCAActionStatus,
RCAActionType,
RCACorrectiveAction,
RCAStatus,
RCASeverity,
RCAAttachment,
RCANote,
RCARootCause,
RootCauseAnalysis,
)
class RootCauseAnalysisForm(forms.ModelForm):
"""Form for creating and editing RootCauseAnalysis"""
class Meta:
model = RootCauseAnalysis
fields = [
'title',
'description',
'background',
'hospital',
'department',
'status',
'severity',
'priority',
'assigned_to',
'target_completion_date',
'root_cause_summary',
]
widgets = {
'title': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter RCA title'
}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Describe the incident or issue'
}),
'background': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Provide background information and context'
}),
'hospital': forms.Select(attrs={'class': 'form-select'}),
'department': forms.Select(attrs={'class': 'form-select'}),
'status': forms.Select(attrs={'class': 'form-select'}),
'severity': forms.Select(attrs={'class': 'form-select'}),
'priority': forms.Select(attrs={'class': 'form-select'}),
'assigned_to': forms.Select(attrs={'class': 'form-select'}),
'target_completion_date': forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
}),
'root_cause_summary': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Summary of root cause analysis findings'
}),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
# Filter assigned_to to show only active users
if 'assigned_to' in self.fields:
from apps.accounts.models import User
self.fields['assigned_to'].queryset = User.objects.filter(
is_active=True
).order_by('email')
# Set initial hospital if user has one
if user and not self.instance.pk:
from apps.organizations.models import Hospital
try:
user_hospital = Hospital.objects.filter(
staff__user=user
).first()
if user_hospital:
self.fields['hospital'].initial = user_hospital
self.fields['hospital'].widget.attrs['readonly'] = True
except:
pass
class RCARootCauseForm(forms.ModelForm):
"""Form for creating and editing RCARootCause"""
class Meta:
model = RCARootCause
fields = [
'description',
'category',
'contributing_factors',
'likelihood',
'impact',
'evidence',
]
widgets = {
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Describe the root cause'
}),
'category': forms.Select(attrs={'class': 'form-select'}),
'contributing_factors': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Factors that contributed to this root cause'
}),
'likelihood': forms.NumberInput(attrs={
'class': 'form-control',
'min': 1,
'max': 5,
'placeholder': '1-5'
}),
'impact': forms.NumberInput(attrs={
'class': 'form-control',
'min': 1,
'max': 5,
'placeholder': '1-5'
}),
'evidence': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Evidence supporting this root cause'
}),
}
def clean_likelihood(self):
likelihood = self.cleaned_data.get('likelihood')
if likelihood and (likelihood < 1 or likelihood > 5):
raise forms.ValidationError('Likelihood must be between 1 and 5')
return likelihood
def clean_impact(self):
impact = self.cleaned_data.get('impact')
if impact and (impact < 1 or impact > 5):
raise forms.ValidationError('Impact must be between 1 and 5')
return impact
class RCACorrectiveActionForm(forms.ModelForm):
"""Form for creating and editing RCACorrectiveAction"""
class Meta:
model = RCACorrectiveAction
fields = [
'description',
'action_type',
'root_cause',
'responsible_person',
'target_date',
'completion_date',
'status',
'effectiveness_measure',
'effectiveness_assessment',
'effectiveness_score',
'obstacles',
]
widgets = {
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Describe the corrective action'
}),
'action_type': forms.Select(attrs={'class': 'form-select'}),
'root_cause': forms.Select(attrs={'class': 'form-select'}),
'responsible_person': forms.Select(attrs={'class': 'form-select'}),
'target_date': forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
}),
'completion_date': forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
}),
'status': forms.Select(attrs={'class': 'form-select'}),
'effectiveness_measure': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'How will effectiveness be measured?'
}),
'effectiveness_assessment': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Assessment of action effectiveness'
}),
'effectiveness_score': forms.NumberInput(attrs={
'class': 'form-control',
'min': 1,
'max': 5,
'placeholder': '1-5'
}),
'obstacles': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Obstacles encountered during implementation'
}),
}
def __init__(self, *args, **kwargs):
rca = kwargs.pop('rca', None)
super().__init__(*args, **kwargs)
# Filter root_cause to show only for this RCA
if 'root_cause' in self.fields and rca:
self.fields['root_cause'].queryset = rca.root_causes.all()
def clean_effectiveness_score(self):
score = self.cleaned_data.get('effectiveness_score')
if score and (score < 1 or score > 5):
raise forms.ValidationError('Effectiveness score must be between 1 and 5')
return score
class RCAFilterForm(forms.Form):
"""Form for filtering RCA list"""
status = forms.ChoiceField(
required=False,
choices=[('', 'All Statuses')] + list(RCAStatus.choices),
widget=forms.Select(attrs={'class': 'form-select'})
)
severity = forms.ChoiceField(
required=False,
choices=[('', 'All Severities')] + list(RCASeverity.choices),
widget=forms.Select(attrs={'class': 'form-select'})
)
priority = forms.ChoiceField(
required=False,
choices=[('', 'All Priorities')] + list(PriorityChoices.choices),
widget=forms.Select(attrs={'class': 'form-select'})
)
hospital = forms.ChoiceField(
required=False,
choices=[('', 'All Hospitals')],
widget=forms.Select(attrs={'class': 'form-select'})
)
department = forms.ChoiceField(
required=False,
choices=[('', 'All Departments')],
widget=forms.Select(attrs={'class': 'form-select'})
)
search = forms.CharField(
required=False,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Search RCAs...'
})
)
date_from = forms.DateField(
required=False,
widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
)
date_to = forms.DateField(
required=False,
widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Populate hospital choices
from apps.organizations.models import Hospital
hospital_choices = [('', 'All Hospitals')]
hospital_choices.extend([
(h.id, h.name)
for h in Hospital.objects.all()
])
self.fields['hospital'].choices = hospital_choices
class RCAStatusChangeForm(forms.Form):
"""Form for changing RCA status"""
new_status = forms.ChoiceField(
choices=RCAStatus.choices,
widget=forms.Select(attrs={'class': 'form-select'})
)
notes = forms.CharField(
required=False,
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Add notes about this status change'
})
)
class RCAApprovalForm(forms.Form):
"""Form for approving RCA"""
approval_notes = forms.CharField(
required=False,
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Add approval notes'
})
)
class RCAClosureForm(forms.Form):
"""Form for closing RCA"""
closure_notes = forms.CharField(
required=True,
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Provide closure notes and summary'
})
)
actual_completion_date = forms.DateField(
required=True,
widget=forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
})
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Set default to today
self.fields['actual_completion_date'].initial = timezone.now().date()
class RCAAttachmentForm(forms.ModelForm):
"""Form for uploading RCA attachments"""
class Meta:
model = RCAAttachment
fields = ['file', 'description']
widgets = {
'file': forms.FileInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Describe this attachment'
}),
}
class RCANoteForm(forms.ModelForm):
"""Form for adding RCA notes"""
class Meta:
model = RCANote
fields = ['note', 'is_internal']
widgets = {
'note': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Add a note'
}),
'is_internal': forms.CheckboxInput(attrs={
'class': 'form-check-input'
}),
}