HH/COMPLAINT_CATEGORY_SUBCATEGORY_EXAMINATION.md

15 KiB

Complaint Category and Subcategory Structure Examination

Executive Summary

This document provides a comprehensive examination of the complaint category and subcategory structure in the PX360 system, including the 4-level SHCT taxonomy implementation and a diagnosis of the domain dropdown issue.


1. Taxonomy Structure Overview

The complaint taxonomy follows a 4-level hierarchical structure based on SHCT (Saudi Health Commission for Tourism) standards:

Level 1: DOMAIN (3 domains)

  • CLINICAL (سريري) - Medical and healthcare-related complaints
  • MANAGEMENT (إداري) - Administrative and operational complaints
  • RELATIONSHIPS (علاقات) - Staff-patient relationship complaints

Level 2: CATEGORY (8 categories)

These are specific areas within each domain. Examples include:

  • Under CLINICAL: "Medical Treatment", "Diagnosis", "Medication"
  • Under MANAGEMENT: "Billing", "Scheduling", "Facilities"
  • Under RELATIONSHIPS: "Staff Behavior", "Communication"

Level 3: SUBCATEGORY (20 subcategories)

More detailed classifications within each category. Examples:

  • Under "Medical Treatment": "Treatment Delay", "Treatment Quality"
  • Under "Billing": "Incorrect Charges", "Payment Issues"

Level 4: CLASSIFICATION (75 classifications)

The most granular level, providing specific complaint types. Examples:

  • Under "Treatment Delay": "Emergency Room", "Outpatient"
  • Under "Incorrect Charges": "Insurance", "Self-Pay"

2. Database Schema

ComplaintCategory Model

