340 lines
9.4 KiB
Markdown
340 lines
9.4 KiB
Markdown
# Survey Charts Fix - Complete Summary
|
|
|
|
## Problem Identified
|
|
|
|
The survey response list page had empty charts showing no data, even though survey data existed in the database.
|
|
|
|
### Root Causes
|
|
|
|
1. **ApexCharts Series Structure Error**
|
|
- Bar charts (Engagement Funnel, Completion Time, Score Distribution) had incorrect series structure
|
|
- They used simple arrays: `series: [18, 2, 7, 6, 29]`
|
|
- ApexCharts bar charts require: `series: [{name: 'Surveys', data: [18, 2, 7, 6, 29]}]`
|
|
- This triggered the error: "It is a possibility that you may have not included 'data' property in series"
|
|
|
|
2. **Messy Django Template Loops in JavaScript**
|
|
- All chart data was generated using `{% for %}` loops inside JavaScript code
|
|
- This made the code hard to maintain and debug
|
|
- Example of old code:
|
|
```javascript
|
|
series: [{% for item in engagement_funnel %}{{ item.count }}{% if not forloop.last %},{% endif %}{% endfor %}]
|
|
```
|
|
|
|
3. **No Safety Checks for Empty Data**
|
|
- Charts would try to render even with no data
|
|
- This caused errors when viewing as users with limited access
|
|
|
|
## Solution Implemented
|
|
|
|
### 1. Refactored View to Serialize Data to JSON
|
|
|
|
**File:** `apps/surveys/ui_views.py`
|
|
|
|
Changed from passing multiple arrays to passing JSON-serialized data:
|
|
|
|
```python
|
|
# Old way (removed)
|
|
context = {
|
|
'engagement_funnel': engagement_funnel,
|
|
'completion_time_distribution': completion_time_distribution,
|
|
'device_distribution': device_distribution,
|
|
'score_distribution': score_distribution,
|
|
'survey_type_labels': survey_type_labels,
|
|
'survey_type_counts': survey_type_counts,
|
|
'trend_labels': trend_labels,
|
|
'trend_sent': trend_sent,
|
|
'trend_completed': trend_completed,
|
|
}
|
|
|
|
# New way
|
|
import json
|
|
context = {
|
|
'engagement_funnel_json': json.dumps(engagement_funnel),
|
|
'completion_time_distribution_json': json.dumps(completion_time_distribution),
|
|
'device_distribution_json': json.dumps(device_distribution),
|
|
'score_distribution_json': json.dumps(score_distribution),
|
|
'survey_types_json': json.dumps(survey_types),
|
|
'trend_labels_json': json.dumps(trend_labels),
|
|
'trend_sent_json': json.dumps(trend_sent),
|
|
'trend_completed_json': json.dumps(trend_completed),
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Clean separation of concerns
|
|
- Data is computed once in Python, not multiple times in template
|
|
- JSON is validated before reaching the browser
|
|
- Easier to debug (can log JSON in browser console)
|
|
|
|
### 2. Fixed Bar Chart Series Structure
|
|
|
|
**File:** `templates/surveys/instance_list.html`
|
|
|
|
Fixed all three bar charts to use correct `{name, data}` format:
|
|
|
|
```javascript
|
|
// Old (BROKEN)
|
|
const engagementFunnelOptions = {
|
|
series: [{% for item in engagement_funnel %}{{ item.count }}{% if not forloop.last %},{% endif %}{% endfor %}],
|
|
// ...
|
|
};
|
|
|
|
// New (FIXED)
|
|
const engagementFunnelOptions = {
|
|
series: [{
|
|
name: 'Surveys',
|
|
data: engagementFunnelData.map(item => item.count)
|
|
}],
|
|
// ...
|
|
};
|
|
```
|
|
|
|
This was applied to:
|
|
- Engagement Funnel Chart (horizontal bar)
|
|
- Completion Time Distribution Chart (vertical bar)
|
|
- Score Distribution Chart (vertical bar)
|
|
|
|
### 3. Removed All Django Template Loops from JavaScript
|
|
|
|
**Before (messy):**
|
|
```javascript
|
|
series: [{% for item in engagement_funnel %}{{ item.count }}{% if not forloop.last %},{% endif %}{% endfor %}],
|
|
categories: [{% for item in engagement_funnel %}'{{ item.stage }}'{% if not forloop.last %},{% endif %}{% endfor %}],
|
|
```
|
|
|
|
**After (clean):**
|
|
```javascript
|
|
// Parse JSON data from server
|
|
const engagementFunnelData = {{ engagement_funnel_json|safe }};
|
|
|
|
series: [{
|
|
name: 'Surveys',
|
|
data: engagementFunnelData.map(item => item.count)
|
|
}],
|
|
categories: engagementFunnelData.map(item => item.stage),
|
|
```
|
|
|
|
**Benefits:**
|
|
- Pure JavaScript, no Django template syntax
|
|
- Modern JavaScript array methods (map, filter, etc.)
|
|
- Much easier to read and maintain
|
|
- Better IDE support and IntelliSense
|
|
- Can easily debug in browser console
|
|
|
|
### 4. Added Safety Checks for Empty Data
|
|
|
|
```javascript
|
|
// Helper function to check if data is valid
|
|
function hasData(data) {
|
|
return data && data.length > 0 && data.some(item => item.count > 0);
|
|
}
|
|
|
|
// Only render chart if data exists
|
|
if (hasData(engagementFunnelData)) {
|
|
const engagementFunnelOptions = { /* ... */ };
|
|
const engagementFunnelChart = new ApexCharts(...);
|
|
engagementFunnelChart.render();
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Prevents errors when viewing as limited users
|
|
- Charts won't render empty when no data is available
|
|
- Clean user experience - chart containers simply remain empty
|
|
|
|
### 5. Fixed Tooltip Formatters
|
|
|
|
Updated all tooltip formatters to use the new JSON data structure:
|
|
|
|
```javascript
|
|
// Old
|
|
tooltip: {
|
|
y: {
|
|
formatter: function (value, { series, seriesIndex, dataPointIndex, w }) {
|
|
var percentages = [{% for item in engagement_funnel %}{{ item.percentage }}{% if not forloop.last %},{% endif %}{% endfor %}];
|
|
return value + " surveys (" + percentages[seriesIndex] + "%)";
|
|
}
|
|
}
|
|
}
|
|
|
|
// New
|
|
tooltip: {
|
|
y: {
|
|
formatter: function (value, { seriesIndex, dataPointIndex }) {
|
|
return value + " surveys (" + engagementFunnelData[dataPointIndex].percentage + "%)";
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Charts Fixed
|
|
|
|
1. ✅ **Engagement Funnel Chart** (Horizontal Bar)
|
|
- Shows survey progression from sent to completed
|
|
- Fixed series structure
|
|
- Clean JSON data
|
|
|
|
2. ✅ **Completion Time Distribution Chart** (Vertical Bar)
|
|
- Shows how long users take to complete surveys
|
|
- Fixed series structure
|
|
- Clean JSON data
|
|
|
|
3. ✅ **Device Type Distribution Chart** (Donut)
|
|
- Shows which devices users use to complete surveys
|
|
- Already had correct structure
|
|
- Clean JSON data
|
|
|
|
4. ✅ **Score Distribution Chart** (Vertical Bar)
|
|
- Shows distribution of survey scores
|
|
- Fixed series structure
|
|
- Clean JSON data
|
|
|
|
5. ✅ **Survey Types Chart** (Donut)
|
|
- Shows breakdown by survey type
|
|
- Already had correct structure
|
|
- Clean JSON data
|
|
|
|
6. ✅ **30-Day Trend Chart** (Line)
|
|
- Shows sent vs completed surveys over time
|
|
- Already had correct structure
|
|
- Clean JSON data
|
|
|
|
## Testing Instructions
|
|
|
|
### 1. Access the Survey List Page
|
|
Navigate to: `http://localhost:8000/surveys/instances/`
|
|
|
|
### 2. Verify Charts Display
|
|
All six charts should display with actual data:
|
|
- Engagement Funnel (top left)
|
|
- Completion Time (top center)
|
|
- Device Types (top right)
|
|
- Score Distribution (bottom left)
|
|
- Survey Types (bottom center)
|
|
- 30-Day Trend (bottom right)
|
|
|
|
### 3. Check Browser Console
|
|
Open browser DevTools (F12) and check console for:
|
|
- No ApexCharts errors
|
|
- No JavaScript errors
|
|
- JSON data objects logged (if you add console.log)
|
|
|
|
### 4. Test with Different User Roles
|
|
Test as:
|
|
- PX Admin (should see all surveys)
|
|
- Hospital Admin (should see hospital's surveys only)
|
|
- Hospital User (should see hospital's surveys only)
|
|
|
|
### 5. Test with Filters
|
|
Apply different filters to ensure charts update correctly:
|
|
- Status filter
|
|
- Survey type filter
|
|
- Hospital filter
|
|
- Date range filter
|
|
|
|
## Files Modified
|
|
|
|
1. **apps/surveys/ui_views.py**
|
|
- Refactored to serialize all chart data to JSON
|
|
- Removed separate arrays for labels and counts
|
|
- Added JSON serialization using `json.dumps()`
|
|
|
|
2. **templates/surveys/instance_list.html**
|
|
- Removed all `{% for %}` loops from JavaScript
|
|
- Fixed bar chart series structure to `{name, data}` format
|
|
- Added `hasData()` helper function for safety checks
|
|
- Updated all chart configurations to use JSON data
|
|
- Fixed tooltip formatters to use data from JSON objects
|
|
|
|
## Technical Details
|
|
|
|
### JSON Data Structure
|
|
|
|
Each chart receives clean JSON arrays of objects:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"range": "1-2",
|
|
"count": 15,
|
|
"percentage": 25.5
|
|
},
|
|
{
|
|
"range": "2-3",
|
|
"count": 20,
|
|
"percentage": 34.0
|
|
}
|
|
]
|
|
```
|
|
|
|
### ApexCharts Requirements
|
|
|
|
**Bar Charts (vertical/horizontal):**
|
|
```javascript
|
|
{
|
|
series: [{
|
|
name: 'Series Name',
|
|
data: [value1, value2, value3, ...]
|
|
}],
|
|
xaxis: {
|
|
categories: ['label1', 'label2', 'label3', ...]
|
|
}
|
|
}
|
|
```
|
|
|
|
**Donut/Pie Charts:**
|
|
```javascript
|
|
{
|
|
series: [value1, value2, value3, ...],
|
|
labels: ['label1', 'label2', 'label3', ...]
|
|
}
|
|
```
|
|
|
|
**Line Charts:**
|
|
```javascript
|
|
{
|
|
series: [
|
|
{
|
|
name: 'Series 1',
|
|
data: [value1, value2, value3, ...]
|
|
},
|
|
{
|
|
name: 'Series 2',
|
|
data: [value1, value2, value3, ...]
|
|
}
|
|
],
|
|
xaxis: {
|
|
categories: ['label1', 'label2', 'label3', ...]
|
|
}
|
|
}
|
|
```
|
|
|
|
## Benefits of This Fix
|
|
|
|
1. **No More Empty Charts** - Charts display correctly with actual data
|
|
2. **Cleaner Code** - No Django template syntax in JavaScript
|
|
3. **Better Maintainability** - Easy to understand and modify
|
|
4. **Better Performance** - Data computed once, not multiple times
|
|
5. **Better Debugging** - Can inspect JSON in browser console
|
|
6. **Safer** - Charts won't crash with empty data
|
|
7. **Modern Practices** - Uses modern JavaScript array methods
|
|
8. **IDE Friendly** - Better IntelliSense and error detection
|
|
|
|
## Next Steps
|
|
|
|
1. Test the survey list page at `http://localhost:8000/surveys/instances/`
|
|
2. Verify all six charts display correctly
|
|
3. Check browser console for any errors
|
|
4. Test with different user roles and filters
|
|
5. Review the code changes if desired
|
|
|
|
## Rollback Plan (if needed)
|
|
|
|
If issues arise, the fix can be easily rolled back by reverting the two modified files:
|
|
|
|
```bash
|
|
git checkout HEAD -- apps/surveys/ui_views.py templates/surveys/instance_list.html
|
|
```
|
|
|
|
However, the old code had the charts not working at all, so rolling back would break the charts again.
|