HH/COMPLAINT_CATEGORY_SUBCATEGORY_EXAMINATION.md

500 lines
15 KiB
Markdown

# 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
```python
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)
```python
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**:
```json
{
"categories": [
{
"id": "uuid",
"name_en": "CLINICAL",
"name_ar": "سريري",
"code": "CLINICAL",
"parent_id": null,
"level": 1,
"domain_type": "CLINICAL",
"description_en": "...",
"description_ar": "..."
},
...
]
}
```
### Query Logic
```python
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)
```javascript
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:
```javascript
$(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**:
```javascript
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.'
});
}
```
2. **Add success message when domains load**:
```javascript
success: function(response) {
console.log('Loaded categories:', response.categories.length);
allCategories = response.categories;
// ... rest of code
}
```
3. **Add loading indicator**:
```javascript
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
```bash
python manage.py load_shct_taxonomy
```
### Examine Taxonomy Structure
```bash
python examine_taxonomy.py
```
### Diagnose Domain Dropdown Issue
```bash
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