""" 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) reference_number = serializers.SerializerMethodField() class Meta: model = Appreciation fields = [ "id", "reference_number", "message_en", "status", "hospital_name", "is_anonymous", "created_at", "updated_at", ] read_only_fields = fields def get_reference_number(self, obj): if obj.metadata and "reference_number" in obj.metadata: return obj.metadata["reference_number"] return None # --------------------------------------------------------------------------- # 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) reference_number = serializers.SerializerMethodField() 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 def get_reference_number(self, obj): if obj.metadata and "reference_number" in obj.metadata: return obj.metadata["reference_number"] return None 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