HH/apps/rca/forms.py

273 lines
10 KiB
Python

"""
RCA (Root Cause Analysis) forms
"""
from django import forms
from django.utils import timezone
from apps.core.models import PriorityChoices
from apps.core.form_mixins import HospitalFieldMixin
from .models import (
RCAActionStatus,
RCAActionType,
RCACorrectiveAction,
RCAStatus,
RCASeverity,
RCAAttachment,
RCANote,
RCARootCause,
RootCauseAnalysis,
)
class RootCauseAnalysisForm(HospitalFieldMixin, 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):
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")
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"}),
}