518 lines
20 KiB
Python
518 lines
20 KiB
Python
"""
|
|
External API serializers for public-facing create/retrieve operations.
|
|
"""
|
|
|
|
from rest_framework import serializers
|
|
|
|
from apps.complaints.models import Complaint, Inquiry
|
|
from apps.observations.models import Observation
|
|
from apps.appreciation.models import Appreciation, AppreciationStatus, AppreciationVisibility
|
|
from apps.feedback.models import Feedback, FeedbackType, FeedbackStatus, FeedbackCategory
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Complaint
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ExternalComplaintCreateSerializer(serializers.Serializer):
|
|
"""Public-facing fields for creating a complaint via external API."""
|
|
|
|
# Contact
|
|
contact_name = serializers.CharField(max_length=200, required=True)
|
|
contact_phone = serializers.CharField(max_length=20, required=True)
|
|
contact_email = serializers.EmailField(required=False, allow_blank=True, default="")
|
|
relation_to_patient = serializers.ChoiceField(
|
|
choices=[("patient", "Patient"), ("relative", "Relative")],
|
|
required=False,
|
|
allow_blank=True,
|
|
default="",
|
|
)
|
|
|
|
# Patient
|
|
patient_name = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
|
national_id = serializers.CharField(max_length=50, required=False, allow_blank=True, default="")
|
|
incident_date = serializers.DateField(required=False, allow_null=True, default=None)
|
|
|
|
# Hospital context
|
|
hospital = serializers.CharField(max_length=200, required=True)
|
|
|
|
# Location hierarchy (all optional)
|
|
location_type = serializers.ChoiceField(
|
|
choices=[("OP", "Outpatient"), ("IP", "Inpatient"), ("ER", "Emergency"), ("GENERAL", "General")],
|
|
required=False,
|
|
allow_blank=True,
|
|
default="",
|
|
)
|
|
area = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
|
department = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
|
section = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
|
|
|
# Complaint content
|
|
title = serializers.CharField(max_length=500, required=True)
|
|
description = serializers.CharField(required=True)
|
|
expected_result = serializers.CharField(required=False, allow_blank=True, default="")
|
|
|
|
def validate_hospital(self, value):
|
|
from apps.organizations.models import Hospital
|
|
try:
|
|
return Hospital.objects.get(name__iexact=value.strip())
|
|
except Hospital.DoesNotExist:
|
|
raise serializers.ValidationError("Hospital not found.")
|
|
|
|
def validate(self, data):
|
|
hospital = data.get("hospital")
|
|
if hospital:
|
|
area_name = data.get("area")
|
|
if area_name:
|
|
from apps.organizations.models import Area
|
|
try:
|
|
data["area"] = Area.objects.get(
|
|
name_en__iexact=area_name.strip(), hospital=hospital, status="active"
|
|
)
|
|
except Area.DoesNotExist:
|
|
raise serializers.ValidationError({"area": f"Area '{area_name}' not found for this hospital."})
|
|
|
|
dept_name = data.get("department")
|
|
if dept_name:
|
|
from apps.organizations.models import Department
|
|
try:
|
|
data["department"] = Department.objects.get(
|
|
name__iexact=dept_name.strip(), hospital=hospital, status="active"
|
|
)
|
|
except Department.DoesNotExist:
|
|
raise serializers.ValidationError(
|
|
{"department": f"Department '{dept_name}' not found for this hospital."}
|
|
)
|
|
|
|
section_name = data.get("section")
|
|
if section_name:
|
|
department = data.get("department")
|
|
if not department:
|
|
raise serializers.ValidationError({"section": "Department is required to look up section."})
|
|
from apps.organizations.models import Section
|
|
try:
|
|
data["section"] = Section.objects.get(
|
|
name_en__iexact=section_name.strip(), department=department, status="active"
|
|
)
|
|
except Section.DoesNotExist:
|
|
raise serializers.ValidationError(
|
|
{"section": f"Section '{section_name}' not found for this department."}
|
|
)
|
|
|
|
return data
|
|
|
|
|
|
class ExternalComplaintRetrieveSerializer(serializers.ModelSerializer):
|
|
"""Read-only serializer for retrieving a complaint."""
|
|
|
|
hospital_name = serializers.CharField(source="hospital.name", read_only=True)
|
|
|
|
class Meta:
|
|
model = Complaint
|
|
fields = [
|
|
"id",
|
|
"reference_number",
|
|
"title",
|
|
"description",
|
|
"status",
|
|
"severity",
|
|
"priority",
|
|
"contact_name",
|
|
"contact_phone",
|
|
"contact_email",
|
|
"relation_to_patient",
|
|
"patient_name",
|
|
"incident_date",
|
|
"expected_result",
|
|
"resolution",
|
|
"satisfaction",
|
|
"hospital_name",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Inquiry
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ExternalInquiryCreateSerializer(serializers.Serializer):
|
|
"""Public-facing fields for creating an inquiry via external API."""
|
|
|
|
# Contact
|
|
contact_name = serializers.CharField(max_length=200, required=True)
|
|
contact_phone = serializers.CharField(max_length=20, required=True)
|
|
contact_email = serializers.EmailField(required=False, allow_blank=True, default="")
|
|
|
|
# Hospital context
|
|
hospital = serializers.CharField(max_length=200, required=True)
|
|
|
|
# Location hierarchy (all optional)
|
|
location_type = serializers.ChoiceField(
|
|
choices=[("OP", "Outpatient"), ("IP", "Inpatient"), ("ER", "Emergency"), ("GENERAL", "General")],
|
|
required=False,
|
|
allow_blank=True,
|
|
default="",
|
|
)
|
|
area = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
|
department = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
|
section = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
|
|
|
# Inquiry content
|
|
subject = serializers.CharField(max_length=500, required=True)
|
|
message = serializers.CharField(required=True)
|
|
category = serializers.ChoiceField(
|
|
choices=[
|
|
("appointment", "Appointment"),
|
|
("billing", "Billing"),
|
|
("medical_records", "Medical Records"),
|
|
("general", "General Information"),
|
|
("other", "Other"),
|
|
],
|
|
required=False,
|
|
default="general",
|
|
)
|
|
|
|
def validate_hospital(self, value):
|
|
from apps.organizations.models import Hospital
|
|
try:
|
|
return Hospital.objects.get(name__iexact=value.strip())
|
|
except Hospital.DoesNotExist:
|
|
raise serializers.ValidationError("Hospital not found.")
|
|
|
|
def validate(self, data):
|
|
hospital = data.get("hospital")
|
|
if hospital:
|
|
area_name = data.get("area")
|
|
if area_name:
|
|
from apps.organizations.models import Area
|
|
try:
|
|
data["area"] = Area.objects.get(
|
|
name_en__iexact=area_name.strip(), hospital=hospital, status="active"
|
|
)
|
|
except Area.DoesNotExist:
|
|
raise serializers.ValidationError({"area": f"Area '{area_name}' not found for this hospital."})
|
|
|
|
dept_name = data.get("department")
|
|
if dept_name:
|
|
from apps.organizations.models import Department
|
|
try:
|
|
data["department"] = Department.objects.get(
|
|
name__iexact=dept_name.strip(), hospital=hospital, status="active"
|
|
)
|
|
except Department.DoesNotExist:
|
|
raise serializers.ValidationError(
|
|
{"department": f"Department '{dept_name}' not found for this hospital."}
|
|
)
|
|
|
|
section_name = data.get("section")
|
|
if section_name:
|
|
department = data.get("department")
|
|
if not department:
|
|
raise serializers.ValidationError({"section": "Department is required to look up section."})
|
|
from apps.organizations.models import Section
|
|
try:
|
|
data["section"] = Section.objects.get(
|
|
name_en__iexact=section_name.strip(), department=department, status="active"
|
|
)
|
|
except Section.DoesNotExist:
|
|
raise serializers.ValidationError(
|
|
{"section": f"Section '{section_name}' not found for this department."}
|
|
)
|
|
|
|
return data
|
|
|
|
|
|
class ExternalInquiryRetrieveSerializer(serializers.ModelSerializer):
|
|
"""Read-only serializer for retrieving an inquiry."""
|
|
|
|
hospital_name = serializers.CharField(source="hospital.name", read_only=True)
|
|
|
|
class Meta:
|
|
model = Inquiry
|
|
fields = [
|
|
"id",
|
|
"reference_number",
|
|
"subject",
|
|
"message",
|
|
"category",
|
|
"status",
|
|
"contact_name",
|
|
"contact_phone",
|
|
"contact_email",
|
|
"hospital_name",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Observation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ExternalObservationCreateSerializer(serializers.Serializer):
|
|
"""Public-facing fields for creating an observation via external API."""
|
|
|
|
# Hospital context
|
|
hospital = serializers.CharField(max_length=200, required=True)
|
|
|
|
# Classification
|
|
category = serializers.UUIDField(required=False, allow_null=True, default=None)
|
|
|
|
# Content
|
|
title = serializers.CharField(max_length=300, required=False, allow_blank=True, default="")
|
|
description = serializers.CharField(required=True)
|
|
severity = serializers.ChoiceField(
|
|
choices=[("low", "Low"), ("medium", "Medium"), ("high", "High"), ("critical", "Critical")],
|
|
required=False,
|
|
default="medium",
|
|
)
|
|
|
|
# Location and timing
|
|
location_text = serializers.CharField(max_length=500, required=False, allow_blank=True, default="")
|
|
incident_datetime = serializers.DateTimeField(required=False, allow_null=True, default=None)
|
|
|
|
# Reporter info (all optional - anonymous supported)
|
|
contact_name = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
|
contact_phone = serializers.CharField(max_length=20, required=False, allow_blank=True, default="")
|
|
contact_email = serializers.EmailField(required=False, allow_blank=True, default="")
|
|
reporter_staff_id = serializers.CharField(max_length=50, required=False, allow_blank=True, default="")
|
|
|
|
# Patient info
|
|
patient_file_number = serializers.CharField(max_length=100, required=False, allow_blank=True, default="")
|
|
|
|
def validate_hospital(self, value):
|
|
from apps.organizations.models import Hospital
|
|
try:
|
|
return Hospital.objects.get(name__iexact=value.strip())
|
|
except Hospital.DoesNotExist:
|
|
raise serializers.ValidationError("Hospital not found.")
|
|
|
|
def validate_category(self, value):
|
|
if value is None:
|
|
return None
|
|
from apps.observations.models import ObservationCategory
|
|
try:
|
|
return ObservationCategory.objects.get(id=value)
|
|
except ObservationCategory.DoesNotExist:
|
|
raise serializers.ValidationError("Observation category not found.")
|
|
|
|
|
|
class ExternalObservationRetrieveSerializer(serializers.ModelSerializer):
|
|
"""Read-only serializer for retrieving an observation."""
|
|
|
|
reference_number = serializers.CharField(source="tracking_code", read_only=True)
|
|
hospital_name = serializers.CharField(source="hospital.name", read_only=True, default=None)
|
|
category_name = serializers.CharField(source="category.name", read_only=True, default=None)
|
|
contact_name = serializers.CharField(source="reporter_name", read_only=True)
|
|
contact_phone = serializers.CharField(source="reporter_phone", read_only=True)
|
|
contact_email = serializers.CharField(source="reporter_email", read_only=True)
|
|
|
|
class Meta:
|
|
model = Observation
|
|
fields = [
|
|
"id",
|
|
"reference_number",
|
|
"title",
|
|
"description",
|
|
"severity",
|
|
"status",
|
|
"location_text",
|
|
"incident_datetime",
|
|
"contact_name",
|
|
"contact_phone",
|
|
"contact_email",
|
|
"reporter_staff_id",
|
|
"patient_file_number",
|
|
"hospital_name",
|
|
"category_name",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Appreciation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ExternalAppreciationCreateSerializer(serializers.Serializer):
|
|
"""Public-facing fields for creating an appreciation via external API."""
|
|
|
|
# Contact
|
|
contact_name = serializers.CharField(max_length=200, required=True)
|
|
contact_phone = serializers.CharField(max_length=20, required=True)
|
|
|
|
# Content
|
|
message = serializers.CharField(required=True)
|
|
|
|
# Hospital context
|
|
hospital = serializers.CharField(max_length=200, required=True)
|
|
|
|
def validate_hospital(self, value):
|
|
from apps.organizations.models import Hospital
|
|
try:
|
|
return Hospital.objects.get(name__iexact=value.strip())
|
|
except Hospital.DoesNotExist:
|
|
raise serializers.ValidationError("Hospital not found.")
|
|
|
|
|
|
class ExternalAppreciationRetrieveSerializer(serializers.ModelSerializer):
|
|
"""Read-only serializer for retrieving an appreciation."""
|
|
|
|
hospital_name = serializers.CharField(source="hospital.name", read_only=True)
|
|
|
|
class Meta:
|
|
model = Appreciation
|
|
fields = [
|
|
"id",
|
|
"reference_number",
|
|
"message_en",
|
|
"status",
|
|
"hospital_name",
|
|
"is_anonymous",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Suggestion (Feedback)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ExternalSuggestionCreateSerializer(serializers.Serializer):
|
|
"""Public-facing fields for creating a suggestion via external API."""
|
|
|
|
# Contact
|
|
contact_name = serializers.CharField(max_length=200, required=True)
|
|
contact_phone = serializers.CharField(max_length=20, required=True)
|
|
|
|
# Content
|
|
title = serializers.CharField(max_length=500, required=False, allow_blank=True, default="")
|
|
message = serializers.CharField(required=True)
|
|
category = serializers.ChoiceField(
|
|
choices=[
|
|
("clinical_care", "Clinical Care"),
|
|
("staff_service", "Staff Service"),
|
|
("facility", "Facility"),
|
|
("communication", "Communication"),
|
|
("appointment", "Appointment"),
|
|
("billing", "Billing"),
|
|
("food_service", "Food Service"),
|
|
("cleanliness", "Cleanliness"),
|
|
("technology", "Technology"),
|
|
("general", "General"),
|
|
("other", "Other"),
|
|
],
|
|
required=False,
|
|
default="general",
|
|
)
|
|
rating = serializers.IntegerField(min_value=1, max_value=5, required=False, allow_null=True, default=None)
|
|
|
|
# Hospital context
|
|
hospital = serializers.CharField(max_length=200, required=True)
|
|
|
|
def validate_hospital(self, value):
|
|
from apps.organizations.models import Hospital
|
|
try:
|
|
return Hospital.objects.get(name__iexact=value.strip())
|
|
except Hospital.DoesNotExist:
|
|
raise serializers.ValidationError("Hospital not found.")
|
|
|
|
|
|
class ExternalSuggestionRetrieveSerializer(serializers.ModelSerializer):
|
|
"""Read-only serializer for retrieving a suggestion."""
|
|
|
|
hospital_name = serializers.CharField(source="hospital.name", read_only=True)
|
|
|
|
class Meta:
|
|
model = Feedback
|
|
fields = [
|
|
"id",
|
|
"reference_number",
|
|
"title",
|
|
"message",
|
|
"category",
|
|
"rating",
|
|
"status",
|
|
"feedback_type",
|
|
"sentiment",
|
|
"contact_name",
|
|
"contact_phone",
|
|
"hospital_name",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
|
|
class ExternalComplaintSatisfactionSerializer(serializers.Serializer):
|
|
satisfaction = serializers.ChoiceField(
|
|
choices=[
|
|
("satisfied", "Satisfied"),
|
|
("neutral", "Neutral"),
|
|
("dissatisfied", "Dissatisfied"),
|
|
("no_response", "No Response"),
|
|
],
|
|
required=True,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Doctor Rating
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ExternalDoctorRatingCreateSerializer(serializers.Serializer):
|
|
|
|
hospital_id = serializers.IntegerField(required=True)
|
|
doctor_id = serializers.CharField(max_length=50, required=True)
|
|
doctor_name = serializers.CharField(max_length=300, required=False, allow_blank=True, default="")
|
|
rating = serializers.IntegerField(min_value=1, max_value=5, required=True)
|
|
feedback = serializers.CharField(required=False, allow_blank=True, default="")
|
|
rating_date = serializers.DateField(required=False, allow_null=True, default=None)
|
|
patient_uhid = serializers.CharField(max_length=100, required=False, allow_blank=True, default="")
|
|
patient_name = serializers.CharField(max_length=300, required=False, allow_blank=True, default="")
|
|
patient_type = serializers.ChoiceField(
|
|
choices=["IP", "OP", "ER", "DC"],
|
|
required=False,
|
|
allow_blank=True,
|
|
default="",
|
|
)
|
|
department_name = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
|
admit_date = serializers.DateField(required=False, allow_null=True, default=None)
|
|
discharge_date = serializers.DateField(required=False, allow_null=True, default=None)
|
|
|
|
def validate_hospital_id(self, value):
|
|
from apps.organizations.models import Hospital
|
|
|
|
try:
|
|
return Hospital.objects.get(id=value)
|
|
except Hospital.DoesNotExist:
|
|
raise serializers.ValidationError("Hospital not found.")
|
|
|
|
def validate_doctor_id(self, value):
|
|
return value.strip()
|
|
|
|
def validate(self, data):
|
|
from apps.organizations.models import Staff
|
|
|
|
hospital = data.get("hospital_id")
|
|
doctor_id = data.get("doctor_id", "").strip()
|
|
|
|
if hospital and doctor_id:
|
|
staff = Staff.objects.filter(
|
|
hospital=hospital, employee_id=doctor_id
|
|
).first()
|
|
if not staff:
|
|
raise serializers.ValidationError(
|
|
{
|
|
"doctor_id": f"Doctor with employee ID '{doctor_id}' not found at this hospital."
|
|
}
|
|
)
|
|
data["_staff"] = staff
|
|
|
|
return data
|