class ComplaintCategory(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    
    # Taxonomy fields
    level = models.IntegerField(
        choices=[
            (1, "DOMAIN"),
            (2, "CATEGORY"), 
            (3, "SUBCATEGORY"),
            (4, "CLASSIFICATION")
        ],
        db_index=True
    )
    parent_id = models.UUIDField(null=True, blank=True, db_index=True)
    domain_type = models.CharField(
        max_length=50,
        choices=[
            ('CLINICAL', 'Clinical'),
            ('MANAGEMENT', 'Management'),
            ('RELATIONSHIPS', 'Relationships')
        ]
    )
    
    # Bilingual fields
    name_en = models.CharField(max_length=200)
    name_ar = models.CharField(max_length=200)
    description_en = models.TextField(blank=True)
    description_ar = models.TextField(blank=True)
    
    # SHCT code
    code = models.CharField(max_length=50, unique=True)
    
    # Hospital-specific categories (optional)
    hospitals = models.ManyToManyField(
        Hospital,
        blank=True,
        related_name='complaint_categories'
    )
    
    # Ordering and status
    order = models.IntegerField(default=0)
    is_active = models.BooleanField(default=True)
    
    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

Complaint Model (4-level taxonomy)

class Complaint(models.Model):
    # Level 1: Domain (FK to ComplaintCategory)
    domain = models.ForeignKey(
        ComplaintCategory,
        on_delete=models.PROTECT,
        related_name='complaints_as_domain',
        null=True,
        blank=True
    )
    
    # Level 2: Category (FK to ComplaintCategory)
    category = models.ForeignKey(
        ComplaintCategory,
        on_delete=models.PROTECT,
        related_name='complaints_as_category',
        null=True,
        blank=True
    )
    
    # Level 3: Subcategory (stored as code)
    subcategory = models.CharField(max_length=50, blank=True)
    
    # Level 4: Classification (stored as code)
    classification = models.CharField(max_length=50, blank=True)

3. Current Taxonomy Data Status

Database Statistics (as of latest diagnostic)

  • Total Categories: 106
  • Level 1 (Domains): 3
  • Level 2 (Categories): 8
  • Level 3 (Subcategories): 20
  • Level 4 (Classifications): 75
  • Active Hospitals: 1 (Alhammadi Hospital)

Level 1 Domains in Database

  1. CLINICAL (سريري)

    • ID: 3ab92484-840b-4f81-b50a-b51d1d807929
    • Type: CLINICAL
    • Active: True
  2. MANAGEMENT (إداري)

    • ID: 1dc25dd0-a550-4cbe-9af1-0a5f8a71ce69
    • Type: MANAGEMENT
    • Active: True
  3. RELATIONSHIPS (علاقات)

    • ID: 537132ad-0035-4a1e-bea5-8ebee3c7d5af
    • Type: RELATIONSHIPS
    • Active: True

Category Visibility

  • All 106 categories are system-wide (not hospital-specific)
  • All categories have is_active=True
  • The hospital "Alhammadi Hospital" has 0 hospital-specific categories

4. API Endpoint Analysis

Endpoint: /complaints/public/api/load-categories/

Method: GET
Authentication: Not required (public form)
Parameters:

  • hospital_id (optional): UUID of selected hospital

Response Format:

{
    "categories": [
        {
            "id": "uuid",
            "name_en": "CLINICAL",
            "name_ar": "سريري",
            "code": "CLINICAL",
            "parent_id": null,
            "level": 1,
            "domain_type": "CLINICAL",
            "description_en": "...",
            "description_ar": "..."
        },
        ...
    ]
}

Query Logic

if hospital_id:
    # Return hospital-specific + system-wide categories
    categories_queryset = (
        ComplaintCategory.objects.filter(
            Q(hospitals__id=hospital_id) | Q(hospitals__isnull=True),
            is_active=True
        )
        .distinct()
        .order_by("level", "order", "name_en")
    )
else:
    # Return only system-wide categories
    categories_queryset = ComplaintCategory.objects.filter(
        hospitals__isnull=True,
        is_active=True
    ).order_by("level", "order", "name_en")

Diagnostic Results

✓ API query returns 106 categories for Alhammadi Hospital
✓ API query returns 3 Level 1 domains for dropdown
✓ All domains are active and visible


5. Frontend Implementation

Public Complaint Form Template

Location: templates/complaints/public_complaint_form.html

JavaScript Dependencies

  • jQuery 3.7.1 - Loaded via CDN in templates/layouts/public_base.html
  • SweetAlert2 - Loaded for user feedback
  • Bootstrap 5 - UI framework

Cascading Dropdown Logic

The form implements a 4-level cascading dropdown system:

  1. Hospital Selection → Triggers Domain load
  2. Domain Selection → Triggers Category load (filtered by domain)
  3. Category Selection → Triggers Subcategory load (filtered by category)
  4. Subcategory Selection → Triggers Classification load (filtered by subcategory)

Key JavaScript Functions

loadDomains(hospitalId)

function loadDomains(hospitalId) {
    if (!hospitalId) {
        // Clear all dropdowns
        $('#id_domain').find('option:not(:first)').remove();
        $('#category_container').hide();
        $('#subcategory_container').hide();
        $('#classification_container').hide();
        return;
    }

    $.ajax({
        url: '{% url "complaints:api_load_categories" %}',
        type: 'GET',
        data: { hospital_id: hospitalId },
        success: function(response) {
            allCategories = response.categories;
            const domainSelect = $('#id_domain');
            domainSelect.find('option:not(:first)').remove();

            // Only show level 1 categories (Domains)
            allCategories.forEach(function(category) {
                if (category.level === 1) {
                    domainSelect.append($('<option>', {
                        value: category.id,
                        text: getName(category)
                    }));
                }
            });
        },
        error: function() {
            console.error('Failed to load domains');
        }
    });
}

loadCategories(domainId), loadSubcategories(categoryId), loadClassifications(subcategoryId)

Similar pattern: filter allCategories by level and parent_id, then populate the appropriate dropdown.


6. Domain Dropdown Issue Diagnosis

Problem Description

The domain dropdown shows no data even though:

  • Database contains 3 active domains
  • API returns correct data when tested directly
  • jQuery is loaded
  • URL configuration is correct

Diagnostic Findings

What Works

  1. ✓ Database has 3 Level 1 domains with is_active=True
  2. ✓ API endpoint is configured correctly: /complaints/public/api/load-categories/
  3. ✓ API query returns 3 domains when filtered by hospital
  4. ✓ jQuery 3.7.1 is loaded in base template
  5. ✓ JavaScript code logic is correct
  6. ✓ AJAX endpoint returns correct JSON structure

Potential Root Causes

Most Likely: JavaScript event handler not firing or console error

Possible issues:

  1. Hospital selection event not triggering: The $('#id_hospital').on('change', ...) event handler might not be firing
  2. Console JavaScript error: An error in JavaScript preventing execution
  3. Timing issue: JavaScript code running before DOM is fully ready
  4. jQuery selector issue: $('#id_hospital') selector not finding the element
  5. CSRF token issue: Though GET requests shouldn't need CSRF

Investigation Steps

  1. Open browser developer tools

    • Press F12 or right-click → Inspect
    • Go to Console tab
    • Look for any JavaScript errors (red text)
  2. Check Network tab

    • Go to Network tab
    • Select a hospital from dropdown
    • Look for AJAX request to /complaints/public/api/load-categories/
    • Check if request is being made
    • If made, check response status and content
  3. Test API directly

    • Open URL: http://localhost:8000/complaints/public/api/load-categories/?hospital_id=<hospital_uuid>
    • Should return JSON with 106 categories including 3 domains
  4. Check console for errors

    • Common errors:
      • $ is not defined - jQuery not loaded
      • Uncaught ReferenceError - variable not defined
      • Failed to load resource - network error

7. Solution Recommendations

Immediate Fix

Add a $(document).ready() wrapper to ensure JavaScript runs after DOM loads:

$(document).ready(function() {
    // Store all categories data globally for easy access
    let allCategories = [];
    let currentLanguage = 'en';

    // Get CSRF token
    function getCSRFToken() {
        const cookieValue = document.cookie
            .split('; ')
            .find(row => row.startsWith('csrftoken='))
            ?.split('=')[1];

        if (cookieValue) {
            return cookieValue;
        }

        return $('[name="csrfmiddlewaretoken"]').val();
    }

    // ... rest of JavaScript code ...

    // Handle hospital change
    $('#id_hospital').on('change', function() {
        const hospitalId = $(this).val();
        console.log('Hospital changed to:', hospitalId); // Debug log
        loadDomains(hospitalId);
        // Clear all taxonomy dropdowns when hospital changes
        $('#id_domain').val('');
        $('#id_category').val('');
        $('#id_subcategory').val('');
        $('#id_classification').val('');
        hideAllDescriptions();
    });

    // ... rest of event handlers ...

    // Detect current language from HTML dir
    currentLanguage = $('html').attr('dir') === 'rtl' ? 'ar' : 'en';
});

Additional Improvements

  1. Add error handling to AJAX calls:
error: function(xhr, status, error) {
    console.error('Failed to load domains:', error);
    console.error('Response:', xhr.responseText);
    Swal.fire({
        icon: 'error',
        title: 'Error',
        text: 'Failed to load complaint categories. Please try again.'
    });
}
  1. Add success message when domains load:
success: function(response) {
    console.log('Loaded categories:', response.categories.length);
    allCategories = response.categories;
    // ... rest of code
}
  1. Add loading indicator:
beforeSend: function() {
    $('#id_domain').prop('disabled', true);
},
complete: function() {
    $('#id_domain').prop('disabled', false);
}

8. Testing Checklist

After implementing fixes, test the following:

  • Open public complaint form
  • Select a hospital from dropdown
  • Verify domain dropdown populates with 3 options
  • Select a domain
  • Verify category dropdown populates with child categories
  • Select a category
  • Verify subcategory dropdown populates
  • Select a subcategory
  • Verify classification dropdown populates (if applicable)
  • Submit a complaint with all 4 levels filled
  • Verify complaint is created with correct taxonomy data
  • Test in both English and Arabic modes
  • Check browser console for no errors
  • Check network tab for successful AJAX calls

9. Taxonomy Management Commands

Load SHCT Taxonomy Data

python manage.py load_shct_taxonomy

Examine Taxonomy Structure

python examine_taxonomy.py

Diagnose Domain Dropdown Issue

python diagnose_domain_dropdown.py

Create New Categories (Management Command)

Use Django admin: /admin/complaints/complaintcategory/


10. Summary

The complaint taxonomy system is well-structured with:

  • 4-level hierarchical classification (Domain → Category → Subcategory → Classification)
  • Bilingual support (English/Arabic)
  • SHCT compliance
  • 106 categories loaded in database
  • Hospital-specific and system-wide category support
  • RESTful API for frontend integration
  • Cascading dropdown UI implementation

Known Issue: Domain dropdown not populating on hospital selection

  • Backend and API are working correctly
  • ⚠️ Frontend JavaScript event handler may have timing or execution issue
  • 📋 Fix: Add $(document).ready() wrapper and debug logging

Appendix: Quick Reference

Category Levels

  • Level 1 (DOMAIN): Highest level - Clinical, Management, Relationships
  • Level 2 (CATEGORY): Specific areas within domains
  • Level 3 (SUBCATEGORY): Detailed classifications within categories
  • Level 4 (CLASSIFICATION): Most granular complaint types

API Endpoints

  • Load categories: GET /complaints/public/api/load-categories/?hospital_id={uuid}
  • Load departments: GET /complaints/public/api/load-departments/?hospital_id={uuid}

Database Tables

  • complaints_complaintcategory - Stores taxonomy structure
  • complaints_complaint - Stores complaints with 4-level taxonomy
  • organizations_hospital_complaintcategories - Hospital-category mapping

Key Files

  • apps/complaints/models.py - Database models
  • apps/complaints/ui_views.py - UI views and API endpoints
  • templates/complaints/public_complaint_form.html - Public form template
  • apps/complaints/management/commands/load_shct_taxonomy.py - Data loading

Document Version: 1.0
Last Updated: January 29, 2026
Status: Complete - Diagnosis provided, fix recommendations included