648 lines
26 KiB
Python
648 lines
26 KiB
Python
"""
|
|
Survey Celery tasks
|
|
|
|
This module contains tasks for:
|
|
- Creating and sending surveys
|
|
- Sending survey reminders
|
|
- Processing survey responses
|
|
- Triggering actions based on negative feedback
|
|
"""
|
|
import logging
|
|
|
|
from celery import shared_task
|
|
from django.db import transaction
|
|
from django.utils import timezone
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@shared_task(bind=True, max_retries=3)
|
|
def create_and_send_survey(self, stage_instance_id):
|
|
"""
|
|
Create survey instance and send invitation.
|
|
|
|
This task is triggered when a journey stage completes
|
|
and auto_send_survey=True.
|
|
|
|
Args:
|
|
stage_instance_id: UUID of PatientJourneyStageInstance
|
|
|
|
Returns:
|
|
dict: Result with survey_instance_id and delivery status
|
|
"""
|
|
from apps.core.services import create_audit_log
|
|
from apps.journeys.models import PatientJourneyStageInstance
|
|
from apps.notifications.services import NotificationService
|
|
from apps.surveys.models import SurveyInstance
|
|
|
|
try:
|
|
# Get stage instance
|
|
stage_instance = PatientJourneyStageInstance.objects.select_related(
|
|
'stage_template__survey_template',
|
|
'journey_instance__patient',
|
|
'journey_instance__hospital'
|
|
).get(id=stage_instance_id)
|
|
|
|
# Verify survey template exists
|
|
if not stage_instance.stage_template.survey_template:
|
|
logger.warning(f"No survey template for stage {stage_instance.stage_template.name}")
|
|
return {'status': 'skipped', 'reason': 'no_survey_template'}
|
|
|
|
# Check if survey already created
|
|
if stage_instance.survey_instance:
|
|
logger.info(f"Survey already exists for stage instance {stage_instance_id}")
|
|
return {'status': 'skipped', 'reason': 'already_exists'}
|
|
|
|
patient = stage_instance.journey_instance.patient
|
|
|
|
# Determine delivery channel and recipient
|
|
delivery_channel = 'sms' # Default
|
|
recipient_phone = patient.phone
|
|
recipient_email = patient.email
|
|
|
|
# Create survey instance
|
|
with transaction.atomic():
|
|
survey_instance = SurveyInstance.objects.create(
|
|
survey_template=stage_instance.stage_template.survey_template,
|
|
patient=patient,
|
|
journey_instance=stage_instance.journey_instance,
|
|
encounter_id=stage_instance.journey_instance.encounter_id,
|
|
delivery_channel=delivery_channel,
|
|
recipient_phone=recipient_phone,
|
|
recipient_email=recipient_email
|
|
)
|
|
|
|
# Link survey to stage
|
|
stage_instance.survey_instance = survey_instance
|
|
stage_instance.survey_sent_at = timezone.now()
|
|
stage_instance.save(update_fields=['survey_instance', 'survey_sent_at'])
|
|
|
|
# Send survey invitation
|
|
notification_log = NotificationService.send_survey_invitation(
|
|
survey_instance=survey_instance,
|
|
language=patient.language if hasattr(patient, 'language') else 'en'
|
|
)
|
|
|
|
# Update survey instance status
|
|
survey_instance.status = 'active'
|
|
survey_instance.sent_at = timezone.now()
|
|
survey_instance.save(update_fields=['status', 'sent_at'])
|
|
|
|
# Log audit event
|
|
create_audit_log(
|
|
event_type='survey_sent',
|
|
description=f"Survey sent to {patient.get_full_name()} for stage {stage_instance.stage_template.name}",
|
|
content_object=survey_instance,
|
|
metadata={
|
|
'survey_template': stage_instance.stage_template.survey_template.name,
|
|
'stage': stage_instance.stage_template.name,
|
|
'encounter_id': stage_instance.journey_instance.encounter_id,
|
|
'channel': delivery_channel
|
|
}
|
|
)
|
|
|
|
logger.info(
|
|
f"Survey created and sent: {survey_instance.id} to {patient.get_full_name()} "
|
|
f"via {delivery_channel}"
|
|
)
|
|
|
|
return {
|
|
'status': 'sent',
|
|
'survey_instance_id': str(survey_instance.id),
|
|
'notification_log_id': str(notification_log.id)
|
|
}
|
|
|
|
except PatientJourneyStageInstance.DoesNotExist:
|
|
error_msg = f"Stage instance {stage_instance_id} not found"
|
|
logger.error(error_msg)
|
|
return {'status': 'error', 'reason': error_msg}
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error creating/sending survey: {str(e)}"
|
|
logger.error(error_msg, exc_info=True)
|
|
|
|
# Retry the task
|
|
raise self.retry(exc=e, countdown=60 * (self.request.retries + 1))
|
|
|
|
|
|
@shared_task
|
|
def mark_abandoned_surveys(hours=24):
|
|
"""
|
|
Mark surveys as abandoned if not completed within specified time.
|
|
|
|
This task runs periodically to check for surveys that have been opened
|
|
or started but not completed. It marks them as 'abandoned' status.
|
|
|
|
Args:
|
|
hours: Hours after which to mark survey as abandoned (default: 24)
|
|
|
|
Returns:
|
|
dict: Result with count of surveys marked as abandoned
|
|
"""
|
|
from django.conf import settings
|
|
from apps.surveys.models import SurveyInstance, SurveyTracking
|
|
from datetime import timedelta
|
|
|
|
try:
|
|
# Get hours from settings if not provided
|
|
if hours is None:
|
|
hours = getattr(settings, 'SURVEY_ABANDONMENT_HOURS', 24)
|
|
|
|
logger.info(f"Checking for abandoned surveys (cutoff: {hours} hours)")
|
|
|
|
# Calculate cutoff time
|
|
cutoff_time = timezone.now() - timedelta(hours=hours)
|
|
|
|
# Find surveys that should be marked as abandoned
|
|
surveys_to_abandon = SurveyInstance.objects.filter(
|
|
status__in=['viewed', 'in_progress'],
|
|
token_expires_at__gt=timezone.now(),
|
|
last_opened_at__lt=cutoff_time
|
|
).select_related('survey_template', 'patient')
|
|
|
|
count = surveys_to_abandon.count()
|
|
|
|
if count == 0:
|
|
logger.info('No surveys to mark as abandoned')
|
|
return {'status': 'completed', 'marked': 0}
|
|
|
|
logger.info(f"Marking {count} surveys as abandoned")
|
|
|
|
# Mark surveys as abandoned
|
|
for survey in surveys_to_abandon:
|
|
time_since_open = timezone.now() - survey.last_opened_at
|
|
|
|
# Update status
|
|
survey.status = 'abandoned'
|
|
survey.save(update_fields=['status'])
|
|
|
|
# Get question count for this survey
|
|
tracking_events = survey.tracking_events.filter(
|
|
event_type='question_answered'
|
|
)
|
|
|
|
# Track abandonment event
|
|
SurveyTracking.objects.create(
|
|
survey_instance=survey,
|
|
event_type='survey_abandoned',
|
|
current_question=tracking_events.count(),
|
|
total_time_spent=survey.time_spent_seconds or 0,
|
|
metadata={
|
|
'time_since_open_hours': round(time_since_open.total_seconds() / 3600, 2),
|
|
'questions_answered': tracking_events.count(),
|
|
'original_status': survey.status,
|
|
}
|
|
)
|
|
|
|
logger.info(f"Successfully marked {count} surveys as abandoned")
|
|
|
|
return {
|
|
'status': 'completed',
|
|
'marked': count,
|
|
'hours_cutoff': hours
|
|
}
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error marking abandoned surveys: {str(e)}"
|
|
logger.error(error_msg, exc_info=True)
|
|
return {'status': 'error', 'reason': error_msg}
|
|
|
|
|
|
@shared_task
|
|
def send_survey_reminder(survey_instance_id):
|
|
"""
|
|
Send reminder for incomplete survey.
|
|
|
|
Args:
|
|
survey_instance_id: UUID of SurveyInstance
|
|
|
|
Returns:
|
|
dict: Result with delivery status
|
|
"""
|
|
from apps.notifications.services import NotificationService
|
|
from apps.surveys.models import SurveyInstance
|
|
|
|
try:
|
|
survey_instance = SurveyInstance.objects.select_related(
|
|
'patient', 'survey_template'
|
|
).get(id=survey_instance_id)
|
|
|
|
# Only send reminder if survey is still pending/active
|
|
if survey_instance.status not in ['pending', 'active']:
|
|
return {'status': 'skipped', 'reason': f'survey_status_{survey_instance.status}'}
|
|
|
|
# Send reminder
|
|
patient = survey_instance.patient
|
|
language = patient.language if hasattr(patient, 'language') else 'en'
|
|
|
|
notification_log = NotificationService.send_survey_invitation(
|
|
survey_instance=survey_instance,
|
|
language=language
|
|
)
|
|
|
|
logger.info(f"Survey reminder sent for {survey_instance.id}")
|
|
|
|
return {
|
|
'status': 'sent',
|
|
'notification_log_id': str(notification_log.id)
|
|
}
|
|
|
|
except SurveyInstance.DoesNotExist:
|
|
error_msg = f"Survey instance {survey_instance_id} not found"
|
|
logger.error(error_msg)
|
|
return {'status': 'error', 'reason': error_msg}
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error sending survey reminder: {str(e)}"
|
|
logger.error(error_msg, exc_info=True)
|
|
return {'status': 'error', 'reason': error_msg}
|
|
|
|
|
|
@shared_task
|
|
def process_survey_completion(survey_instance_id):
|
|
"""
|
|
Process completed survey.
|
|
|
|
This task:
|
|
1. Calculates the survey score
|
|
2. Checks if score is negative
|
|
3. Creates PXAction if negative (Phase 6)
|
|
|
|
Args:
|
|
survey_instance_id: UUID of SurveyInstance
|
|
|
|
Returns:
|
|
dict: Result with score and action status
|
|
"""
|
|
from apps.core.services import create_audit_log
|
|
from apps.surveys.models import SurveyInstance
|
|
|
|
try:
|
|
survey_instance = SurveyInstance.objects.select_related(
|
|
'survey_template', 'patient'
|
|
).get(id=survey_instance_id)
|
|
|
|
# Calculate score
|
|
score = survey_instance.calculate_score()
|
|
|
|
# Log completion
|
|
create_audit_log(
|
|
event_type='survey_completed',
|
|
description=f"Survey completed by {survey_instance.patient.get_full_name()} with score {score}",
|
|
content_object=survey_instance,
|
|
metadata={
|
|
'score': float(score) if score else None,
|
|
'is_negative': survey_instance.is_negative,
|
|
'survey_template': survey_instance.survey_template.name
|
|
}
|
|
)
|
|
|
|
logger.info(
|
|
f"Survey {survey_instance.id} completed with score {score} "
|
|
f"(negative: {survey_instance.is_negative})"
|
|
)
|
|
|
|
# If negative, create PXAction
|
|
if survey_instance.is_negative:
|
|
logger.info(f"Negative survey detected - creating PXAction")
|
|
|
|
# Check if it's a complaint resolution survey
|
|
if survey_instance.survey_template.survey_type == 'complaint_resolution':
|
|
from apps.px_action_center.tasks import create_action_from_complaint_resolution
|
|
create_action_from_complaint_resolution.delay(str(survey_instance.id))
|
|
else:
|
|
# Regular survey
|
|
from apps.px_action_center.tasks import create_action_from_survey
|
|
create_action_from_survey.delay(str(survey_instance.id))
|
|
|
|
return {
|
|
'status': 'processed',
|
|
'score': float(score) if score else None,
|
|
'is_negative': survey_instance.is_negative
|
|
}
|
|
|
|
except SurveyInstance.DoesNotExist:
|
|
error_msg = f"Survey instance {survey_instance_id} not found"
|
|
logger.error(error_msg)
|
|
return {'status': 'error', 'reason': error_msg}
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error processing survey completion: {str(e)}"
|
|
logger.error(error_msg, exc_info=True)
|
|
return {'status': 'error', 'reason': error_msg}
|
|
|
|
|
|
@shared_task(bind=True, max_retries=3)
|
|
def create_post_discharge_survey(self, journey_instance_id):
|
|
"""
|
|
Create comprehensive post-discharge survey by merging questions from completed stages.
|
|
|
|
This task is triggered after patient discharge:
|
|
1. Gets all completed stages in the journey
|
|
2. Merges questions from each stage's survey_template
|
|
3. Creates a single comprehensive survey instance
|
|
4. Sends survey invitation to patient
|
|
|
|
Args:
|
|
journey_instance_id: UUID of PatientJourneyInstance
|
|
|
|
Returns:
|
|
dict: Result with survey_instance_id and delivery status
|
|
"""
|
|
from apps.core.services import create_audit_log
|
|
from apps.journeys.models import PatientJourneyInstance, StageStatus
|
|
from apps.notifications.services import NotificationService
|
|
from apps.surveys.models import SurveyInstance, SurveyQuestion, SurveyTemplate
|
|
|
|
try:
|
|
# Get journey instance
|
|
journey_instance = PatientJourneyInstance.objects.select_related(
|
|
'journey_template',
|
|
'patient',
|
|
'hospital'
|
|
).prefetch_related(
|
|
'stage_instances__stage_template__survey_template__questions'
|
|
).get(id=journey_instance_id)
|
|
|
|
logger.info(f"Creating post-discharge survey for journey {journey_instance_id}")
|
|
|
|
# Get all completed stages
|
|
completed_stages = journey_instance.stage_instances.filter(
|
|
status=StageStatus.COMPLETED
|
|
).select_related('stage_template__survey_template').order_by('stage_template__order')
|
|
|
|
if not completed_stages.exists():
|
|
logger.warning(f"No completed stages for journey {journey_instance_id}")
|
|
return {'status': 'skipped', 'reason': 'no_completed_stages'}
|
|
|
|
# Collect survey templates from completed stages
|
|
survey_templates = []
|
|
for stage_instance in completed_stages:
|
|
if stage_instance.stage_template.survey_template:
|
|
survey_templates.append({
|
|
'stage': stage_instance.stage_template,
|
|
'survey_template': stage_instance.stage_template.survey_template
|
|
})
|
|
logger.info(
|
|
f"Including questions from stage: {stage_instance.stage_template.name} "
|
|
f"(template: {stage_instance.stage_template.survey_template.name})"
|
|
)
|
|
|
|
if not survey_templates:
|
|
logger.warning(f"No survey templates found for completed stages in journey {journey_instance_id}")
|
|
return {'status': 'skipped', 'reason': 'no_survey_templates'}
|
|
|
|
# Create comprehensive survey template on-the-fly
|
|
from django.utils import timezone
|
|
import uuid
|
|
|
|
# Generate a unique name for this comprehensive survey
|
|
survey_name = f"Post-Discharge Survey - {journey_instance.patient.get_full_name()} - {journey_instance.encounter_id}"
|
|
survey_code = f"POST_DISCHARGE_{uuid.uuid4().hex[:8].upper()}"
|
|
|
|
# Create the survey template
|
|
comprehensive_template = SurveyTemplate.objects.create(
|
|
name=survey_name,
|
|
name_ar=f"استبيان ما بعد الخروج - {journey_instance.patient.get_full_name()}",
|
|
code=survey_code,
|
|
survey_type='general', # Use 'general' instead of SurveyType.GENERAL_FEEDBACK
|
|
hospital=journey_instance.hospital,
|
|
description=f"Comprehensive post-discharge survey for encounter {journey_instance.encounter_id}",
|
|
scoring_method='average',
|
|
negative_threshold=3.0,
|
|
is_active=True,
|
|
metadata={
|
|
'is_post_discharge_comprehensive': True,
|
|
'journey_instance_id': str(journey_instance.id),
|
|
'encounter_id': journey_instance.encounter_id,
|
|
'stages_count': len(survey_templates)
|
|
}
|
|
)
|
|
|
|
# Merge questions from all stage survey templates
|
|
question_order = 0
|
|
for stage_info in survey_templates:
|
|
stage_template = stage_info['stage']
|
|
stage_survey_template = stage_info['survey_template']
|
|
|
|
# Add section header for this stage
|
|
SurveyQuestion.objects.create(
|
|
survey_template=comprehensive_template,
|
|
text=f"--- {stage_template.name} ---",
|
|
text_ar=f"--- {stage_template.name_ar or stage_template.name} ---",
|
|
question_type='section_header',
|
|
order=question_order,
|
|
is_required=False,
|
|
weight=0,
|
|
metadata={'is_section_header': True, 'stage_name': stage_template.name}
|
|
)
|
|
question_order += 1
|
|
|
|
# Add all questions from this stage's template
|
|
for original_question in stage_survey_template.questions.filter(is_active=True).order_by('order'):
|
|
# Create a copy of the question
|
|
SurveyQuestion.objects.create(
|
|
survey_template=comprehensive_template,
|
|
text=original_question.text, # Use 'text' instead of 'question'
|
|
text_ar=original_question.text_ar, # Use 'text_ar' instead of 'question_ar'
|
|
question_type=original_question.question_type,
|
|
order=question_order,
|
|
is_required=original_question.is_required,
|
|
weight=original_question.weight,
|
|
choices_json=original_question.choices_json, # Use 'choices_json' instead of 'choices'
|
|
branch_logic=original_question.branch_logic,
|
|
metadata={
|
|
'original_question_id': str(original_question.id),
|
|
'original_stage': stage_template.name,
|
|
'original_survey_template': stage_survey_template.name
|
|
}
|
|
)
|
|
question_order += 1
|
|
|
|
logger.info(f"Added {stage_survey_template.questions.filter(is_active=True).count()} questions from {stage_template.name}")
|
|
|
|
logger.info(f"Created comprehensive survey template {comprehensive_template.id} with {question_order} items")
|
|
|
|
# Determine delivery channel and recipient
|
|
delivery_channel = 'sms' # Default
|
|
recipient_phone = journey_instance.patient.phone
|
|
recipient_email = journey_instance.patient.email
|
|
|
|
# Create survey instance
|
|
with transaction.atomic():
|
|
survey_instance = SurveyInstance.objects.create(
|
|
survey_template=comprehensive_template,
|
|
patient=journey_instance.patient,
|
|
journey_instance=journey_instance,
|
|
encounter_id=journey_instance.encounter_id,
|
|
delivery_channel=delivery_channel,
|
|
recipient_phone=recipient_phone,
|
|
recipient_email=recipient_email,
|
|
status='pending'
|
|
)
|
|
|
|
# Send survey invitation
|
|
notification_log = NotificationService.send_survey_invitation(
|
|
survey_instance=survey_instance,
|
|
language=journey_instance.patient.language if hasattr(journey_instance.patient, 'language') else 'en'
|
|
)
|
|
|
|
# Update survey instance status
|
|
survey_instance.status = 'active'
|
|
survey_instance.sent_at = timezone.now()
|
|
survey_instance.save(update_fields=['status', 'sent_at'])
|
|
|
|
# Log audit event
|
|
create_audit_log(
|
|
event_type='post_discharge_survey_sent',
|
|
description=f"Post-discharge survey sent to {journey_instance.patient.get_full_name()} for encounter {journey_instance.encounter_id}",
|
|
content_object=survey_instance,
|
|
metadata={
|
|
'survey_template': comprehensive_template.name,
|
|
'journey_instance': str(journey_instance.id),
|
|
'encounter_id': journey_instance.encounter_id,
|
|
'stages_included': len(survey_templates),
|
|
'total_questions': question_order,
|
|
'channel': delivery_channel
|
|
}
|
|
)
|
|
|
|
logger.info(
|
|
f"Post-discharge survey created and sent: {survey_instance.id} to "
|
|
f"{journey_instance.patient.get_full_name()} via {delivery_channel} "
|
|
f"({len(survey_templates)} stages, {question_order} questions)"
|
|
)
|
|
|
|
return {
|
|
'status': 'sent',
|
|
'survey_instance_id': str(survey_instance.id),
|
|
'survey_template_id': str(comprehensive_template.id),
|
|
'notification_log_id': str(notification_log.id),
|
|
'stages_included': len(survey_templates),
|
|
'total_questions': question_order
|
|
}
|
|
|
|
except PatientJourneyInstance.DoesNotExist:
|
|
error_msg = f"Journey instance {journey_instance_id} not found"
|
|
logger.error(error_msg)
|
|
return {'status': 'error', 'reason': error_msg}
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error creating post-discharge survey: {str(e)}"
|
|
logger.error(error_msg, exc_info=True)
|
|
|
|
# Retry the task
|
|
raise self.retry(exc=e, countdown=60 * (self.request.retries + 1))
|
|
|
|
|
|
@shared_task(bind=True, max_retries=3)
|
|
def send_satisfaction_feedback(self, survey_instance_id, user_id=None):
|
|
"""
|
|
Send satisfaction feedback form to patient after negative survey contact.
|
|
|
|
This creates a feedback form linked to the survey and sends it to the patient
|
|
to assess their satisfaction with the resolution.
|
|
|
|
Args:
|
|
survey_instance_id: UUID of SurveyInstance
|
|
user_id: UUID of User who initiated the feedback (optional)
|
|
|
|
Returns:
|
|
dict: Result with feedback_id and delivery status
|
|
"""
|
|
from apps.core.services import create_audit_log
|
|
from apps.feedback.models import Feedback, FeedbackType, FeedbackResponse
|
|
from apps.notifications.services import NotificationService
|
|
from apps.surveys.models import SurveyInstance
|
|
|
|
try:
|
|
# Get survey instance
|
|
survey_instance = SurveyInstance.objects.select_related(
|
|
'patient', 'survey_template', 'journey_instance__hospital'
|
|
).get(id=survey_instance_id)
|
|
|
|
# Check if feedback already sent
|
|
if survey_instance.satisfaction_feedback_sent:
|
|
logger.warning(f"Satisfaction feedback already sent for survey {survey_instance_id}")
|
|
return {'status': 'skipped', 'reason': 'already_sent'}
|
|
|
|
# Check if patient was contacted
|
|
if not survey_instance.patient_contacted:
|
|
logger.warning(f"Patient not contacted yet for survey {survey_instance_id}")
|
|
return {'status': 'skipped', 'reason': 'patient_not_contacted'}
|
|
|
|
patient = survey_instance.patient
|
|
hospital = survey_instance.journey_instance.hospital if survey_instance.journey_instance else patient.primary_hospital
|
|
|
|
# Create satisfaction feedback
|
|
with transaction.atomic():
|
|
feedback = Feedback.objects.create(
|
|
patient=patient,
|
|
hospital=hospital,
|
|
department=None, # Department not directly linked to survey instance
|
|
feedback_type=FeedbackType.SATISFACTION_CHECK,
|
|
title=f"Satisfaction Check - {survey_instance.survey_template.name}",
|
|
message=f"Please rate your satisfaction with how we addressed your concerns regarding the survey.",
|
|
category='communication',
|
|
priority='medium',
|
|
sentiment='neutral',
|
|
status='submitted',
|
|
related_survey=survey_instance,
|
|
encounter_id=survey_instance.encounter_id,
|
|
source='system',
|
|
metadata={
|
|
'survey_id': str(survey_instance.id),
|
|
'survey_score': float(survey_instance.total_score) if survey_instance.total_score else None,
|
|
'auto_generated': True
|
|
}
|
|
)
|
|
|
|
# Create initial response
|
|
FeedbackResponse.objects.create(
|
|
feedback=feedback,
|
|
response_type='note',
|
|
message=f"Satisfaction feedback automatically created following negative survey (Score: {survey_instance.total_score})",
|
|
created_by_id=user_id,
|
|
is_internal=True
|
|
)
|
|
|
|
# Update survey instance
|
|
survey_instance.satisfaction_feedback_sent = True
|
|
survey_instance.satisfaction_feedback_sent_at = timezone.now()
|
|
survey_instance.save(update_fields=['satisfaction_feedback_sent', 'satisfaction_feedback_sent_at'])
|
|
|
|
# Send notification to patient
|
|
# TODO: Implement feedback form link notification
|
|
# For now, we'll log it
|
|
logger.info(f"Satisfaction feedback {feedback.id} created for survey {survey_instance.id}")
|
|
|
|
# Log audit event
|
|
create_audit_log(
|
|
event_type='satisfaction_feedback_sent',
|
|
description=f"Satisfaction feedback sent to {patient.get_full_name()} for survey {survey_instance.survey_template.name}",
|
|
content_object=feedback,
|
|
metadata={
|
|
'survey_id': str(survey_instance.id),
|
|
'survey_score': float(survey_instance.total_score) if survey_instance.total_score else None,
|
|
'feedback_id': str(feedback.id)
|
|
}
|
|
)
|
|
|
|
return {
|
|
'status': 'sent',
|
|
'feedback_id': str(feedback.id),
|
|
'survey_id': str(survey_instance.id)
|
|
}
|
|
|
|
except SurveyInstance.DoesNotExist:
|
|
error_msg = f"Survey instance {survey_instance_id} not found"
|
|
logger.error(error_msg)
|
|
return {'status': 'error', 'reason': error_msg}
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error sending satisfaction feedback: {str(e)}"
|
|
logger.error(error_msg, exc_info=True)
|
|
|
|
# Retry the task
|
|
raise self.retry(exc=e, countdown=60 * (self.request.retries + 1))
|