111 lines
3.6 KiB
Markdown
111 lines
3.6 KiB
Markdown
# Survey Multiple Access Fix
|
|
|
|
## Problem
|
|
|
|
Previously, survey links could only be viewed once. When a patient refreshed the page or revisited the survey link, they would see an error message:
|
|
|
|
> "We're sorry, but this survey link is no longer valid or has expired."
|
|
|
|
This was caused by the survey status being updated to `'viewed'` on first access, which was not included in the list of allowed statuses for subsequent accesses.
|
|
|
|
## Root Cause
|
|
|
|
In `apps/surveys/public_views.py`, the `survey_form` view was filtering surveys by status:
|
|
|
|
```python
|
|
survey = SurveyInstance.objects.get(
|
|
access_token=token,
|
|
status__in=['pending', 'sent', 'in_progress'], # Missing 'viewed'!
|
|
token_expires_at__gt=timezone.now()
|
|
)
|
|
```
|
|
|
|
**Flow of the bug:**
|
|
1. Patient opens survey link → Status changes from `'sent'` to `'viewed'`
|
|
2. Patient refreshes page or opens link again
|
|
3. Status is now `'viewed'` → **Not in allowed list**
|
|
4. SurveyInstance.DoesNotExist → Error page shown
|
|
|
|
## Solution
|
|
|
|
Added `'viewed'` to the list of allowed statuses:
|
|
|
|
```python
|
|
survey = SurveyInstance.objects.get(
|
|
access_token=token,
|
|
status__in=['pending', 'sent', 'viewed', 'in_progress'],
|
|
token_expires_at__gt=timezone.now()
|
|
)
|
|
```
|
|
|
|
## Behavior After Fix
|
|
|
|
### Survey Access Rules
|
|
|
|
✅ **Can Access Multiple Times:**
|
|
- Status: `pending`, `sent`, `viewed`, `in_progress`
|
|
- Token: Not expired (`token_expires_at > now`)
|
|
|
|
❌ **Cannot Access:**
|
|
- Status: `completed`, `expired`, `cancelled`
|
|
- Token: Expired (`token_expires_at <= now`)
|
|
|
|
### Survey Lifecycle
|
|
|
|
1. **Created** → Status: `pending`, Token: Valid for 2 days
|
|
2. **Sent to patient** → Status: `sent`
|
|
3. **First open** → Status: `viewed`, `open_count` = 1, `opened_at` = now
|
|
4. **Patient refreshes** → Status: `viewed`, `open_count` = 2, `last_opened_at` = now
|
|
5. **Patient answers questions** → Status: `in_progress` (auto-detected)
|
|
6. **Patient refreshes again** → Status: `in_progress`, `open_count` = 3
|
|
7. **Patient submits** → Status: `completed`, `completed_at` = now
|
|
8. **Patient tries to access again** → Error: Survey already completed
|
|
9. **After 2 days** → Token expires → Error: Link expired
|
|
|
|
### Token Expiry
|
|
|
|
The survey link is valid for **2 days** from creation by default:
|
|
|
|
```python
|
|
token_expires_at = timezone.now() + timedelta(days=2)
|
|
```
|
|
|
|
After this time, the link shows an error message regardless of survey status.
|
|
|
|
## Files Modified
|
|
|
|
- `apps/surveys/public_views.py` - Line 31: Added `'viewed'` to allowed statuses
|
|
|
|
## Testing
|
|
|
|
A test script has been created to verify the fix:
|
|
|
|
```bash
|
|
python test_survey_multiple_access.py
|
|
```
|
|
|
|
Tests:
|
|
1. ✅ Survey can be opened multiple times
|
|
2. ✅ Survey cannot be accessed after completion
|
|
3. ✅ Survey cannot be accessed after token expiry
|
|
|
|
## Benefits
|
|
|
|
1. **Patient Experience**: Patients can now take their time to complete surveys
|
|
2. **Data Integrity**: `open_count` accurately tracks multiple visits
|
|
3. **Tracking**: Each visit is logged with timestamp and device info
|
|
4. **Flexibility**: Patients can refresh the page without losing access
|
|
5. **Security**: Still prevents access after completion or token expiry
|
|
|
|
## Backward Compatibility
|
|
|
|
This fix is fully backward compatible. Existing surveys will continue to work as expected, and the change only affects the behavior when patients refresh or revisit survey links.
|
|
|
|
## Related Features
|
|
|
|
This fix works in conjunction with:
|
|
- **Survey Tracking System**: Tracks opens, time spent, and completion
|
|
- **Automatic Status Detection**: `viewed` → `in_progress` on first interaction
|
|
- **Abandoned Survey Detection**: Auto-marks surveys as abandoned after 24 hours
|
|
- **2-Day Token Expiry**: Link automatically expires after 48 hours
|