From 4dd3c3e5057ce04e2c50d35183fb187df944217b Mon Sep 17 00:00:00 2001 From: Faheed Date: Tue, 13 Jan 2026 18:05:54 +0300 Subject: [PATCH] standards app done --- apps/accounts/admin.py | 4 +- apps/accounts/migrations/0001_initial.py | 2 +- apps/accounts/migrations/0002_initial.py | 2 +- apps/ai_engine/migrations/0001_initial.py | 2 +- apps/analytics/migrations/0001_initial.py | 2 +- apps/appreciation/migrations/0001_initial.py | 2 +- apps/callcenter/migrations/0001_initial.py | 2 +- apps/complaints/admin.py | 19 ++- apps/complaints/migrations/0001_initial.py | 24 +--- apps/complaints/migrations/0002_initial.py | 2 +- apps/complaints/migrations/0003_initial.py | 16 +-- apps/complaints/models.py | 13 +-- apps/complaints/ui_views.py | 13 ++- apps/core/migrations/0001_initial.py | 2 +- apps/feedback/migrations/0002_initial.py | 2 +- apps/feedback/migrations/0003_initial.py | 2 +- apps/integrations/migrations/0001_initial.py | 2 +- apps/journeys/migrations/0001_initial.py | 2 +- apps/journeys/migrations/0002_initial.py | 2 +- ...stagetemplate_auto_send_survey_and_more.py | 38 ++++++ ...ageinstance_completed_by_event_and_more.py | 37 ++++++ apps/notifications/migrations/0001_initial.py | 2 +- apps/observations/migrations/0001_initial.py | 2 +- apps/organizations/migrations/0001_initial.py | 3 +- apps/physicians/migrations/0001_initial.py | 2 +- apps/projects/migrations/0001_initial.py | 2 +- apps/projects/migrations/0002_initial.py | 2 +- apps/references/migrations/0001_initial.py | 2 +- apps/surveys/migrations/0001_initial.py | 2 +- ...nstance_journey_stage_instance_and_more.py | 57 +++++++++ .../migrations/0003_add_survey_tracking.py | 96 +++++++++++++++ ...4_alter_surveytracking_options_and_more.py | 110 ++++++++++++++++++ templates/complaints/complaint_form.html | 1 + templates/complaints/inquiry_form.html | 9 ++ 34 files changed, 398 insertions(+), 82 deletions(-) create mode 100644 apps/journeys/migrations/0003_remove_patientjourneystagetemplate_auto_send_survey_and_more.py create mode 100644 apps/journeys/migrations/0004_remove_patientjourneystageinstance_completed_by_event_and_more.py create mode 100644 apps/surveys/migrations/0002_remove_surveyinstance_journey_stage_instance_and_more.py create mode 100644 apps/surveys/migrations/0003_add_survey_tracking.py create mode 100644 apps/surveys/migrations/0004_alter_surveytracking_options_and_more.py diff --git a/apps/accounts/admin.py b/apps/accounts/admin.py index 805ea35..5ea22dd 100644 --- a/apps/accounts/admin.py +++ b/apps/accounts/admin.py @@ -35,8 +35,8 @@ class UserAdmin(BaseUserAdmin): ordering = ['-date_joined'] fieldsets = ( - (None, {'fields': ('email', 'username', 'password')}), - (_('Personal info'), {'fields': ('first_name', 'last_name', 'phone', 'employee_id')}), + (None, {'fields': ('email', 'password')}), + (_('Personal info'), {'fields': ('first_name', 'last_name', 'username', 'phone', 'employee_id')}), (_('Organization'), {'fields': ('hospital', 'department')}), (_('Profile'), {'fields': ('avatar', 'bio', 'language')}), (_('Permissions'), { diff --git a/apps/accounts/migrations/0001_initial.py b/apps/accounts/migrations/0001_initial.py index afc5d28..67f5b30 100644 --- a/apps/accounts/migrations/0001_initial.py +++ b/apps/accounts/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.utils.timezone import uuid diff --git a/apps/accounts/migrations/0002_initial.py b/apps/accounts/migrations/0002_initial.py index e6176ad..3d923a7 100644 --- a/apps/accounts/migrations/0002_initial.py +++ b/apps/accounts/migrations/0002_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion from django.conf import settings diff --git a/apps/ai_engine/migrations/0001_initial.py b/apps/ai_engine/migrations/0001_initial.py index dc2cb65..3f4e143 100644 --- a/apps/ai_engine/migrations/0001_initial.py +++ b/apps/ai_engine/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/analytics/migrations/0001_initial.py b/apps/analytics/migrations/0001_initial.py index 4d630ae..66e3d3d 100644 --- a/apps/analytics/migrations/0001_initial.py +++ b/apps/analytics/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/appreciation/migrations/0001_initial.py b/apps/appreciation/migrations/0001_initial.py index 42d7b59..572d681 100644 --- a/apps/appreciation/migrations/0001_initial.py +++ b/apps/appreciation/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/callcenter/migrations/0001_initial.py b/apps/callcenter/migrations/0001_initial.py index 22f88e1..27f9002 100644 --- a/apps/callcenter/migrations/0001_initial.py +++ b/apps/callcenter/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/complaints/admin.py b/apps/complaints/admin.py index df62c09..77c035c 100644 --- a/apps/complaints/admin.py +++ b/apps/complaints/admin.py @@ -39,11 +39,11 @@ class ComplaintAdmin(admin.ModelAdmin): list_display = [ 'title_preview', 'patient', 'hospital', 'category', 'severity_badge', 'status_badge', 'sla_indicator', - 'assigned_to', 'created_at' + 'created_by', 'assigned_to', 'created_at' ] list_filter = [ 'status', 'severity', 'priority', 'category', 'source', - 'is_overdue', 'hospital', 'created_at' + 'is_overdue', 'hospital', 'created_by', 'created_at' ] search_fields = [ 'title', 'description', 'patient__mrn', @@ -66,6 +66,9 @@ class ComplaintAdmin(admin.ModelAdmin): ('Classification', { 'fields': ('priority', 'severity', 'source') }), + ('Creator Tracking', { + 'fields': ('created_by',) + }), ('Status & Assignment', { 'fields': ('status', 'assigned_to', 'assigned_at') }), @@ -94,7 +97,8 @@ class ComplaintAdmin(admin.ModelAdmin): qs = super().get_queryset(request) return qs.select_related( 'patient', 'hospital', 'department', 'staff', - 'assigned_to', 'resolved_by', 'closed_by', 'resolution_survey' + 'assigned_to', 'resolved_by', 'closed_by', 'resolution_survey', + 'created_by' ) def title_preview(self, obj): @@ -219,9 +223,9 @@ class InquiryAdmin(admin.ModelAdmin): """Inquiry admin""" list_display = [ 'subject_preview', 'patient', 'contact_name', - 'hospital', 'category', 'status', 'assigned_to', 'created_at' + 'hospital', 'category', 'status', 'created_by', 'assigned_to', 'created_at' ] - list_filter = ['status', 'category', 'source', 'hospital', 'created_at'] + list_filter = ['status', 'category', 'source', 'hospital', 'created_by', 'created_at'] search_fields = [ 'subject', 'message', 'contact_name', 'contact_phone', 'patient__mrn', 'patient__first_name', 'patient__last_name' @@ -242,6 +246,9 @@ class InquiryAdmin(admin.ModelAdmin): ('Inquiry Details', { 'fields': ('subject', 'message', 'category', 'source') }), + ('Creator Tracking', { + 'fields': ('created_by',) + }), ('Status & Assignment', { 'fields': ('status', 'assigned_to') }), @@ -259,7 +266,7 @@ class InquiryAdmin(admin.ModelAdmin): qs = super().get_queryset(request) return qs.select_related( 'patient', 'hospital', 'department', - 'assigned_to', 'responded_by' + 'assigned_to', 'responded_by', 'created_by' ) def subject_preview(self, obj): diff --git a/apps/complaints/migrations/0001_initial.py b/apps/complaints/migrations/0001_initial.py index 6d19799..d856b47 100644 --- a/apps/complaints/migrations/0001_initial.py +++ b/apps/complaints/migrations/0001_initial.py @@ -1,4 +1,5 @@ # Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid @@ -64,10 +65,6 @@ class Migration(migrations.Migration): ('email_sent_at', models.DateTimeField(blank=True, help_text='When the explanation request email was sent', null=True)), ('responded_at', models.DateTimeField(blank=True, help_text='When the explanation was submitted', null=True)), ('request_message', models.TextField(blank=True, help_text='Optional message sent with the explanation request')), - ('sla_due_at', models.DateTimeField(blank=True, db_index=True, help_text='SLA deadline for staff to submit explanation', null=True)), - ('is_overdue', models.BooleanField(db_index=True, default=False, help_text='Explanation request is overdue')), - ('reminder_sent_at', models.DateTimeField(blank=True, help_text='Reminder sent to staff about overdue explanation', null=True)), - ('escalated_at', models.DateTimeField(blank=True, help_text='When explanation was escalated to manager', null=True)), ], options={ 'verbose_name': 'Complaint Explanation', @@ -168,25 +165,6 @@ class Migration(migrations.Migration): 'ordering': ['-created_at'], }, ), - migrations.CreateModel( - name='ExplanationSLAConfig', - fields=[ - ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('response_hours', models.IntegerField(default=48, help_text='Hours staff has to submit explanation')), - ('reminder_hours_before', models.IntegerField(default=12, help_text='Send reminder X hours before deadline')), - ('auto_escalate_enabled', models.BooleanField(default=True, help_text='Automatically escalate to manager if no response')), - ('escalation_hours_overdue', models.IntegerField(default=0, help_text='Escalate X hours after overdue (0 = immediately)')), - ('max_escalation_levels', models.IntegerField(default=3, help_text='Maximum levels to escalate up staff hierarchy')), - ('is_active', models.BooleanField(default=True)), - ], - options={ - 'verbose_name': 'Explanation SLA Config', - 'verbose_name_plural': 'Explanation SLA Configs', - 'ordering': ['hospital'], - }, - ), migrations.CreateModel( name='Inquiry', fields=[ diff --git a/apps/complaints/migrations/0002_initial.py b/apps/complaints/migrations/0002_initial.py index 417c8c1..78f119d 100644 --- a/apps/complaints/migrations/0002_initial.py +++ b/apps/complaints/migrations/0002_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion from django.conf import settings diff --git a/apps/complaints/migrations/0003_initial.py b/apps/complaints/migrations/0003_initial.py index 39e0b26..b38faa2 100644 --- a/apps/complaints/migrations/0003_initial.py +++ b/apps/complaints/migrations/0003_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion from django.conf import settings @@ -57,11 +57,6 @@ class Migration(migrations.Migration): name='complaint', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='explanations', to='complaints.complaint'), ), - migrations.AddField( - model_name='complaintexplanation', - name='escalated_to_manager', - field=models.ForeignKey(blank=True, help_text="Escalated to this explanation (manager's explanation request)", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='escalated_from_staff', to='complaints.complaintexplanation'), - ), migrations.AddField( model_name='complaintexplanation', name='requested_by', @@ -107,11 +102,6 @@ class Migration(migrations.Migration): name='explanation', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='complaints.complaintexplanation'), ), - migrations.AddField( - model_name='explanationslaconfig', - name='hospital', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='explanation_sla_configs', to='organizations.hospital'), - ), migrations.AddField( model_name='inquiry', name='assigned_to', @@ -214,10 +204,6 @@ class Migration(migrations.Migration): model_name='escalationrule', index=models.Index(fields=['hospital', 'is_active'], name='complaints__hospita_3c8bac_idx'), ), - migrations.AddIndex( - model_name='explanationslaconfig', - index=models.Index(fields=['hospital', 'is_active'], name='complaints__hospita_fe4ec5_idx'), - ), migrations.AddIndex( model_name='inquiry', index=models.Index(fields=['status', '-created_at'], name='complaints__status_3d0678_idx'), diff --git a/apps/complaints/models.py b/apps/complaints/models.py index aee4650..faad4ff 100644 --- a/apps/complaints/models.py +++ b/apps/complaints/models.py @@ -180,6 +180,7 @@ class Complaint(UUIDModel, TimeStampedModel): blank=True, related_name='created_complaints', help_text="User who created this complaint (SourceUser or Patient)" + help_text="Source of complaint" ) # Status and workflow @@ -760,17 +761,7 @@ class Inquiry(UUIDModel, TimeStampedModel): related_name="inquiries", null=True, blank=True, - help_text="Source of inquiry", - ) - - # Creator tracking - created_by = models.ForeignKey( - 'accounts.User', - on_delete=models.SET_NULL, - null=True, - blank=True, - related_name='created_inquiries', - help_text="User who created this inquiry (SourceUser or Patient)" + help_text="Source of inquiry" ) # Status diff --git a/apps/complaints/ui_views.py b/apps/complaints/ui_views.py index 9b241c6..73f325d 100644 --- a/apps/complaints/ui_views.py +++ b/apps/complaints/ui_views.py @@ -1029,8 +1029,13 @@ def inquiry_create(request): from .models import Inquiry from .forms import InquiryForm from apps.organizations.models import Patient - - if request.method == "POST": + + # Determine base layout based on user type + from apps.px_sources.models import SourceUser + source_user = SourceUser.objects.filter(user=request.user).first() + base_layout = 'layouts/source_user_base.html' if source_user else 'layouts/base.html' + + if request.method == 'POST': try: # Get form data patient_id = request.POST.get("patient_id", None) @@ -1086,7 +1091,9 @@ def inquiry_create(request): hospitals = hospitals.filter(id=request.user.hospital.id) context = { - "hospitals": hospitals, + 'hospitals': hospitals, + 'base_layout': base_layout, + 'source_user': source_user, } return render(request, "complaints/inquiry_form.html", context) diff --git a/apps/core/migrations/0001_initial.py b/apps/core/migrations/0001_initial.py index dc15c96..7fae5e7 100644 --- a/apps/core/migrations/0001_initial.py +++ b/apps/core/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/feedback/migrations/0002_initial.py b/apps/feedback/migrations/0002_initial.py index e4c4363..7a12514 100644 --- a/apps/feedback/migrations/0002_initial.py +++ b/apps/feedback/migrations/0002_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion from django.conf import settings diff --git a/apps/feedback/migrations/0003_initial.py b/apps/feedback/migrations/0003_initial.py index 84e6b90..b86ab6a 100644 --- a/apps/feedback/migrations/0003_initial.py +++ b/apps/feedback/migrations/0003_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion from django.conf import settings diff --git a/apps/integrations/migrations/0001_initial.py b/apps/integrations/migrations/0001_initial.py index df00ae1..dc082dc 100644 --- a/apps/integrations/migrations/0001_initial.py +++ b/apps/integrations/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/journeys/migrations/0001_initial.py b/apps/journeys/migrations/0001_initial.py index aa4744d..07dedf4 100644 --- a/apps/journeys/migrations/0001_initial.py +++ b/apps/journeys/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/journeys/migrations/0002_initial.py b/apps/journeys/migrations/0002_initial.py index 4d2c3b8..fb75bfe 100644 --- a/apps/journeys/migrations/0002_initial.py +++ b/apps/journeys/migrations/0002_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion from django.db import migrations, models diff --git a/apps/journeys/migrations/0003_remove_patientjourneystagetemplate_auto_send_survey_and_more.py b/apps/journeys/migrations/0003_remove_patientjourneystagetemplate_auto_send_survey_and_more.py new file mode 100644 index 0000000..b345fb7 --- /dev/null +++ b/apps/journeys/migrations/0003_remove_patientjourneystagetemplate_auto_send_survey_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 6.0.1 on 2026-01-20 12:38 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journeys', '0002_initial'), + ('surveys', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='patientjourneystagetemplate', + name='auto_send_survey', + ), + migrations.RemoveField( + model_name='patientjourneystagetemplate', + name='survey_delay_hours', + ), + migrations.AddField( + model_name='patientjourneytemplate', + name='post_discharge_survey_delay_hours', + field=models.IntegerField(default=1, help_text='Hours after discharge to send the survey'), + ), + migrations.AddField( + model_name='patientjourneytemplate', + name='send_post_discharge_survey', + field=models.BooleanField(default=False, help_text='Send a comprehensive survey after patient discharge'), + ), + migrations.AlterField( + model_name='patientjourneystagetemplate', + name='survey_template', + field=models.ForeignKey(blank=True, help_text='Survey template containing questions for this stage (merged into post-discharge survey)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='journey_stages', to='surveys.surveytemplate'), + ), + ] diff --git a/apps/journeys/migrations/0004_remove_patientjourneystageinstance_completed_by_event_and_more.py b/apps/journeys/migrations/0004_remove_patientjourneystageinstance_completed_by_event_and_more.py new file mode 100644 index 0000000..36c3371 --- /dev/null +++ b/apps/journeys/migrations/0004_remove_patientjourneystageinstance_completed_by_event_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 6.0.1 on 2026-01-20 13:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('journeys', '0003_remove_patientjourneystagetemplate_auto_send_survey_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='patientjourneystageinstance', + name='completed_by_event', + ), + migrations.RemoveField( + model_name='patientjourneystageinstance', + name='survey_instance', + ), + migrations.RemoveField( + model_name='patientjourneystageinstance', + name='survey_sent_at', + ), + migrations.RemoveField( + model_name='patientjourneystagetemplate', + name='description', + ), + migrations.RemoveField( + model_name='patientjourneystagetemplate', + name='requires_department', + ), + migrations.RemoveField( + model_name='patientjourneystagetemplate', + name='requires_physician', + ), + ] diff --git a/apps/notifications/migrations/0001_initial.py b/apps/notifications/migrations/0001_initial.py index e671f2c..12978eb 100644 --- a/apps/notifications/migrations/0001_initial.py +++ b/apps/notifications/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/observations/migrations/0001_initial.py b/apps/observations/migrations/0001_initial.py index be05b1c..dfaacfd 100644 --- a/apps/observations/migrations/0001_initial.py +++ b/apps/observations/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import apps.observations.models import django.db.models.deletion diff --git a/apps/organizations/migrations/0001_initial.py b/apps/organizations/migrations/0001_initial.py index 9124a57..04d519d 100644 --- a/apps/organizations/migrations/0001_initial.py +++ b/apps/organizations/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid @@ -129,7 +129,6 @@ class Migration(migrations.Migration): ('license_number', models.CharField(blank=True, max_length=100, null=True, unique=True)), ('specialization', models.CharField(blank=True, max_length=200)), ('email', models.EmailField(blank=True, max_length=254)), - ('phone', models.CharField(blank=True, max_length=20, verbose_name='Phone Number')), ('employee_id', models.CharField(db_index=True, max_length=50, unique=True)), ('name', models.CharField(blank=True, max_length=300, verbose_name='Full Name (Original)')), ('country', models.CharField(blank=True, max_length=100, verbose_name='Country')), diff --git a/apps/physicians/migrations/0001_initial.py b/apps/physicians/migrations/0001_initial.py index 508393b..bba5ef5 100644 --- a/apps/physicians/migrations/0001_initial.py +++ b/apps/physicians/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/projects/migrations/0001_initial.py b/apps/projects/migrations/0001_initial.py index 22a5ff1..de3c36b 100644 --- a/apps/projects/migrations/0001_initial.py +++ b/apps/projects/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/projects/migrations/0002_initial.py b/apps/projects/migrations/0002_initial.py index f4ebeb7..2fe3f6d 100644 --- a/apps/projects/migrations/0002_initial.py +++ b/apps/projects/migrations/0002_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion from django.conf import settings diff --git a/apps/references/migrations/0001_initial.py b/apps/references/migrations/0001_initial.py index 4f84f9b..0059d7a 100644 --- a/apps/references/migrations/0001_initial.py +++ b/apps/references/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import apps.references.models import django.db.models.deletion diff --git a/apps/surveys/migrations/0001_initial.py b/apps/surveys/migrations/0001_initial.py index f1b054c..96193fb 100644 --- a/apps/surveys/migrations/0001_initial.py +++ b/apps/surveys/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.1 on 2026-01-15 12:02 +# Generated by Django 6.0 on 2026-01-12 09:50 import django.db.models.deletion import uuid diff --git a/apps/surveys/migrations/0002_remove_surveyinstance_journey_stage_instance_and_more.py b/apps/surveys/migrations/0002_remove_surveyinstance_journey_stage_instance_and_more.py new file mode 100644 index 0000000..f2c4f54 --- /dev/null +++ b/apps/surveys/migrations/0002_remove_surveyinstance_journey_stage_instance_and_more.py @@ -0,0 +1,57 @@ +# Generated by Django 6.0.1 on 2026-01-20 13:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('surveys', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='surveyinstance', + name='journey_stage_instance', + ), + migrations.RemoveField( + model_name='surveyinstance', + name='satisfaction_feedback_sent', + ), + migrations.RemoveField( + model_name='surveyinstance', + name='satisfaction_feedback_sent_at', + ), + migrations.RemoveField( + model_name='surveyquestion', + name='branch_logic', + ), + migrations.RemoveField( + model_name='surveyquestion', + name='help_text', + ), + migrations.RemoveField( + model_name='surveyquestion', + name='help_text_ar', + ), + migrations.RemoveField( + model_name='surveyquestion', + name='weight', + ), + migrations.RemoveField( + model_name='surveyresponse', + name='response_time_seconds', + ), + migrations.RemoveField( + model_name='surveytemplate', + name='description', + ), + migrations.RemoveField( + model_name='surveytemplate', + name='description_ar', + ), + migrations.RemoveField( + model_name='surveytemplate', + name='version', + ), + ] diff --git a/apps/surveys/migrations/0003_add_survey_tracking.py b/apps/surveys/migrations/0003_add_survey_tracking.py new file mode 100644 index 0000000..2bed7bc --- /dev/null +++ b/apps/surveys/migrations/0003_add_survey_tracking.py @@ -0,0 +1,96 @@ +# Generated migration for survey tracking features + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ('surveys', '0002_remove_surveyinstance_journey_stage_instance_and_more'), + ] + + operations = [ + # Add tracking fields to SurveyInstance + migrations.AddField( + model_name='surveyinstance', + name='open_count', + field=models.PositiveIntegerField(default=0, help_text='Number of times the survey link was opened'), + ), + migrations.AddField( + model_name='surveyinstance', + name='last_opened_at', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='surveyinstance', + name='time_spent_seconds', + field=models.PositiveIntegerField(default=0, help_text='Total time spent on survey in seconds'), + ), + + # Update status field choices + migrations.AlterField( + model_name='surveyinstance', + name='status', + field=models.CharField( + choices=[ + ('sent', 'Sent'), + ('viewed', 'Viewed'), + ('in_progress', 'In Progress'), + ('completed', 'Completed'), + ('abandoned', 'Abandoned'), + ('expired', 'Expired'), + ('cancelled', 'Cancelled'), + ], + default='sent', + max_length=20, + help_text='Current status of the survey instance' + ), + ), + + # Create SurveyTracking model + migrations.CreateModel( + name='SurveyTracking', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('event_type', models.CharField( + choices=[ + ('page_view', 'Page View'), + ('survey_started', 'Survey Started'), + ('question_answered', 'Question Answered'), + ('survey_completed', 'Survey Completed'), + ('survey_abandoned', 'Survey Abandoned'), + ('reminder_sent', 'Reminder Sent'), + ], + default='page_view', + max_length=20, + help_text='Type of tracking event' + )), + ('time_on_page', models.PositiveIntegerField(blank=True, null=True, help_text='Time spent on current page in seconds')), + ('total_time_spent', models.PositiveIntegerField(default=0, help_text='Total time spent in survey in seconds')), + ('current_question', models.PositiveIntegerField(blank=True, null=True, help_text='Current question number being viewed')), + ('user_agent', models.TextField(blank=True, help_text='Browser user agent string')), + ('ip_address', models.GenericIPAddressField(blank=True, null=True)), + ('device_type', models.CharField(blank=True, max_length=50)), + ('browser', models.CharField(blank=True, max_length=50)), + ('country', models.CharField(blank=True, max_length=100)), + ('city', models.CharField(blank=True, max_length=100)), + ('metadata', models.JSONField(blank=True, null=True, default=dict, help_text='Additional tracking metadata')), + ('survey_instance', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='tracking_events', + to='surveys.surveyinstance' + )), + ], + options={ + 'verbose_name': 'Survey Tracking', + 'verbose_name_plural': 'Survey Tracking Events', + 'ordering': ['-created_at'], + 'indexes': [ + models.Index(fields=['survey_instance', '-created_at'], name='idx_survey_instance_created'), + models.Index(fields=['event_type', '-created_at'], name='idx_event_type_created'), + models.Index(fields=['ip_address'], name='idx_ip_address'), + ], + }, + ), + ] diff --git a/apps/surveys/migrations/0004_alter_surveytracking_options_and_more.py b/apps/surveys/migrations/0004_alter_surveytracking_options_and_more.py new file mode 100644 index 0000000..2711ebb --- /dev/null +++ b/apps/surveys/migrations/0004_alter_surveytracking_options_and_more.py @@ -0,0 +1,110 @@ +# Generated by Django 6.0.1 on 2026-01-21 13:54 + +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('surveys', '0003_add_survey_tracking'), + ] + + operations = [ + migrations.AlterModelOptions( + name='surveytracking', + options={'ordering': ['survey_instance', 'created_at']}, + ), + migrations.RemoveIndex( + model_name='surveytracking', + name='idx_survey_instance_created', + ), + migrations.RemoveIndex( + model_name='surveytracking', + name='idx_ip_address', + ), + migrations.RenameIndex( + model_name='surveytracking', + new_name='surveys_sur_event_t_885d23_idx', + old_name='idx_event_type_created', + ), + migrations.AddField( + model_name='surveytracking', + name='updated_at', + field=models.DateTimeField(auto_now=True), + ), + migrations.AlterField( + model_name='surveyinstance', + name='last_opened_at', + field=models.DateTimeField(blank=True, help_text='Most recent time survey was opened', null=True), + ), + migrations.AlterField( + model_name='surveyinstance', + name='open_count', + field=models.IntegerField(default=0, help_text='Number of times survey link was opened'), + ), + migrations.AlterField( + model_name='surveyinstance', + name='status', + field=models.CharField(choices=[('sent', 'Sent (Not Opened)'), ('viewed', 'Viewed (Opened, Not Started)'), ('in_progress', 'In Progress (Started, Not Completed)'), ('completed', 'Completed'), ('abandoned', 'Abandoned (Started but Left)'), ('expired', 'Expired'), ('cancelled', 'Cancelled')], db_index=True, default='sent', max_length=20), + ), + migrations.AlterField( + model_name='surveyinstance', + name='time_spent_seconds', + field=models.IntegerField(blank=True, help_text='Total time spent on survey in seconds', null=True), + ), + migrations.AlterField( + model_name='surveytracking', + name='browser', + field=models.CharField(blank=True, max_length=100), + ), + migrations.AlterField( + model_name='surveytracking', + name='created_at', + field=models.DateTimeField(auto_now_add=True, db_index=True), + ), + migrations.AlterField( + model_name='surveytracking', + name='current_question', + field=models.IntegerField(blank=True, help_text='Question number when event occurred', null=True), + ), + migrations.AlterField( + model_name='surveytracking', + name='device_type', + field=models.CharField(blank=True, help_text='mobile, tablet, desktop', max_length=50), + ), + migrations.AlterField( + model_name='surveytracking', + name='event_type', + field=models.CharField(choices=[('page_view', 'Page View'), ('survey_started', 'Survey Started'), ('question_answered', 'Question Answered'), ('survey_abandoned', 'Survey Abandoned'), ('survey_completed', 'Survey Completed'), ('reminder_sent', 'Reminder Sent')], db_index=True, max_length=50), + ), + migrations.AlterField( + model_name='surveytracking', + name='id', + field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='surveytracking', + name='metadata', + field=models.JSONField(blank=True, default=dict), + ), + migrations.AlterField( + model_name='surveytracking', + name='time_on_page', + field=models.IntegerField(blank=True, help_text='Time spent on page in seconds', null=True), + ), + migrations.AlterField( + model_name='surveytracking', + name='total_time_spent', + field=models.IntegerField(blank=True, help_text='Total time spent on survey so far in seconds', null=True), + ), + migrations.AlterField( + model_name='surveytracking', + name='user_agent', + field=models.TextField(blank=True), + ), + migrations.AddIndex( + model_name='surveytracking', + index=models.Index(fields=['survey_instance', 'event_type', '-created_at'], name='surveys_sur_survey__9743a1_idx'), + ), + ] diff --git a/templates/complaints/complaint_form.html b/templates/complaints/complaint_form.html index bf3e1d3..5064ac6 100644 --- a/templates/complaints/complaint_form.html +++ b/templates/complaints/complaint_form.html @@ -1,4 +1,5 @@ {% extends base_layout %} +{% extends base_layout %} {% load i18n %} {% load static %} diff --git a/templates/complaints/inquiry_form.html b/templates/complaints/inquiry_form.html index ce61e37..aaf4e35 100644 --- a/templates/complaints/inquiry_form.html +++ b/templates/complaints/inquiry_form.html @@ -156,6 +156,15 @@
+ +
+ + + + {{ _("Leave empty for default")}} + +