From beba30e53254bb35b0e938d95716215f79da8239 Mon Sep 17 00:00:00 2001 From: Marwan Alwali Date: Thu, 11 Sep 2025 20:32:09 +0300 Subject: [PATCH] update --- .DS_Store | Bin 14340 -> 14340 bytes .../__pycache__/admin.cpython-312.pyc | Bin 13399 -> 16941 bytes .../__pycache__/forms.cpython-312.pyc | Bin 20506 -> 34153 bytes .../__pycache__/models.cpython-312.pyc | Bin 31961 -> 47465 bytes appointments/__pycache__/urls.cpython-312.pyc | Bin 7028 -> 8314 bytes .../__pycache__/views.cpython-312.pyc | Bin 81928 -> 99029 bytes appointments/admin.py | 153 ++- appointments/forms.py | 443 ++++++- ...tinglist_waitinglistcontactlog_and_more.py | 688 ++++++++++ ...inglistcontactlog_and_more.cpython-312.pyc | Bin 0 -> 15786 bytes appointments/models.py | 637 ++++++++++ appointments/templates/.DS_Store | Bin 6148 -> 8196 bytes appointments/templates/appointments/.DS_Store | Bin 6148 -> 8196 bytes .../partials/contact_log_list.html | 39 + .../queue/waiting_queue_form.html | 23 +- .../waiting_list/waiting_list.html | 525 ++++++++ .../waiting_list_confirm_delete.html | 76 ++ .../waiting_list/waiting_list_detail.html | 427 +++++++ .../waiting_list/waiting_list_form.html | 561 +++++++++ .../waiting_list/waitinglistentry_list.html | 299 +++++ .../waitlist_management.html | 21 +- appointments/urls.py | 9 + appointments/views.py | 353 +++++- db.sqlite3 | Bin 40984576 -> 41086976 bytes logs/hospital_management.log | 431 +++++++ operating_theatre/.DS_Store | Bin 6148 -> 6148 bytes operating_theatre/templates/.DS_Store | Bin 0 -> 8196 bytes .../templates/operating_theatre/.DS_Store | Bin 6148 -> 6148 bytes .../surgical_note_confirm_delete (1).html | 76 ++ .../others/surgical_note_confirm_delete.html | 503 ++++++++ .../others/surgical_note_detail (1).html | 649 ++++++++++ .../others/surgical_note_detail.html | 649 ++++++++++ .../others/surgical_note_form (1).html | 318 +++++ .../others/surgical_note_form.html | 745 +++++++++++ .../others/surgical_note_list (1).html | 516 ++++++++ .../others/surgical_note_list.html | 516 ++++++++ ...ical_note_template_confirm_delete (1).html | 76 ++ ...surgical_note_template_confirm_delete.html | 837 +++++++++++++ .../surgical_note_template_detail (1).html | 136 ++ .../others/surgical_note_template_detail.html | 937 ++++++++++++++ .../surgical_note_template_form (1).html | 207 +++ .../others/surgical_note_template_form.html | 1110 +++++++++++++++++ .../surgical_note_template_list (1).html | 135 ++ .../others/surgical_note_template_list.html | 947 ++++++++++++++ patients/.DS_Store | Bin 6148 -> 6148 bytes .../patients/dashboard-last one.html | 488 ++++++++ 46 files changed, 13478 insertions(+), 52 deletions(-) create mode 100644 appointments/migrations/0003_waitinglist_waitinglistcontactlog_and_more.py create mode 100644 appointments/migrations/__pycache__/0003_waitinglist_waitinglistcontactlog_and_more.cpython-312.pyc create mode 100644 appointments/templates/appointments/partials/contact_log_list.html create mode 100644 appointments/templates/appointments/waiting_list/waiting_list.html create mode 100644 appointments/templates/appointments/waiting_list/waiting_list_confirm_delete.html create mode 100644 appointments/templates/appointments/waiting_list/waiting_list_detail.html create mode 100644 appointments/templates/appointments/waiting_list/waiting_list_form.html create mode 100644 appointments/templates/appointments/waiting_list/waitinglistentry_list.html rename appointments/templates/appointments/{waitlist => waiting_list}/waitlist_management.html (95%) create mode 100644 operating_theatre/templates/.DS_Store create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_confirm_delete (1).html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_confirm_delete.html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_detail (1).html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_detail.html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_form (1).html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_form.html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_list (1).html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_list.html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_template_confirm_delete (1).html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_template_confirm_delete.html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_template_detail (1).html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_template_detail.html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_template_form (1).html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_template_form.html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_template_list (1).html create mode 100644 operating_theatre/templates/operating_theatre/others/surgical_note_template_list.html create mode 100644 patients/templates/patients/dashboard-last one.html diff --git a/.DS_Store b/.DS_Store index a9edf1ea6302a6eefe4deaf003a2bd94d4c43a48..ff1afbfdeb8887716407009a9cae39862526efce 100644 GIT binary patch delta 18 ZcmZoEXerpRTbYqza=ntw<{Qd7A^=9!2DShI delta 16 XcmZoEXerpRTX}MwlI-Ri$~qzdKiUSh diff --git a/appointments/__pycache__/admin.cpython-312.pyc b/appointments/__pycache__/admin.cpython-312.pyc index 647d5ed51528fb0aaeb1cefb19e51ac8d1486c44..e062354c53823416220b09a862187c786041dff6 100644 GIT binary patch delta 6562 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 CRnx@) diff --git a/appointments/__pycache__/models.cpython-312.pyc b/appointments/__pycache__/models.cpython-312.pyc index 2870cc34705c5ffa93f33547bd4c6e0cabb436b7..d253311d6cef6acd9115c1a1af375c96648cbe28 100644 GIT binary patch delta 16579 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 delta 4359 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 delta 471 zcmez6@WqVpG%qg~0}veDeJJCi;6y$NCL6Ae8dh9vQAU-~nlYPQczoHJ{WOg?{}GwU zJegCnL_G?qnh}VLm$EW2d|+l|Wc!u=o)$`4UX>NlO96wWOuYZ26f!Dlo8eUf|SN zp?Y1*`=XZjg`oT^ECnAFK~ic!sSe2ruGd9$E{f<}Fm}Jf;_*Sq&Y}l3#?Uf zf$4Q+yNk+p7hGemu*81Q21)6FrKD$sUY9kxC~I`V((4L~_Xl0u$rq$0ftJ6PmXZM} zvj8j8TH$(K$N8d;^98TiD=cvzENv$%%Ygjh4koi@R9QiaohI*xq!Aeau-Da;{^aiiiXz! diff --git a/appointments/__pycache__/views.cpython-312.pyc b/appointments/__pycache__/views.cpython-312.pyc index d66e29ff6f236f7d1a9c9411add9121160ca1607..0c64ffa1986a829559c996765b2660e3e8fd1a84 100644 GIT binary patch delta 29795 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 delta 17769 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 PH(zuV?;_(G2_gR latest_date: + raise ValidationError("Earliest acceptable date must be before latest acceptable date.") + + if preferred_date and earliest_date and preferred_date < earliest_date: + raise ValidationError("Preferred date must be within acceptable date range.") + + if preferred_date and latest_date and preferred_date > latest_date: + raise ValidationError("Preferred date must be within acceptable date range.") + + # Validate contact information + contact_method = cleaned_data.get('contact_method') + contact_phone = cleaned_data.get('contact_phone') + contact_email = cleaned_data.get('contact_email') + + if contact_method == 'PHONE' and not contact_phone: + raise ValidationError("Phone number is required when phone is selected as contact method.") + + if contact_method == 'EMAIL' and not contact_email: + raise ValidationError("Email address is required when email is selected as contact method.") + + # Validate interpreter requirements + requires_interpreter = cleaned_data.get('requires_interpreter') + interpreter_language = cleaned_data.get('interpreter_language') + + if requires_interpreter and not interpreter_language: + raise ValidationError("Interpreter language is required when interpreter services are needed.") + + return cleaned_data + + +class WaitingListContactLogForm(forms.ModelForm): + """ + Form for logging contact attempts with waiting list patients. + """ + + class Meta: + model = WaitingListContactLog + fields = [ + 'contact_method', 'contact_outcome', 'appointment_offered', + 'offered_date', 'offered_time', 'patient_response', + 'notes', 'next_contact_date' + ] + + widgets = { + 'contact_method': forms.Select(attrs={ + 'class': 'form-select', + 'required': True + }), + 'contact_outcome': forms.Select(attrs={ + 'class': 'form-select', + 'required': True + }), + 'appointment_offered': forms.CheckboxInput(attrs={ + 'class': 'form-check-input' + }), + 'offered_date': forms.DateInput(attrs={ + 'class': 'form-control', + 'type': 'date' + }), + 'offered_time': forms.TimeInput(attrs={ + 'class': 'form-control', + 'type': 'time' + }), + 'patient_response': forms.Select(attrs={ + 'class': 'form-select' + }), + 'notes': forms.Textarea(attrs={ + 'class': 'form-control', + 'rows': 4, + 'placeholder': 'Notes from contact attempt...' + }), + 'next_contact_date': forms.DateInput(attrs={ + 'class': 'form-control', + 'type': 'date', + 'min': date.today().isoformat() + }), + } + + def clean(self): + cleaned_data = super().clean() + + appointment_offered = cleaned_data.get('appointment_offered') + offered_date = cleaned_data.get('offered_date') + offered_time = cleaned_data.get('offered_time') + patient_response = cleaned_data.get('patient_response') + + if appointment_offered: + if not offered_date: + raise ValidationError("Offered date is required when appointment is offered.") + if not offered_time: + raise ValidationError("Offered time is required when appointment is offered.") + if not patient_response: + raise ValidationError("Patient response is required when appointment is offered.") + + next_contact_date = cleaned_data.get('next_contact_date') + if next_contact_date and next_contact_date < date.today(): + raise ValidationError("Next contact date cannot be in the past.") + + return cleaned_data + + +class WaitingListFilterForm(forms.Form): + """ + Form for filtering waiting list entries. + """ + + PRIORITY_CHOICES = [ + ('', 'All Priorities'), + ('EMERGENCY', 'Emergency'), + ('STAT', 'STAT'), + ('URGENT', 'Urgent'), + ('ROUTINE', 'Routine'), + ] + + STATUS_CHOICES = [ + ('', 'All Status'), + ('ACTIVE', 'Active'), + ('CONTACTED', 'Contacted'), + ('OFFERED', 'Appointment Offered'), + ('SCHEDULED', 'Scheduled'), + ('CANCELLED', 'Cancelled'), + ('EXPIRED', 'Expired'), + ] + + department = forms.ModelChoiceField( + queryset=None, + required=False, + empty_label="All Departments", + widget=forms.Select(attrs={'class': 'form-select'}) + ) + + specialty = forms.ChoiceField( + choices=[('', 'All Specialties')] + WaitingList._meta.get_field('specialty').choices, + required=False, + widget=forms.Select(attrs={'class': 'form-select'}) + ) + + priority = forms.ChoiceField( + choices=PRIORITY_CHOICES, + required=False, + widget=forms.Select(attrs={'class': 'form-select'}) + ) + + status = forms.ChoiceField( + choices=STATUS_CHOICES, + required=False, + widget=forms.Select(attrs={'class': 'form-select'}) + ) + + provider = forms.ModelChoiceField( + queryset=None, + required=False, + empty_label="All Providers", + widget=forms.Select(attrs={'class': 'form-select'}) + ) + + date_from = forms.DateField( + required=False, + widget=forms.DateInput(attrs={ + 'class': 'form-control', + 'type': 'date' + }) + ) + + date_to = forms.DateField( + required=False, + widget=forms.DateInput(attrs={ + 'class': 'form-control', + 'type': 'date' + }) + ) + + urgency_min = forms.IntegerField( + required=False, + min_value=1, + max_value=10, + widget=forms.NumberInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Min urgency (1-10)' + }) + ) + + urgency_max = forms.IntegerField( + required=False, + min_value=1, + max_value=10, + widget=forms.NumberInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Max urgency (1-10)' + }) + ) + + def __init__(self, *args, **kwargs): + tenant = kwargs.pop('tenant', None) + super().__init__(*args, **kwargs) + + if tenant: + self.fields['department'].queryset = Department.objects.filter(tenant=tenant) + self.fields['provider'].queryset = User.objects.filter( + tenant=tenant + ) + + +class WaitingListBulkActionForm(forms.Form): + """ + Form for bulk actions on waiting list entries. + """ + + ACTION_CHOICES = [ + ('', 'Select Action'), + ('contact', 'Mark as Contacted'), + ('cancel', 'Cancel Entries'), + ('update_priority', 'Update Priority'), + ('transfer_provider', 'Transfer to Provider'), + ('export', 'Export Selected'), + ] + + action = forms.ChoiceField( + choices=ACTION_CHOICES, + required=True, + widget=forms.Select(attrs={'class': 'form-select'}) + ) + + # Fields for specific actions + new_priority = forms.ChoiceField( + choices=WaitingList._meta.get_field('priority').choices, + required=False, + widget=forms.Select(attrs={'class': 'form-select'}) + ) + + transfer_provider = forms.ModelChoiceField( + queryset=None, + required=False, + widget=forms.Select(attrs={'class': 'form-select'}) + ) + + contact_notes = forms.CharField( + required=False, + widget=forms.Textarea(attrs={ + 'class': 'form-control', + 'rows': 3, + 'placeholder': 'Notes for contact action...' + }) + ) + + cancellation_reason = forms.CharField( + required=False, + widget=forms.Textarea(attrs={ + 'class': 'form-control', + 'rows': 3, + 'placeholder': 'Reason for cancellation...' + }) + ) + + def __init__(self, *args, **kwargs): + tenant = kwargs.pop('tenant', None) + super().__init__(*args, **kwargs) + + if tenant: + from django.contrib.auth import get_user_model + User = get_user_model() + + self.fields['transfer_provider'].queryset = User.objects.filter( + tenant=tenant + ) + # from django import forms # from django.core.exceptions import ValidationError # from django.utils import timezone diff --git a/appointments/migrations/0003_waitinglist_waitinglistcontactlog_and_more.py b/appointments/migrations/0003_waitinglist_waitinglistcontactlog_and_more.py new file mode 100644 index 00000000..7b3fa2ed --- /dev/null +++ b/appointments/migrations/0003_waitinglist_waitinglistcontactlog_and_more.py @@ -0,0 +1,688 @@ +# Generated by Django 5.2.6 on 2025-09-11 17:03 + +import django.core.validators +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("appointments", "0002_initial"), + ("core", "0001_initial"), + ("hr", "0001_initial"), + ("patients", "0003_remove_insuranceinfo_subscriber_ssn_and_more"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="WaitingList", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "waiting_list_id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + help_text="Unique waiting list entry identifier", + unique=True, + ), + ), + ( + "appointment_type", + models.CharField( + choices=[ + ("CONSULTATION", "Consultation"), + ("FOLLOW_UP", "Follow-up"), + ("PROCEDURE", "Procedure"), + ("SURGERY", "Surgery"), + ("DIAGNOSTIC", "Diagnostic"), + ("THERAPY", "Therapy"), + ("VACCINATION", "Vaccination"), + ("SCREENING", "Screening"), + ("EMERGENCY", "Emergency"), + ("TELEMEDICINE", "Telemedicine"), + ("OTHER", "Other"), + ], + help_text="Type of appointment requested", + max_length=50, + ), + ), + ( + "specialty", + models.CharField( + choices=[ + ("FAMILY_MEDICINE", "Family Medicine"), + ("INTERNAL_MEDICINE", "Internal Medicine"), + ("PEDIATRICS", "Pediatrics"), + ("CARDIOLOGY", "Cardiology"), + ("DERMATOLOGY", "Dermatology"), + ("ENDOCRINOLOGY", "Endocrinology"), + ("GASTROENTEROLOGY", "Gastroenterology"), + ("NEUROLOGY", "Neurology"), + ("ONCOLOGY", "Oncology"), + ("ORTHOPEDICS", "Orthopedics"), + ("PSYCHIATRY", "Psychiatry"), + ("RADIOLOGY", "Radiology"), + ("SURGERY", "Surgery"), + ("UROLOGY", "Urology"), + ("GYNECOLOGY", "Gynecology"), + ("OPHTHALMOLOGY", "Ophthalmology"), + ("ENT", "Ear, Nose & Throat"), + ("EMERGENCY", "Emergency Medicine"), + ("OTHER", "Other"), + ], + help_text="Medical specialty required", + max_length=100, + ), + ), + ( + "priority", + models.CharField( + choices=[ + ("ROUTINE", "Routine"), + ("URGENT", "Urgent"), + ("STAT", "STAT"), + ("EMERGENCY", "Emergency"), + ], + default="ROUTINE", + help_text="Clinical priority level", + max_length=20, + ), + ), + ( + "urgency_score", + models.PositiveIntegerField( + default=1, + help_text="Clinical urgency score (1-10, 10 being most urgent)", + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(10), + ], + ), + ), + ( + "clinical_indication", + models.TextField( + help_text="Clinical reason for appointment request" + ), + ), + ( + "diagnosis_codes", + models.JSONField( + blank=True, default=list, help_text="ICD-10 diagnosis codes" + ), + ), + ( + "preferred_date", + models.DateField( + blank=True, + help_text="Patient preferred appointment date", + null=True, + ), + ), + ( + "preferred_time", + models.TimeField( + blank=True, + help_text="Patient preferred appointment time", + null=True, + ), + ), + ( + "flexible_scheduling", + models.BooleanField( + default=True, + help_text="Patient accepts alternative dates/times", + ), + ), + ( + "earliest_acceptable_date", + models.DateField( + blank=True, + help_text="Earliest acceptable appointment date", + null=True, + ), + ), + ( + "latest_acceptable_date", + models.DateField( + blank=True, + help_text="Latest acceptable appointment date", + null=True, + ), + ), + ( + "acceptable_days", + models.JSONField( + blank=True, + default=list, + help_text="Acceptable days of week (0=Monday, 6=Sunday)", + ), + ), + ( + "acceptable_times", + models.JSONField( + blank=True, default=list, help_text="Acceptable time ranges" + ), + ), + ( + "contact_method", + models.CharField( + choices=[ + ("PHONE", "Phone"), + ("EMAIL", "Email"), + ("SMS", "SMS"), + ("PORTAL", "Patient Portal"), + ("MAIL", "Mail"), + ], + default="PHONE", + help_text="Preferred contact method", + max_length=20, + ), + ), + ( + "contact_phone", + models.CharField( + blank=True, + help_text="Contact phone number", + max_length=20, + null=True, + ), + ), + ( + "contact_email", + models.EmailField( + blank=True, + help_text="Contact email address", + max_length=254, + null=True, + ), + ), + ( + "status", + models.CharField( + choices=[ + ("ACTIVE", "Active"), + ("CONTACTED", "Contacted"), + ("OFFERED", "Appointment Offered"), + ("SCHEDULED", "Scheduled"), + ("CANCELLED", "Cancelled"), + ("EXPIRED", "Expired"), + ("TRANSFERRED", "Transferred"), + ], + default="ACTIVE", + help_text="Waiting list status", + max_length=20, + ), + ), + ( + "position", + models.PositiveIntegerField( + blank=True, + help_text="Position in waiting list queue", + null=True, + ), + ), + ( + "estimated_wait_time", + models.PositiveIntegerField( + blank=True, help_text="Estimated wait time in days", null=True + ), + ), + ( + "last_contacted", + models.DateTimeField( + blank=True, + help_text="Last contact attempt date/time", + null=True, + ), + ), + ( + "contact_attempts", + models.PositiveIntegerField( + default=0, help_text="Number of contact attempts made" + ), + ), + ( + "max_contact_attempts", + models.PositiveIntegerField( + default=3, help_text="Maximum contact attempts before expiring" + ), + ), + ( + "appointments_offered", + models.PositiveIntegerField( + default=0, help_text="Number of appointments offered" + ), + ), + ( + "appointments_declined", + models.PositiveIntegerField( + default=0, help_text="Number of appointments declined" + ), + ), + ( + "last_offer_date", + models.DateTimeField( + blank=True, + help_text="Date of last appointment offer", + null=True, + ), + ), + ( + "requires_interpreter", + models.BooleanField( + default=False, help_text="Patient requires interpreter services" + ), + ), + ( + "interpreter_language", + models.CharField( + blank=True, + help_text="Required interpreter language", + max_length=50, + null=True, + ), + ), + ( + "accessibility_requirements", + models.TextField( + blank=True, + help_text="Special accessibility requirements", + null=True, + ), + ), + ( + "transportation_needed", + models.BooleanField( + default=False, + help_text="Patient needs transportation assistance", + ), + ), + ( + "insurance_verified", + models.BooleanField( + default=False, help_text="Insurance coverage verified" + ), + ), + ( + "authorization_required", + models.BooleanField( + default=False, help_text="Prior authorization required" + ), + ), + ( + "authorization_status", + models.CharField( + choices=[ + ("NOT_REQUIRED", "Not Required"), + ("PENDING", "Pending"), + ("APPROVED", "Approved"), + ("DENIED", "Denied"), + ("EXPIRED", "Expired"), + ], + default="NOT_REQUIRED", + help_text="Authorization status", + max_length=20, + ), + ), + ( + "authorization_number", + models.CharField( + blank=True, + help_text="Authorization number", + max_length=100, + null=True, + ), + ), + ( + "referring_provider", + models.CharField( + blank=True, + help_text="Referring provider name", + max_length=200, + null=True, + ), + ), + ( + "referral_date", + models.DateField( + blank=True, help_text="Date of referral", null=True + ), + ), + ( + "referral_urgency", + models.CharField( + choices=[ + ("ROUTINE", "Routine"), + ("URGENT", "Urgent"), + ("STAT", "STAT"), + ], + default="ROUTINE", + help_text="Referral urgency level", + max_length=20, + ), + ), + ( + "removal_reason", + models.CharField( + blank=True, + choices=[ + ("SCHEDULED", "Appointment Scheduled"), + ("PATIENT_CANCELLED", "Patient Cancelled"), + ("PROVIDER_CANCELLED", "Provider Cancelled"), + ("NO_RESPONSE", "No Response to Contact"), + ("INSURANCE_ISSUE", "Insurance Issue"), + ("TRANSFERRED", "Transferred to Another Provider"), + ("EXPIRED", "Entry Expired"), + ("DUPLICATE", "Duplicate Entry"), + ("OTHER", "Other"), + ], + help_text="Reason for removal from waiting list", + max_length=50, + null=True, + ), + ), + ( + "removal_notes", + models.TextField( + blank=True, + help_text="Additional notes about removal", + null=True, + ), + ), + ( + "removed_at", + models.DateTimeField( + blank=True, + help_text="Date/time removed from waiting list", + null=True, + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "notes", + models.TextField( + blank=True, help_text="Additional notes and comments", null=True + ), + ), + ( + "created_by", + models.ForeignKey( + blank=True, + help_text="User who created the waiting list entry", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="created_waiting_list_entries", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "department", + models.ForeignKey( + help_text="Department for appointment", + on_delete=django.db.models.deletion.CASCADE, + related_name="waiting_list_entries", + to="hr.department", + ), + ), + ( + "patient", + models.ForeignKey( + help_text="Patient on waiting list", + on_delete=django.db.models.deletion.CASCADE, + related_name="waiting_list_entries", + to="patients.patientprofile", + ), + ), + ( + "provider", + models.ForeignKey( + blank=True, + help_text="Preferred healthcare provider", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="provider_waiting_list", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "removed_by", + models.ForeignKey( + blank=True, + help_text="User who removed entry from waiting list", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="removed_waiting_list_entries", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "scheduled_appointment", + models.ForeignKey( + blank=True, + help_text="Scheduled appointment from waiting list", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="waiting_list_entry", + to="appointments.appointmentrequest", + ), + ), + ( + "tenant", + models.ForeignKey( + help_text="Organization tenant", + on_delete=django.db.models.deletion.CASCADE, + related_name="waiting_list_entries", + to="core.tenant", + ), + ), + ], + options={ + "verbose_name": "Waiting List Entry", + "verbose_name_plural": "Waiting List Entries", + "db_table": "appointments_waiting_list", + "ordering": ["priority", "urgency_score", "created_at"], + }, + ), + migrations.CreateModel( + name="WaitingListContactLog", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "contact_date", + models.DateTimeField( + auto_now_add=True, help_text="Date and time of contact attempt" + ), + ), + ( + "contact_method", + models.CharField( + choices=[ + ("PHONE", "Phone Call"), + ("EMAIL", "Email"), + ("SMS", "SMS"), + ("PORTAL", "Patient Portal Message"), + ("MAIL", "Mail"), + ("IN_PERSON", "In Person"), + ], + help_text="Method of contact used", + max_length=20, + ), + ), + ( + "contact_outcome", + models.CharField( + choices=[ + ("SUCCESSFUL", "Successful Contact"), + ("NO_ANSWER", "No Answer"), + ("BUSY", "Line Busy"), + ("VOICEMAIL", "Left Voicemail"), + ("EMAIL_SENT", "Email Sent"), + ("EMAIL_BOUNCED", "Email Bounced"), + ("SMS_SENT", "SMS Sent"), + ("SMS_FAILED", "SMS Failed"), + ("WRONG_NUMBER", "Wrong Number"), + ("DECLINED", "Patient Declined"), + ], + help_text="Outcome of contact attempt", + max_length=20, + ), + ), + ( + "appointment_offered", + models.BooleanField( + default=False, + help_text="Appointment was offered during contact", + ), + ), + ( + "offered_date", + models.DateField( + blank=True, help_text="Date of offered appointment", null=True + ), + ), + ( + "offered_time", + models.TimeField( + blank=True, help_text="Time of offered appointment", null=True + ), + ), + ( + "patient_response", + models.CharField( + blank=True, + choices=[ + ("ACCEPTED", "Accepted Appointment"), + ("DECLINED", "Declined Appointment"), + ("REQUESTED_DIFFERENT", "Requested Different Time"), + ("WILL_CALL_BACK", "Will Call Back"), + ("NO_LONGER_NEEDED", "No Longer Needed"), + ("INSURANCE_ISSUE", "Insurance Issue"), + ("NO_RESPONSE", "No Response"), + ], + help_text="Patient response to contact", + max_length=20, + null=True, + ), + ), + ( + "notes", + models.TextField( + blank=True, help_text="Notes from contact attempt", null=True + ), + ), + ( + "next_contact_date", + models.DateField( + blank=True, + help_text="Scheduled date for next contact attempt", + null=True, + ), + ), + ( + "contacted_by", + models.ForeignKey( + blank=True, + help_text="Staff member who made contact", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "waiting_list_entry", + models.ForeignKey( + help_text="Associated waiting list entry", + on_delete=django.db.models.deletion.CASCADE, + related_name="contact_logs", + to="appointments.waitinglist", + ), + ), + ], + options={ + "verbose_name": "Waiting List Contact Log", + "verbose_name_plural": "Waiting List Contact Logs", + "db_table": "appointments_waiting_list_contact_log", + "ordering": ["-contact_date"], + }, + ), + migrations.AddIndex( + model_name="waitinglist", + index=models.Index( + fields=["tenant", "status"], name="appointment_tenant__a558da_idx" + ), + ), + migrations.AddIndex( + model_name="waitinglist", + index=models.Index( + fields=["patient", "status"], name="appointment_patient_73f03d_idx" + ), + ), + migrations.AddIndex( + model_name="waitinglist", + index=models.Index( + fields=["department", "specialty", "status"], + name="appointment_departm_78fd70_idx", + ), + ), + migrations.AddIndex( + model_name="waitinglist", + index=models.Index( + fields=["priority", "urgency_score"], + name="appointment_priorit_30fb90_idx", + ), + ), + migrations.AddIndex( + model_name="waitinglist", + index=models.Index( + fields=["status", "created_at"], name="appointment_status_cfe551_idx" + ), + ), + migrations.AddIndex( + model_name="waitinglist", + index=models.Index( + fields=["provider", "status"], name="appointment_provide_dd6c2b_idx" + ), + ), + migrations.AddIndex( + model_name="waitinglistcontactlog", + index=models.Index( + fields=["waiting_list_entry", "contact_date"], + name="appointment_waiting_50d8ac_idx", + ), + ), + migrations.AddIndex( + model_name="waitinglistcontactlog", + index=models.Index( + fields=["contact_outcome"], name="appointment_contact_ad9c45_idx" + ), + ), + migrations.AddIndex( + model_name="waitinglistcontactlog", + index=models.Index( + fields=["next_contact_date"], name="appointment_next_co_b29984_idx" + ), + ), + ] diff --git a/appointments/migrations/__pycache__/0003_waitinglist_waitinglistcontactlog_and_more.cpython-312.pyc b/appointments/migrations/__pycache__/0003_waitinglist_waitinglistcontactlog_and_more.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..12a8e50c6c5a6cb9c726a684ca56cead369cd77c GIT binary patch literal 15786 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 1 + + days_since_contact = (timezone.now().date() - self.last_contacted.date()).days + + if self.priority == 'EMERGENCY': + return days_since_contact > 0 # Same day contact required + elif self.priority == 'STAT': + return days_since_contact > 1 # Next day contact required + elif self.priority == 'URGENT': + return days_since_contact > 3 # 3 day contact window + else: + return days_since_contact > 7 # Weekly contact for routine + + @property + def should_expire(self): + """Check if waiting list entry should expire.""" + if self.contact_attempts >= self.max_contact_attempts: + return True + + # Expire after 90 days for routine, 30 days for urgent + max_days = 30 if self.priority in ['URGENT', 'STAT', 'EMERGENCY'] else 90 + return self.days_waiting > max_days + + def calculate_position(self): + """Calculate position in waiting list queue.""" + # Priority-based position calculation + priority_weights = { + 'EMERGENCY': 1000, + 'STAT': 800, + 'URGENT': 600, + 'ROUTINE': 400, + } + + base_score = priority_weights.get(self.priority, 400) + urgency_bonus = self.urgency_score * 10 + wait_time_bonus = min(self.days_waiting, 30) # Cap at 30 days + + total_score = base_score + urgency_bonus + wait_time_bonus + + # Count entries with higher scores + higher_priority = WaitingList.objects.filter( + department=self.department, + specialty=self.specialty, + status='ACTIVE', + tenant=self.tenant + ).exclude(id=self.id) + + position = 1 + for entry in higher_priority: + entry_score = ( + priority_weights.get(entry.priority, 400) + + entry.urgency_score * 10 + + min(entry.days_waiting, 30) + ) + if entry_score > total_score: + position += 1 + + return position + + def update_position(self): + """Update position in waiting list.""" + self.position = self.calculate_position() + self.save(update_fields=['position']) + + def estimate_wait_time(self): + """Estimate wait time based on historical data and current queue.""" + # This would typically use historical scheduling data + # For now, provide basic estimation + base_wait = { + 'EMERGENCY': 1, + 'STAT': 3, + 'URGENT': 7, + 'ROUTINE': 14, + } + + estimated_days = base_wait.get(self.priority, 14) + + # Adjust based on queue position + if self.position: + estimated_days += max(0, (self.position - 1) * 2) + + return estimated_days + + +class WaitingListContactLog(models.Model): + """ + Contact log for waiting list entries. + Tracks all communication attempts with patients on waiting list. + """ + CONTACT_METHOD_CHOICES = [ + ('PHONE', 'Phone Call'), + ('EMAIL', 'Email'), + ('SMS', 'SMS'), + ('PORTAL', 'Patient Portal Message'), + ('MAIL', 'Mail'), + ('IN_PERSON', 'In Person'), + ] + CONTACT_OUTCOME_CHOICES = [ + ('SUCCESSFUL', 'Successful Contact'), + ('NO_ANSWER', 'No Answer'), + ('BUSY', 'Line Busy'), + ('VOICEMAIL', 'Left Voicemail'), + ('EMAIL_SENT', 'Email Sent'), + ('EMAIL_BOUNCED', 'Email Bounced'), + ('SMS_SENT', 'SMS Sent'), + ('SMS_FAILED', 'SMS Failed'), + ('WRONG_NUMBER', 'Wrong Number'), + ('DECLINED', 'Patient Declined'), + ] + PATIENT_RESPONSE_CHOICES = [ + ('ACCEPTED', 'Accepted Appointment'), + ('DECLINED', 'Declined Appointment'), + ('REQUESTED_DIFFERENT', 'Requested Different Time'), + ('WILL_CALL_BACK', 'Will Call Back'), + ('NO_LONGER_NEEDED', 'No Longer Needed'), + ('INSURANCE_ISSUE', 'Insurance Issue'), + ('NO_RESPONSE', 'No Response'), + ] + + waiting_list_entry = models.ForeignKey( + WaitingList, + on_delete=models.CASCADE, + related_name='contact_logs', + help_text='Associated waiting list entry' + ) + + contact_date = models.DateTimeField( + auto_now_add=True, + help_text='Date and time of contact attempt' + ) + + contact_method = models.CharField( + max_length=20, + choices=CONTACT_METHOD_CHOICES, + help_text='Method of contact used' + ) + + contact_outcome = models.CharField( + max_length=20, + choices=CONTACT_OUTCOME_CHOICES, + help_text='Outcome of contact attempt' + ) + + appointment_offered = models.BooleanField( + default=False, + help_text='Appointment was offered during contact' + ) + + offered_date = models.DateField( + blank=True, + null=True, + help_text='Date of offered appointment' + ) + + offered_time = models.TimeField( + blank=True, + null=True, + help_text='Time of offered appointment' + ) + + patient_response = models.CharField( + max_length=20, + choices=PATIENT_RESPONSE_CHOICES, + blank=True, + null=True, + help_text='Patient response to contact' + ) + + notes = models.TextField( + blank=True, + null=True, + help_text='Notes from contact attempt' + ) + + next_contact_date = models.DateField( + blank=True, + null=True, + help_text='Scheduled date for next contact attempt' + ) + + contacted_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.SET_NULL, + null=True, + blank=True, + help_text='Staff member who made contact' + ) + + class Meta: + db_table = 'appointments_waiting_list_contact_log' + verbose_name = 'Waiting List Contact Log' + verbose_name_plural = 'Waiting List Contact Logs' + ordering = ['-contact_date'] + indexes = [ + models.Index(fields=['waiting_list_entry', 'contact_date']), + models.Index(fields=['contact_outcome']), + models.Index(fields=['next_contact_date']), + ] + + def __str__(self): + return f"{self.waiting_list_entry.patient.get_full_name()} - {self.contact_method} ({self.contact_outcome})" + diff --git a/appointments/templates/.DS_Store b/appointments/templates/.DS_Store index 92b5a68f8ee536041224aa6be62a3ccf46172631..6f9a9259d318ab35e8e9751d37718301aaaa531f 100644 GIT binary patch literal 8196 zcmeHMTWl0n7(U;$r89PbX(>=xaJQ_A!6m(jKm~E>g$S**vMs$r+1(joWOk z&;QSy@BH66dv+OPXf5bXjMXv5WV*Q2s;Ig|;dXvqRHQ&tK@#N8n8|GBu^clUL?A>UMBv|u0PWel$TRHw!Wz~g0wDtbO9c4(5TlFBWFRMn^p_5*{38I#a#H(+ z?&%!h`@{m74CJJczAMEPrw0VC2#y#K?qrYg?j)0eoD@>HGYEHv;K>M1DDa<7elfp0 zLsH1F4iN|uxEKL`c2`-NjnT)Ma(?%in;CbHFtW+wi zDtDyzIO8cV?G@bCbbgq(dL75glWPa_1RAu-sys0HJK41j5+>1X;PJN}U}U7#tcNQAf3!B8|3t z%rhO^-EQPvlB3#Omr&W9b?lzJ!R`H7$13D(cT}s+rmbwjN_)me%SxH|8Kj%*X7i3^ z^*FA{!we31#(t06E;0=axlZ0|^MehT6kqhx3a+uuprB^aXs6I_?KDK79GkN+dfkmH zo7b%0xM@pw>eO7Vx<;*47xWiR*UVT(-?*uJdj`{P*06Qc-aSOqVteM@rjb{wp(wp} zI%k}!TeP?)vLtftOkT1Umj?&q?iJ6dkHi)2O?gD8_qx;3c|_f}MCv2UWqFwE z+wun0LanZdBor+z_qqgt2+^Q6DVnSF?xoxjuB+75vZgD2Ryt!?Le-+KleJEzzbNKF zC^x7{xf%&&AnW9EG#*!|+aevZ_P)H6Kf5dKdfj7&O^^3SS0C9d%Y)pqQP)XXvuJT$ zXICULSD$wtvuXoFnJnpr#RV!K$LO|Zl42s_H&U>~uQ>`V4F`;Ps<&I6E8 zfmx_REtX&z5?F}_G@=EqXhR1!A%zj_z)su)7aocj#{oQqhw%s=#p8GyNAL`u#ZkPB zSMVB+;W*yKd-wn!;&YtFS2%;S_#Qvw7yODpq)Mqqs+HzT3#6#jBsEJnOY5a>sYkj? z+9nN4x|Eah(u5d-R3hol(hvjv0g{*FU$6*CuV0#f(K|M8>FoON@bAnV&bWEC^A{|P zUB9AX)!N%yFC?W%<5K4Sl@UG(rclQxfuALp$ll13rE0W~SYqs-&gT+iwMxzhndf?> zewiB8Du_p-dBbvQ&LR$p=1qxcRFjChqS@FKRkboArsDhFlAtDnM$4_Ls+AK(g>A#_ z)MR|s5bd^hio^-`x266W_ANWdeqz5<>d(PE)L}WL`)aJGRNsmoO7~v$QM&I!23hFH z!NES{DftiJeoW#4JcvU$j3@9Uo}$!$4$tESyoi@5{a+;l9LEW~g}3nzPU2&Hf=}_8 zPlRu94nN}jwA9`@HMQ~E@1{N_YD{&%vaK=^zK);&J7)g<|H{5Z7(GNF zMBu6-fb#ZKdn>)dOp6ddYscu?MHer;-=vVf3swGcoTPsoCwbuyL%NSqSMHMxmM+p4=TN+3B6#NtxbA)D1|)V{Q-Z9 z|GsCly7$*=agqo>ZZ$N=q}#Ur&pXu-sRZ!UWY+8Zr*v$$bHT^g3zA8jO?B{B}0@a{R0RjQiJE%uj#!gsmk=@WP1Wt0yjvDzOAt5pBeJ6Qc|;+5l!K zJ|^1)CEkOVum7NJ^}78(Ot;ebHh5@2hTBajwwoBH$VdnYq!mfQlIV8iU5RBe2frUb zdG<->V5#-be7xRzM{bsU@5Y+ZE}~n-7&w&iw5a}nQGWklv8Z)8ngPu~kpWg4Iin%Y zT>h9YDrro$wuN$nLXGTliNXatQH~>uavbsM4?`SVXi7e|!dxQGVEgA60e`vZC^=8{ M`7c&mcl*M?56vReasU7T delta 121 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{Mvv5r;6q~50D9jC#2aBa9l@}Kz<>V)AEL_RV z$i10Gu!?zNN#kO64h}(Ppei5`0I7Bb>E2lQoq009j3>w}1}2EPAgdTQ$Mei#1_0RB B6Yc;2 diff --git a/appointments/templates/appointments/partials/contact_log_list.html b/appointments/templates/appointments/partials/contact_log_list.html new file mode 100644 index 00000000..9d8a5588 --- /dev/null +++ b/appointments/templates/appointments/partials/contact_log_list.html @@ -0,0 +1,39 @@ +{% for log in contact_logs %} +
+
+
+ + {{ log.get_contact_method_display }} - + + {{ log.get_contact_outcome_display }} + +
+ {{ log.contact_date|date:"M d, Y H:i" }} +
+

{{ log.notes|default:"No notes." }}

+ {% if log.appointment_offered %} +

+ Appointment Offered: + {{ log.offered_date|date:"M d, Y" }} at {{ log.offered_time|time:"g:i A" }} +

+

+ Patient Response: + + {{ log.get_patient_response_display }} + +

+ {% endif %} + {% if log.next_contact_date %} +

+ Next Contact: {{ log.next_contact_date|date:"M d, Y" }} +

+ {% endif %} + Contacted by: {{ log.contacted_by.get_full_name|default:"N/A" }} +
+{% empty %} +
+ +

No contact logs available for this entry.

+
+{% endfor %} + diff --git a/appointments/templates/appointments/queue/waiting_queue_form.html b/appointments/templates/appointments/queue/waiting_queue_form.html index ff2b5d6f..0d1b68ed 100644 --- a/appointments/templates/appointments/queue/waiting_queue_form.html +++ b/appointments/templates/appointments/queue/waiting_queue_form.html @@ -151,22 +151,15 @@ {% csrf_token %} -
-
-

+
+
+

Basic Information -

-Configure the basic details of the waiting queue -
- - - - -
+

Configure the basic details of the waiting queue

-
-
+ +
+
@@ -221,8 +215,7 @@ {% endif %}
-
-

+
diff --git a/appointments/templates/appointments/waiting_list/waiting_list.html b/appointments/templates/appointments/waiting_list/waiting_list.html new file mode 100644 index 00000000..e0be4dcf --- /dev/null +++ b/appointments/templates/appointments/waiting_list/waiting_list.html @@ -0,0 +1,525 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Patient Waiting List Management{% endblock %} + +{% block css %} + + + + +{% endblock %} + +{% block content %} + + + + + +

+ + Patient Waiting List Management + Manage appointment waiting list and patient queue +

+ + + +
+
+
+
+
+
+
Total Waiting
+

{{ stats.total }}

+ Active entries +
+
+ +
+
+
+
+
+
+
+
+
+
+
Urgent Cases
+

{{ stats.urgent }}

+ High priority +
+
+ +
+
+
+
+
+
+
+
+
+
+
Contacted
+

{{ stats.contacted }}

+ Recently contacted +
+
+ +
+
+
+
+
+
+
+
+
+
+
Avg. Wait
+

{{ stats.avg_wait_days }}

+ Days waiting +
+
+ +
+
+
+
+
+
+ + + +
+
+

+ Filter Waiting List +

+
+ + + +
+
+
+{#
#} +{#
#} +{# {{ filter_form.department.label_tag }}#} +{# {{ filter_form.department }}#} +{#
#} +{#
#} +{# {{ filter_form.specialty.label_tag }}#} +{# {{ filter_form.specialty }}#} +{#
#} +{#
#} +{# {{ filter_form.priority.label_tag }}#} +{# {{ filter_form.priority }}#} +{#
#} +{#
#} +{# {{ filter_form.status.label_tag }}#} +{# {{ filter_form.status }}#} +{#
#} +{#
#} +{# {{ filter_form.provider.label_tag }}#} +{# {{ filter_form.provider }}#} +{#
#} +{#
#} +{# #} +{#
#} +{# #} +{# #} +{# Clear#} +{# #} +{#
#} +{#
#} +{#
#} +
+
+ + + +
+ +
+

+ Waiting List Entries +

+
+ + Add to Waiting List + + +
+
+ + + +
+ +
+
+
+ {% csrf_token %} +
+
+ + +
+
+ {{ bulk_action_form.action }} +
+ +
+
+
+
+ + + +
+ + + + + + + + + + + + + + + + + + {% for entry in waiting_list %} + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
+ + Pos.PatientDepartmentSpecialtyPriorityUrgencyStatusWait TimeLast ContactActions
+ + + {{ entry.position|default:"-" }} + +
+
+ {{ entry.patient.first_name|first }}{{ entry.patient.last_name|first }} +
+
+
{{ entry.patient.get_full_name }}
+ + MRN: {{ entry.patient.mrn|default:"N/A" }} + {% if entry.requires_interpreter %} + + {% endif %} + +
+
+
+ {{ entry.department.name }} + + {{ entry.get_specialty_display }} + + {% if entry.priority == 'EMERGENCY' %} + {{ entry.get_priority_display }} + {% elif entry.priority == 'STAT' %} + {{ entry.get_priority_display }} + {% elif entry.priority == 'URGENT' %} + {{ entry.get_priority_display }} + {% else %} + {{ entry.get_priority_display }} + {% endif %} + +
+ {{ entry.urgency_score }} +
+
+
+
+
+ {% if entry.status == 'ACTIVE' %} + Active + {% elif entry.status == 'CONTACTED' %} + Contacted + {% elif entry.status == 'OFFERED' %} + Offered + {% elif entry.status == 'SCHEDULED' %} + Scheduled + {% elif entry.status == 'CANCELLED' %} + Cancelled + {% else %} + {{ entry.get_status_display }} + {% endif %} + +
+
{{ entry.days_waiting }}
+ days +
+
+ {% if entry.last_contacted %} +
+
{{ entry.last_contacted|date:"M d" }}
+ {{ entry.last_contacted|timesince }} ago +
+ {% else %} + Never + {% endif %} + {% if entry.is_overdue_contact %} + + {% endif %} +
+
+ + + + + + + +
+
+
+ +

No patients currently on waiting list

+ + Add First Patient + +
+
+
+ + + + {% if is_paginated %} + {% include 'partial/pagination.html' %} + {% endif %} + +
+ +
+ + + + +{% endblock %} + +{% block js %} + + + + + + + + + + +{% endblock %} + diff --git a/appointments/templates/appointments/waiting_list/waiting_list_confirm_delete.html b/appointments/templates/appointments/waiting_list/waiting_list_confirm_delete.html new file mode 100644 index 00000000..d846f81f --- /dev/null +++ b/appointments/templates/appointments/waiting_list/waiting_list_confirm_delete.html @@ -0,0 +1,76 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Confirm Cancellation{% endblock %} + +{% block content %} + + + + + +

+ + Confirm Waiting List Entry Cancellation + Permanently remove this patient from the waiting list +

+ + + +
+
+

+ Warning: This action cannot be undone! +

+
+
+
+

Are you absolutely sure you want to cancel this waiting list entry?

+

Cancelling this entry will remove {{ object.patient.get_full_name }} from the waiting list for {{ object.get_specialty_display }}.

+

This action is usually taken when the patient no longer requires the appointment, has been scheduled through other means, or has been transferred.

+
+ +
+
+
+ +

{{ object.patient.get_full_name }}

+
+
+ +

{{ object.get_specialty_display }}

+
+
+
+
+ +

{{ object.get_priority_display }}

+
+
+ +

{{ object.days_waiting }} days

+
+
+
+ +
+ {% csrf_token %} +
+ + Cancel + + +
+
+
+
+ +{% endblock %} + diff --git a/appointments/templates/appointments/waiting_list/waiting_list_detail.html b/appointments/templates/appointments/waiting_list/waiting_list_detail.html new file mode 100644 index 00000000..2e117b92 --- /dev/null +++ b/appointments/templates/appointments/waiting_list/waiting_list_detail.html @@ -0,0 +1,427 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Waiting List Entry Details{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} + + + + + +

+ + Waiting List Entry Details + Comprehensive view of patient waiting list entry +

+ + + +
+
+

+ Patient: {{ entry.patient.get_full_name }} +

+ +
+ +
+
+
+ +
+
Patient & Service Information
+
+
Patient Name:
+
{{ entry.patient.get_full_name }} (MRN: {{ entry.patient.mrn|default:'N/A' }})
+
+
+
Department:
+
{{ entry.department.name }}
+
+
+
Preferred Provider:
+
{{ entry.provider.get_full_name|default:'Any' }}
+
+
+
Appointment Type:
+
{{ entry.get_appointment_type_display }}
+
+
+
Medical Specialty:
+
{{ entry.get_specialty_display }}
+
+
+ + +
+
Clinical Priority & Urgency
+
+
Priority Level:
+
+ {% if entry.priority == 'EMERGENCY' %} + {{ entry.get_priority_display }} + {% elif entry.priority == 'STAT' %} + {{ entry.get_priority_display }} + {% elif entry.priority == 'URGENT' %} + {{ entry.get_priority_display }} + {% else %} + {{ entry.get_priority_display }} + {% endif %} +
+
+
+
Urgency Score:
+
{{ entry.urgency_score }} / 10
+
+
+
Clinical Indication:
+
{{ entry.clinical_indication|linebreaksbr }}
+
+
+
Diagnosis Codes:
+
{{ entry.diagnosis_codes|join:", "|default:'N/A' }}
+
+
+ + +
+
Patient Scheduling Preferences
+
+
Preferred Date:
+
{{ entry.preferred_date|date:"M d, Y"|default:'Any' }}
+
+
+
Preferred Time:
+
{{ entry.preferred_time|time:"g:i A"|default:'Any' }}
+
+
+
Flexible Scheduling:
+
{% if entry.flexible_scheduling %}Yes{% else %}No{% endif %}
+
+ {% if entry.flexible_scheduling %} +
+
Acceptable Date Range:
+
+ {% if entry.earliest_acceptable_date %}{{ entry.earliest_acceptable_date|date:"M d, Y" }}{% else %}Any{% endif %} + to + {% if entry.latest_acceptable_date %}{{ entry.latest_acceptable_date|date:"M d, Y" }}{% else %}Any{% endif %} +
+
+ {% endif %} +
+ + +
+
Contact Information
+
+
Preferred Method:
+
{{ entry.get_contact_method_display }}
+
+
+
Phone:
+
{{ entry.contact_phone|default:'N/A' }}
+
+
+
Email:
+
{{ entry.contact_email|default:'N/A' }}
+
+
+ + +
+
Special Requirements & Accommodations
+
+
Interpreter Needed:
+
{% if entry.requires_interpreter %}Yes ({{ entry.interpreter_language|default:'N/A' }}){% else %}No{% endif %}
+
+
+
Transportation Needed:
+
{% if entry.transportation_needed %}Yes{% else %}No{% endif %}
+
+
+
Accessibility:
+
{{ entry.accessibility_requirements|default:'None'|linebreaksbr }}
+
+
+ + +
+
Insurance & Authorization
+
+
Insurance Verified:
+
{% if entry.insurance_verified %}Yes{% else %}No{% endif %}
+
+
+
Authorization Required:
+
{% if entry.authorization_required %}Yes{% else %}No{% endif %}
+
+
+
Authorization Status:
+
{{ entry.get_authorization_status_display }}
+
+
+
Authorization Number:
+
{{ entry.authorization_number|default:'N/A' }}
+
+
+ + +
+
Referral Information
+
+
Referring Provider:
+
{{ entry.referring_provider|default:'N/A' }}
+
+
+
Referral Date:
+
{{ entry.referral_date|date:"M d, Y"|default:'N/A' }}
+
+
+
Referral Urgency:
+
{{ entry.get_referral_urgency_display }}
+
+
+ + +
+
Additional Notes
+
+
{{ entry.notes|default:'No additional notes.'|linebreaksbr }}
+
+
+ + +
+
Outcome Tracking
+
+
Status:
+
+ {% if entry.status == 'ACTIVE' %} + {{ entry.get_status_display }} + {% elif entry.status == 'CONTACTED' %} + {{ entry.get_status_display }} + {% elif entry.status == 'OFFERED' %} + {{ entry.get_status_display }} + {% elif entry.status == 'SCHEDULED' %} + {{ entry.get_status_display }} + {% elif entry.status == 'CANCELLED' %} + {{ entry.get_status_display }} + {% else %} + {{ entry.get_status_display }} + {% endif %} +
+
+
+
Scheduled Appointment:
+ +
+ {% if entry.status == 'CANCELLED' or entry.status == 'EXPIRED' or entry.status == 'TRANSFERRED' %} +
+
Removal Reason:
+
{{ entry.get_removal_reason_display|default:'N/A' }}
+
+
+
Removal Notes:
+
{{ entry.removal_notes|default:'None'|linebreaksbr }}
+
+
+
Removed At:
+
{{ entry.removed_at|date:"M d, Y H:i"|default:'N/A' }}
+
+
+
Removed By:
+
{{ entry.removed_by.get_full_name|default:'N/A' }}
+
+ {% endif %} +
+ + +
+
Metadata
+
+
Entry ID:
+
{{ entry.waiting_list_id }}
+
+
+
Created At:
+
{{ entry.created_at|date:"M d, Y H:i" }} by {{ entry.created_by.get_full_name|default:'N/A' }}
+
+
+
Last Updated:
+
{{ entry.updated_at|date:"M d, Y H:i" }}
+
+
+
+ +
+ +
+
+
Waiting List Metrics
+
+
+
+
Current Position:
+
{{ entry.position|default:'N/A' }}
+
+
+
Days Waiting:
+
{{ entry.days_waiting }} days
+
+
+
Estimated Wait Time:
+
{{ estimated_wait_time }} days
+
+
+
Contact Attempts:
+
{{ entry.contact_attempts }}
+
+
+
Overdue Contact:
+
{% if entry.is_overdue_contact %}Yes{% else %}No{% endif %}
+
+
+
+ + +
+
+
Contact Log
+
+
+ {% include 'appointments/partials/contact_log_list.html' %} +
+ +
+
+
+
+
+ + + + +{% endblock %} + +{% block extra_js %} + + +{% endblock %} + diff --git a/appointments/templates/appointments/waiting_list/waiting_list_form.html b/appointments/templates/appointments/waiting_list/waiting_list_form.html new file mode 100644 index 00000000..645035e4 --- /dev/null +++ b/appointments/templates/appointments/waiting_list/waiting_list_form.html @@ -0,0 +1,561 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}{% if object %}Edit{% else %}Add{% endif %} Waiting List Entry{% endblock %} + +{% block extra_css %} + + + +{% endblock %} + +{% block content %} + + + + + +

+ + {% if object %}Edit Waiting List Entry{% else %}Add Patient to Waiting List{% endif %} + {% if object %}Update patient information{% else %}Register new waiting list entry{% endif %} +

+ + + +
+
+

+ Patient Information & Request Details +

+ +
+ +
+
+ {% csrf_token %} + + +
+
+ Patient & Service Information +
+ +
+
+
+ + {{ form.patient }} + {% if form.patient.errors %} +
{{ form.patient.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.department }} + {% if form.department.errors %} +
{{ form.department.errors.0 }}
+ {% endif %} +
+
+
+ +
+
+
+ + {{ form.provider }} + Leave blank for any available provider + {% if form.provider.errors %} +
{{ form.provider.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.appointment_type }} + {% if form.appointment_type.errors %} +
{{ form.appointment_type.errors.0 }}
+ {% endif %} +
+
+
+ +
+
+
+ + {{ form.specialty }} + {% if form.specialty.errors %} +
{{ form.specialty.errors.0 }}
+ {% endif %} +
+
+
+
+ + +
+
+ Clinical Priority & Urgency +
+ +
+
+
+ + {{ form.priority }} +
+
+ Select priority level +
+
+ {% if form.priority.errors %} +
{{ form.priority.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.urgency_score }} +
+
+
+ 1 = Routine, 10 = Most Urgent + {% if form.urgency_score.errors %} +
{{ form.urgency_score.errors.0 }}
+ {% endif %} +
+
+
+ +
+
+
+ + {{ form.clinical_indication }} + {% if form.clinical_indication.errors %} +
{{ form.clinical_indication.errors.0 }}
+ {% endif %} +
+
+
+ +
+
+
+ + {{ form.diagnosis_codes }} + Enter diagnosis codes separated by commas (e.g., M25.511, Z51.11) + {% if form.diagnosis_codes.errors %} +
{{ form.diagnosis_codes.errors.0 }}
+ {% endif %} +
+
+
+
+ + +
+
+ Patient Scheduling Preferences +
+ +
+
+
+ + {{ form.preferred_date }} + {% if form.preferred_date.errors %} +
{{ form.preferred_date.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.preferred_time }} + {% if form.preferred_time.errors %} +
{{ form.preferred_time.errors.0 }}
+ {% endif %} +
+
+
+
+
+ {{ form.flexible_scheduling }} + +
+
+
+
+ + +
+ + +
+
+ Contact Information +
+ +
+
+
+ + {{ form.contact_method }} + {% if form.contact_method.errors %} +
{{ form.contact_method.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.contact_phone }} + {% if form.contact_phone.errors %} +
{{ form.contact_phone.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.contact_email }} + {% if form.contact_email.errors %} +
{{ form.contact_email.errors.0 }}
+ {% endif %} +
+
+
+
+ + +
+
+ Special Requirements & Accommodations +
+ +
+
+
+ {{ form.requires_interpreter }} + +
+ + +
+
+
+ {{ form.transportation_needed }} + +
+
+
+ +
+
+
+ + {{ form.accessibility_requirements }} + Describe any special accessibility needs (wheelchair access, hearing assistance, etc.) + {% if form.accessibility_requirements.errors %} +
{{ form.accessibility_requirements.errors.0 }}
+ {% endif %} +
+
+
+
+ + +
+
+ Insurance & Authorization +
+ +
+
+
+ {{ form.insurance_verified }} + +
+
+
+
+ {{ form.authorization_required }} + +
+
+
+
+ + +
+
+ Referral Information +
+ +
+
+
+ + {{ form.referring_provider }} + {% if form.referring_provider.errors %} +
{{ form.referring_provider.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.referral_date }} + {% if form.referral_date.errors %} +
{{ form.referral_date.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.referral_urgency }} + {% if form.referral_urgency.errors %} +
{{ form.referral_urgency.errors.0 }}
+ {% endif %} +
+
+
+
+ + +
+
+ Additional Notes +
+ +
+
+
+ + {{ form.notes }} + {% if form.notes.errors %} +
{{ form.notes.errors.0 }}
+ {% endif %} +
+
+
+
+ + +
+
+
+ + Cancel + +
+ +
+
+
+
+
+
+
+ +{% endblock %} + +{% block extra_js %} + + + + +{% endblock %} + diff --git a/appointments/templates/appointments/waiting_list/waitinglistentry_list.html b/appointments/templates/appointments/waiting_list/waitinglistentry_list.html new file mode 100644 index 00000000..af70bb31 --- /dev/null +++ b/appointments/templates/appointments/waiting_list/waitinglistentry_list.html @@ -0,0 +1,299 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Waiting List Management{% endblock %} + +{% block extra_css %} + + + +{% endblock %} + +{% block content %} + + + + + +

+ + Waiting List Management + Manage patient waiting list entries +

+ + + +
+ +
+

+ Waiting List Entries +

+ +
+ + + +
+ +
+
+
+
+
+
+
Total Waiting
+

{{ waitinglistentry_list|length }}

+
+
+ +
+
+
+
+
+
+
+
+
+
+
Pending
+

{{ waitinglistentry_list|length }}

+
+
+ +
+
+
+
+
+
+
+
+
+
+
Priority
+

0

+
+
+ +
+
+
+
+
+
+
+
+
+
+
Avg. Wait Time
+

5 days

+
+
+ +
+
+
+
+
+
+ + + +
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+
+
+
+
+ + + +
+ + + + + + + + + + + + + + + + {% for entry in waitinglistentry_list %} + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
#PatientAppointment TypeDesired DateDesired TimeStatusWait TimePriorityActions
{{ forloop.counter }} +
+
+
+ {{ entry.patient.first_name|first }}{{ entry.patient.last_name|first }} +
+
+
+
{{ entry.patient.get_full_name }}
+ ID: {{ entry.patient.patient_id }} +
+
+
+ {{ entry.appointment_type.name }} + + {% if entry.desired_date %} + {{ entry.desired_date|date:"M d, Y" }} + {% else %} + Any date + {% endif %} + + {% if entry.desired_time %} + {{ entry.desired_time|time:"g:i A" }} + {% else %} + Any time + {% endif %} + + {% if entry.status == 'pending' %} + Pending + {% elif entry.status == 'contacted' %} + Contacted + {% elif entry.status == 'scheduled' %} + Scheduled + {% elif entry.status == 'cancelled' %} + Cancelled + {% else %} + {{ entry.status|title }} + {% endif %} + + {{ entry.created_at|timesince }} + + Normal + + +
+
+ +

No waiting list entries found

+ + Add First Entry + +
+
+
+ +
+ +
+ +{% endblock %} + +{% block extra_js %} + + + + + + + + + + +{% endblock %} + diff --git a/appointments/templates/appointments/waitlist/waitlist_management.html b/appointments/templates/appointments/waiting_list/waitlist_management.html similarity index 95% rename from appointments/templates/appointments/waitlist/waitlist_management.html rename to appointments/templates/appointments/waiting_list/waitlist_management.html index 526575f0..ddec9a03 100644 --- a/appointments/templates/appointments/waitlist/waitlist_management.html +++ b/appointments/templates/appointments/waiting_list/waitlist_management.html @@ -4,8 +4,8 @@ {% block title %}Waitlist Management{% endblock %} {% block css %} - - + + {% endblock %} {% block content %} @@ -163,15 +163,6 @@

Current Waitlist

- {% if messages %} - {% for message in messages %} - - {% endfor %} - {% endif %} -
@@ -405,10 +396,10 @@ {% endblock %} {% block js %} - - - - + + + + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_detail (1).html b/operating_theatre/templates/operating_theatre/others/surgical_note_detail (1).html new file mode 100644 index 00000000..26fc8239 --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_detail (1).html @@ -0,0 +1,649 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Surgical Note - {{ note.patient.get_full_name }}{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+ +
+
+ +

+ Surgical Note Details +

+
+
+
+ + Back to List + + {% if note.status != 'signed' %} + + Edit + + {% endif %} + +
+ + +
+
+
+
+ + +
+
+
+

{{ note.procedure_name }}

+
+
+

Patient: {{ note.patient.get_full_name }}

+

Patient ID: {{ note.patient.patient_id }}

+

Date of Birth: {{ note.patient.date_of_birth|date:"M d, Y" }}

+
+
+

Surgery Date: {{ note.surgery_date|date:"M d, Y" }}

+

Surgeon: {{ note.surgeon.get_full_name }}

+

Operating Room: {{ note.operating_room.name }}

+
+
+
+
+
+ + {{ note.get_status_display }} + +
+
+ + {{ note.get_priority_display }} Priority + +
+

Note ID: {{ note.id }}

+
+
+
+ + +
+
+ Pre-operative Information +
+
+
+
+
Pre-operative Diagnosis
+
{{ note.preoperative_diagnosis|default:"Not specified" }}
+
+
+
Planned Procedure
+
{{ note.planned_procedure|default:"Not specified" }}
+
+
+
Anesthesia Type
+
{{ note.get_anesthesia_type_display|default:"Not specified" }}
+
+
+
ASA Classification
+
{{ note.asa_classification|default:"Not specified" }}
+
+
+ + {% if note.preoperative_notes %} +
+
Pre-operative Notes
+
{{ note.preoperative_notes|linebreaks }}
+
+ {% endif %} +
+
+ + +
+
+ Operative Procedure +
+
+
+
+
Actual Procedure
+
{{ note.actual_procedure|default:"Not specified" }}
+
+
+
Procedure Duration
+
{{ note.procedure_duration|default:"Not specified" }}
+
+
+
Incision Type
+
{{ note.incision_type|default:"Not specified" }}
+
+
+
Closure Method
+
{{ note.closure_method|default:"Not specified" }}
+
+
+ + {% if note.operative_findings %} +
+
Operative Findings
+
{{ note.operative_findings|linebreaks }}
+
+ {% endif %} + + {% if note.procedure_description %} +
+
Detailed Procedure Description
+
{{ note.procedure_description|linebreaks }}
+
+ {% endif %} +
+
+ + +
+
+ Post-operative Information +
+
+
+
+
Post-operative Diagnosis
+
{{ note.postoperative_diagnosis|default:"Not specified" }}
+
+
+
Estimated Blood Loss
+
{{ note.estimated_blood_loss|default:"Not specified" }}
+
+
+
Complications
+
{{ note.complications|default:"None reported" }}
+
+
+
Condition at End
+
{{ note.condition_at_end|default:"Not specified" }}
+
+
+ + {% if note.postoperative_instructions %} +
+
Post-operative Instructions
+
{{ note.postoperative_instructions|linebreaks }}
+
+ {% endif %} +
+
+ + +
+
+ Surgical Team +
+
+
+
+
Primary Surgeon
+
{{ note.surgeon.get_full_name }}
+
+ {% if note.assistant_surgeon %} +
+
Assistant Surgeon
+
{{ note.assistant_surgeon.get_full_name }}
+
+ {% endif %} + {% if note.anesthesiologist %} +
+
Anesthesiologist
+
{{ note.anesthesiologist.get_full_name }}
+
+ {% endif %} + {% if note.scrub_nurse %} +
+
Scrub Nurse
+
{{ note.scrub_nurse.get_full_name }}
+
+ {% endif %} +
+
+
+ + + {% if note.revisions.exists %} +
+
+ Revision History +
+
+
+ {% for revision in note.revisions.all %} +
+
+
+
+
{{ revision.get_action_display }}
+ {{ revision.created_at|date:"M d, Y g:i A" }} +
+

By: {{ revision.created_by.get_full_name }}

+ {% if revision.reason %} +

Reason: {{ revision.reason }}

+ {% endif %} +
+
+ {% endfor %} +
+
+
+ {% endif %} + + + {% if note.status == 'signed' %} +
+
+ Electronic Signature +
+
+
+
+ +
Electronically Signed
+

Signed by: {{ note.signed_by.get_full_name }}

+

Date: {{ note.signed_at|date:"M d, Y g:i A" }}

+

IP Address: {{ note.signature_ip|default:"Not recorded" }}

+
+
+
+
+ {% elif note.status == 'completed' %} +
+
+ Electronic Signature Required +
+

This note is complete and ready for electronic signature.

+ +
+ {% endif %} +
+ + + +{% endblock %} + +{% block extra_js %} + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_detail.html b/operating_theatre/templates/operating_theatre/others/surgical_note_detail.html new file mode 100644 index 00000000..26fc8239 --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_detail.html @@ -0,0 +1,649 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Surgical Note - {{ note.patient.get_full_name }}{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+ +
+
+ +

+ Surgical Note Details +

+
+
+
+ + Back to List + + {% if note.status != 'signed' %} + + Edit + + {% endif %} + +
+ + +
+
+
+
+ + +
+
+
+

{{ note.procedure_name }}

+
+
+

Patient: {{ note.patient.get_full_name }}

+

Patient ID: {{ note.patient.patient_id }}

+

Date of Birth: {{ note.patient.date_of_birth|date:"M d, Y" }}

+
+
+

Surgery Date: {{ note.surgery_date|date:"M d, Y" }}

+

Surgeon: {{ note.surgeon.get_full_name }}

+

Operating Room: {{ note.operating_room.name }}

+
+
+
+
+
+ + {{ note.get_status_display }} + +
+
+ + {{ note.get_priority_display }} Priority + +
+

Note ID: {{ note.id }}

+
+
+
+ + +
+
+ Pre-operative Information +
+
+
+
+
Pre-operative Diagnosis
+
{{ note.preoperative_diagnosis|default:"Not specified" }}
+
+
+
Planned Procedure
+
{{ note.planned_procedure|default:"Not specified" }}
+
+
+
Anesthesia Type
+
{{ note.get_anesthesia_type_display|default:"Not specified" }}
+
+
+
ASA Classification
+
{{ note.asa_classification|default:"Not specified" }}
+
+
+ + {% if note.preoperative_notes %} +
+
Pre-operative Notes
+
{{ note.preoperative_notes|linebreaks }}
+
+ {% endif %} +
+
+ + +
+
+ Operative Procedure +
+
+
+
+
Actual Procedure
+
{{ note.actual_procedure|default:"Not specified" }}
+
+
+
Procedure Duration
+
{{ note.procedure_duration|default:"Not specified" }}
+
+
+
Incision Type
+
{{ note.incision_type|default:"Not specified" }}
+
+
+
Closure Method
+
{{ note.closure_method|default:"Not specified" }}
+
+
+ + {% if note.operative_findings %} +
+
Operative Findings
+
{{ note.operative_findings|linebreaks }}
+
+ {% endif %} + + {% if note.procedure_description %} +
+
Detailed Procedure Description
+
{{ note.procedure_description|linebreaks }}
+
+ {% endif %} +
+
+ + +
+
+ Post-operative Information +
+
+
+
+
Post-operative Diagnosis
+
{{ note.postoperative_diagnosis|default:"Not specified" }}
+
+
+
Estimated Blood Loss
+
{{ note.estimated_blood_loss|default:"Not specified" }}
+
+
+
Complications
+
{{ note.complications|default:"None reported" }}
+
+
+
Condition at End
+
{{ note.condition_at_end|default:"Not specified" }}
+
+
+ + {% if note.postoperative_instructions %} +
+
Post-operative Instructions
+
{{ note.postoperative_instructions|linebreaks }}
+
+ {% endif %} +
+
+ + +
+
+ Surgical Team +
+
+
+
+
Primary Surgeon
+
{{ note.surgeon.get_full_name }}
+
+ {% if note.assistant_surgeon %} +
+
Assistant Surgeon
+
{{ note.assistant_surgeon.get_full_name }}
+
+ {% endif %} + {% if note.anesthesiologist %} +
+
Anesthesiologist
+
{{ note.anesthesiologist.get_full_name }}
+
+ {% endif %} + {% if note.scrub_nurse %} +
+
Scrub Nurse
+
{{ note.scrub_nurse.get_full_name }}
+
+ {% endif %} +
+
+
+ + + {% if note.revisions.exists %} +
+
+ Revision History +
+
+
+ {% for revision in note.revisions.all %} +
+
+
+
+
{{ revision.get_action_display }}
+ {{ revision.created_at|date:"M d, Y g:i A" }} +
+

By: {{ revision.created_by.get_full_name }}

+ {% if revision.reason %} +

Reason: {{ revision.reason }}

+ {% endif %} +
+
+ {% endfor %} +
+
+
+ {% endif %} + + + {% if note.status == 'signed' %} +
+
+ Electronic Signature +
+
+
+
+ +
Electronically Signed
+

Signed by: {{ note.signed_by.get_full_name }}

+

Date: {{ note.signed_at|date:"M d, Y g:i A" }}

+

IP Address: {{ note.signature_ip|default:"Not recorded" }}

+
+
+
+
+ {% elif note.status == 'completed' %} +
+
+ Electronic Signature Required +
+

This note is complete and ready for electronic signature.

+ +
+ {% endif %} +
+ + + +{% endblock %} + +{% block extra_js %} + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_form (1).html b/operating_theatre/templates/operating_theatre/others/surgical_note_form (1).html new file mode 100644 index 00000000..c8a073a4 --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_form (1).html @@ -0,0 +1,318 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}{% if object %}Edit Surgical Note{% else %}Create Surgical Note{% endif %}{% endblock %} + +{% block extra_css %} + + + +{% endblock %} + +{% block content %} + + + + + +

+ + {% if object %}Edit Surgical Note{% else %}Create New Surgical Note{% endif %} + {% if object %}Update existing surgical record{% else %}Document a new surgical procedure{% endif %} +

+ + + +
+
+

+ Surgical Note Details +

+ +
+ +
+
+ {% csrf_token %} + + +
+
Patient & Procedure Information
+ +
+
+
+ + {{ form.patient }} + {% if form.patient.errors %} +
{{ form.patient.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.surgeon }} + {% if form.surgeon.errors %} +
{{ form.surgeon.errors.0 }}
+ {% endif %} +
+
+
+ +
+
+
+ + {{ form.procedure_name }} + {% if form.procedure_name.errors %} +
{{ form.procedure_name.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.procedure_code }} + {% if form.procedure_code.errors %} +
{{ form.procedure_code.errors.0 }}
+ {% endif %} +
+
+
+ +
+
+
+ + {{ form.procedure_date }} + {% if form.procedure_date.errors %} +
{{ form.procedure_date.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.procedure_time }} + {% if form.procedure_time.errors %} +
{{ form.procedure_time.errors.0 }}
+ {% endif %} +
+
+
+
+ + +
+
Pre-operative Details
+ +
+ + {{ form.pre_operative_diagnosis }} + {% if form.pre_operative_diagnosis.errors %} +
{{ form.pre_operative_diagnosis.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.anesthesia_type }} + {% if form.anesthesia_type.errors %} +
{{ form.anesthesia_type.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.anesthesiologist }} + {% if form.anesthesiologist.errors %} +
{{ form.anesthesiologist.errors.0 }}
+ {% endif %} +
+
+ + +
+
Intra-operative Details
+ +
+ + {{ form.post_operative_diagnosis }} + {% if form.post_operative_diagnosis.errors %} +
{{ form.post_operative_diagnosis.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.procedure_description }} + {% if form.procedure_description.errors %} +
{{ form.procedure_description.errors.0 }}
+ {% endif %} +
+ +
+
+
+ + {{ form.estimated_blood_loss }} + {% if form.estimated_blood_loss.errors %} +
{{ form.estimated_blood_loss.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.complications }} + {% if form.complications.errors %} +
{{ form.complications.errors.0 }}
+ {% endif %} +
+
+
+ +
+ + {{ form.specimens_sent }} + {% if form.specimens_sent.errors %} +
{{ form.specimens_sent.errors.0 }}
+ {% endif %} +
+
+ + +
+
Post-operative Details
+ +
+ + {{ form.post_operative_orders }} + {% if form.post_operative_orders.errors %} +
{{ form.post_operative_orders.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.condition_on_discharge }} + {% if form.condition_on_discharge.errors %} +
{{ form.condition_on_discharge.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.follow_up_plan }} + {% if form.follow_up_plan.errors %} +
{{ form.follow_up_plan.errors.0 }}
+ {% endif %} +
+
+ + +
+
Signature
+ +
+ + {{ form.signed_by }} + {% if form.signed_by.errors %} +
{{ form.signed_by.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.signature_date }} + {% if form.signature_date.errors %} +
{{ form.signature_date.errors.0 }}
+ {% endif %} +
+
+ + +
+
+
+ + Cancel + +
+ +
+
+
+
+ +
+
+ +{% endblock %} + +{% block extra_js %} + + + + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_form.html b/operating_theatre/templates/operating_theatre/others/surgical_note_form.html new file mode 100644 index 00000000..c20f042f --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_form.html @@ -0,0 +1,745 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}{% if object %}Edit Surgical Note{% else %}Create Surgical Note{% endif %}{% endblock %} + +{% block extra_css %} + + + + +{% endblock %} + +{% block content %} +
+
+ +
+
+ +
+
+ + +
+
+
+
1
+
Basic Info
+
+
+
2
+
Procedure
+
+
+
3
+
Documentation
+
+
+
4
+
Review
+
+
+
+
+
+
+ +
+ {% csrf_token %} + + + {% if not object %} +
+
Use Template
+
+
+ +
+
+ +
+
+
+ {% endif %} + + +
+
+

+ + Basic Information +

+
+ +
+
+
+ + {{ form.patient }} + {% if form.patient.errors %} +
{{ form.patient.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.surgical_case }} + {% if form.surgical_case.errors %} +
{{ form.surgical_case.errors.0 }}
+ {% endif %} +
+
+
+ +
+
+
+ + {{ form.surgery_date }} + {% if form.surgery_date.errors %} +
{{ form.surgery_date.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.start_time }} + {% if form.start_time.errors %} +
{{ form.start_time.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.end_time }} + {% if form.end_time.errors %} +
{{ form.end_time.errors.0 }}
+ {% endif %} +
+
+
+ +
+
+
+ + {{ form.primary_surgeon }} + {% if form.primary_surgeon.errors %} +
{{ form.primary_surgeon.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.assistant_surgeons }} + {% if form.assistant_surgeons.errors %} +
{{ form.assistant_surgeons.errors.0 }}
+ {% endif %} +
+
+
+
+ + +
+
+

+ + Procedure Details +

+
+ +
+
+
+ + {{ form.procedure_name }} + {% if form.procedure_name.errors %} +
{{ form.procedure_name.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.cpt_code }} + {% if form.cpt_code.errors %} +
{{ form.cpt_code.errors.0 }}
+ {% endif %} +
+
+
+ +
+ + {{ form.preoperative_diagnosis }} + {% if form.preoperative_diagnosis.errors %} +
{{ form.preoperative_diagnosis.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.postoperative_diagnosis }} + {% if form.postoperative_diagnosis.errors %} +
{{ form.postoperative_diagnosis.errors.0 }}
+ {% endif %} +
+ +
+
+
+ + {{ form.anesthesia_type }} + {% if form.anesthesia_type.errors %} +
{{ form.anesthesia_type.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.anesthesiologist }} + {% if form.anesthesiologist.errors %} +
{{ form.anesthesiologist.errors.0 }}
+ {% endif %} +
+
+
+
+ + +
+
+

+ + Surgical Documentation +

+
+ +
+ + {{ form.operative_technique }} + {% if form.operative_technique.errors %} +
{{ form.operative_technique.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.findings }} + {% if form.findings.errors %} +
{{ form.findings.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.complications }} + {% if form.complications.errors %} +
{{ form.complications.errors.0 }}
+ {% endif %} +
+ +
+
+
+ + {{ form.estimated_blood_loss }} + {% if form.estimated_blood_loss.errors %} +
{{ form.estimated_blood_loss.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.specimens_sent }} + {% if form.specimens_sent.errors %} +
{{ form.specimens_sent.errors.0 }}
+ {% endif %} +
+
+
+ +
+ + {{ form.postoperative_instructions }} + {% if form.postoperative_instructions.errors %} +
{{ form.postoperative_instructions.errors.0 }}
+ {% endif %} +
+
+ + +
+
+

+ + Electronic Signature +

+
+ +
+
+
+ +
+
+
+ +
Click to sign or upload signature image
+
+
+
+
+ + + +
+
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+ + +
+
+ + Cancel + + +
+
+
+ +
+
+ + + +{% endblock %} + +{% block extra_js %} + + + + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_list (1).html b/operating_theatre/templates/operating_theatre/others/surgical_note_list (1).html new file mode 100644 index 00000000..69c4314e --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_list (1).html @@ -0,0 +1,516 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Surgical Notes{% endblock %} + +{% block extra_css %} + + + +{% endblock %} + +{% block content %} +
+ +
+
+ +

+ Surgical Notes Management +

+
+ +
+ + +
+
+
+ +
+
{{ stats.total_notes|default:0 }}
+
Total Notes
+
+ +
+
+ +
+
{{ stats.draft_notes|default:0 }}
+
Draft Notes
+
+ +
+
+ +
+
{{ stats.completed_notes|default:0 }}
+
Completed
+
+ +
+
+ +
+
{{ stats.signed_notes|default:0 }}
+
Signed
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
+ +
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ Surgical Notes +
+
+ + + +
+
+
+
+
+ + + + + + + + + + + + + + + {% for note in notes %} + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
+ + PatientProcedureSurgeonDateStatusPriorityLast ModifiedActions
+ + +
+
+ +
+
+
{{ note.patient.get_full_name }}
+ ID: {{ note.patient.patient_id }} +
+
+
+
{{ note.procedure_name }}
+ {{ note.procedure_code|default:"" }} +
+
{{ note.surgeon.get_full_name }}
+ {{ note.surgeon.specialization|default:"" }} +
+
{{ note.surgery_date|date:"M d, Y" }}
+ {{ note.surgery_date|time:"g:i A" }} +
+ + {{ note.get_status_display }} + + + + {{ note.get_priority_display }} + + +
{{ note.updated_at|date:"M d, Y" }}
+ {{ note.updated_at|time:"g:i A" }} +
+
+ + + + {% if note.status != 'signed' %} + + + + {% endif %} + + {% if note.status != 'signed' %} + + + + {% endif %} +
+
+ +

No surgical notes found

+ + Create First Note + +
+
+
+
+
+{% endblock %} + +{% block extra_js %} + + + + + + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_list.html b/operating_theatre/templates/operating_theatre/others/surgical_note_list.html new file mode 100644 index 00000000..69c4314e --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_list.html @@ -0,0 +1,516 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Surgical Notes{% endblock %} + +{% block extra_css %} + + + +{% endblock %} + +{% block content %} +
+ +
+
+ +

+ Surgical Notes Management +

+
+ +
+ + +
+
+
+ +
+
{{ stats.total_notes|default:0 }}
+
Total Notes
+
+ +
+
+ +
+
{{ stats.draft_notes|default:0 }}
+
Draft Notes
+
+ +
+
+ +
+
{{ stats.completed_notes|default:0 }}
+
Completed
+
+ +
+
+ +
+
{{ stats.signed_notes|default:0 }}
+
Signed
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
+ +
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ Surgical Notes +
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + {% for note in notes %} + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
+ + PatientProcedureSurgeonDateStatusPriorityLast ModifiedActions
+ + +
+
+ +
+
+
{{ note.patient.get_full_name }}
+ ID: {{ note.patient.patient_id }} +
+
+
+
{{ note.procedure_name }}
+ {{ note.procedure_code|default:"" }} +
+
{{ note.surgeon.get_full_name }}
+ {{ note.surgeon.specialization|default:"" }} +
+
{{ note.surgery_date|date:"M d, Y" }}
+ {{ note.surgery_date|time:"g:i A" }} +
+ + {{ note.get_status_display }} + + + + {{ note.get_priority_display }} + + +
{{ note.updated_at|date:"M d, Y" }}
+ {{ note.updated_at|time:"g:i A" }} +
+
+ + + + {% if note.status != 'signed' %} + + + + {% endif %} + + {% if note.status != 'signed' %} + + + + {% endif %} +
+
+ +

No surgical notes found

+ + Create First Note + +
+
+
+
+
+{% endblock %} + +{% block extra_js %} + + + + + + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_template_confirm_delete (1).html b/operating_theatre/templates/operating_theatre/others/surgical_note_template_confirm_delete (1).html new file mode 100644 index 00000000..cbe45d74 --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_template_confirm_delete (1).html @@ -0,0 +1,76 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Confirm Delete Surgical Note Template{% endblock %} + +{% block content %} + + + + + +

+ + Confirm Delete Surgical Note Template + Permanently remove this reusable template +

+ + + +
+
+

+ Warning: This action cannot be undone! +

+
+
+
+

Are you absolutely sure you want to delete this surgical note template?

+

Deleting the template {{ object.name }} will permanently remove it from the system.

+

Any surgical notes created using this template will retain their content, but you will no longer be able to use this template for new notes.

+
+ +
+
+
+ +

{{ object.name }}

+
+
+ +

{{ object.specialty }}

+
+
+
+
+ +

{{ object.procedure_type }}

+
+
+ +

{{ object.updated_at|date:"M d, Y H:i" }}

+
+
+
+ +
+ {% csrf_token %} +
+ + Cancel + + +
+
+
+
+ +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_template_confirm_delete.html b/operating_theatre/templates/operating_theatre/others/surgical_note_template_confirm_delete.html new file mode 100644 index 00000000..63d3ca08 --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_template_confirm_delete.html @@ -0,0 +1,837 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Delete Template - {{ object.name }}{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+
+ +
+
+ +
+
+ + +
+
+ +
+ +

Delete "{{ object.name }}"?

+ +

+ You are about to permanently delete this surgical note template. This action cannot be undone and may have significant impact on your hospital's documentation workflow. +

+ + +
+
+ + Template Information +
+ +
+
+
Template Name
+
{{ object.name }}
+
+ +
+
Specialty
+
{{ object.get_specialty_display }}
+
+ +
+
Created By
+
{{ object.created_by.get_full_name }}
+
+ +
+
Created Date
+
{{ object.created_at|date:"F d, Y" }}
+
+ +
+
Last Modified
+
{{ object.updated_at|date:"F d, Y g:i A" }}
+
+ +
+
Status
+
+ {% if object.is_active %} + Active + {% elif object.is_draft %} + Draft + {% else %} + Archived + {% endif %} +
+
+
+
+ + +
+
+ + Usage Statistics +
+
+
+
{{ object.usage_count|default:0 }}
+
Times Used
+
+
+
{{ object.active_notes_count|default:0 }}
+
Active Notes
+
+
+
{{ object.users_count|default:0 }}
+
Users
+
+
+
{{ object.departments_count|default:0 }}
+
Departments
+
+
+
+ + +
+
+ + Deletion Impact +
+
    +
  • All template content and structure will be permanently lost
  • +
  • {{ object.usage_count|default:0 }} existing surgical notes using this template will lose their template reference
  • +
  • Users who have bookmarked this template will lose access
  • +
  • Template sharing permissions will be revoked
  • +
  • Version history and audit trail will be preserved for compliance
  • + {% if object.is_active %} +
  • Warning: This is an active template currently in use
  • + {% endif %} + {% if object.usage_count > 10 %} +
  • High Impact: This template is heavily used ({{ object.usage_count }} times)
  • + {% endif %} +
+
+ + + {% if object.dependent_templates.exists %} +
+
+ + Dependent Templates +
+

The following templates are based on this template and may be affected:

+ {% for dependent in object.dependent_templates.all %} +
+ {{ dependent.name }} + {{ dependent.usage_count|default:0 }} uses +
+ {% endfor %} +
+ {% endif %} + + +
+
+ + Template Preview (Last 10 lines) +
+
+{{ object.content|default:"No content available"|truncatewords:50 }} + +--- +Template Fields: {{ object.field_count|default:0 }} +Last Modified: {{ object.updated_at|date:"F d, Y g:i A" }} +Version: {{ object.version|default:"1.0" }} +
+
+ + +
+
+ + Before You Delete +
+
    +
  1. +
    + Backup Important Data
    + Export the template if you might need it later +
    +
  2. +
  3. +
    + Notify Affected Users
    + Inform users who regularly use this template +
    +
  4. +
  5. +
    + Check Dependencies
    + Ensure no critical workflows depend on this template +
    +
  6. +
  7. +
    + Provide Alternative
    + Suggest alternative templates for users +
    +
  8. +
+
+ + +
+ {% csrf_token %} + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
This information will be logged for audit purposes
+
+ + +
+ + +
Type the exact template name: {{ object.name }}
+
+ + +
+ + Cancel + + +
+
+ + +
+ + Security Notice: This action will be logged and may be subject to review by hospital administration. The deletion will be recorded in the audit trail with your user information and timestamp. +
+ + +
+ + Deletion will be performed by: {{ user.get_full_name }} ({{ user.username }}) + on +
+
+
+
+ + + +{% endblock %} + +{% block extra_js %} + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_template_detail (1).html b/operating_theatre/templates/operating_theatre/others/surgical_note_template_detail (1).html new file mode 100644 index 00000000..3e28a80b --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_template_detail (1).html @@ -0,0 +1,136 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Surgical Note Template Details{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} + + + + + +

+ + Surgical Note Template Details + Comprehensive view of surgical note template +

+ + + +
+
+

+ Template: {{ object.name }} +

+ +
+ +
+
+
+ +
+
Template Information
+
+
Template Name:
+
{{ object.name }}
+
+
+
Specialty:
+
{{ object.specialty }}
+
+
+
Procedure Type:
+
{{ object.procedure_type }}
+
+
+
Description:
+
{{ object.description|default:\'N/A\'|linebreaksbr }}
+
+
+ + +
+
Template Content
+
+
Pre-operative Diagnosis:
+
{{ object.pre_operative_diagnosis|default:\'N/A\'|linebreaksbr }}
+
+
+
Anesthesia Type:
+
{{ object.anesthesia_type|default: +\'N/A\' }}
+
+
+
Post-operative Diagnosis:
+
{{ object.post_operative_diagnosis|default: +\'N/A\'|linebreaksbr }}
+
+
+
Procedure Description:
+
{{ object.procedure_description|default: +\'N/A\'|linebreaksbr }}
+
+
+
Post-operative Orders:
+
{{ object.post_operative_orders|default: +\'N/A\'|linebreaksbr }}
+
+
+
Follow-up Plan:
+
{{ object.follow_up_plan|default: +\'N/A\'|linebreaksbr }}
+
+
+ + +
+
Metadata
+
+
Created At:
+
{{ object.created_at|date:"M d, Y H:i" }} by {{ object.created_by.get_full_name|default: +\'N/A\' }}
+
+
+
Last Updated:
+
{{ object.updated_at|date:"M d, Y H:i" }}
+
+
+
+
+
+
+ +{% endblock %} + +{% block extra_js %} +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_template_detail.html b/operating_theatre/templates/operating_theatre/others/surgical_note_template_detail.html new file mode 100644 index 00000000..022f63f0 --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_template_detail.html @@ -0,0 +1,937 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}{{ object.name }} - Template Details{% endblock %} + +{% block extra_css %} + + +{% endblock %} + +{% block content %} +
+
+ +
+
+
+

{{ object.name }}

+

{{ object.description }}

+
+
+
+ +
+
+
{{ object.get_specialty_display }}
+ Specialty +
+
+
+
+ +
+
+
{{ object.created_by.get_full_name }}
+ Created by +
+
+
+
+ +
+
+
{{ object.created_at|date:"M d, Y" }}
+ Created +
+
+
+
+ +
+
+
{{ object.usage_count|default:0 }}
+ Times Used +
+
+
+
+
+ {% if object.is_active %} + + Active + + {% elif object.is_draft %} + + Draft + + {% else %} + + Archived + + {% endif %} +
+
+
+ + +
+ + Use This Template + + + Edit Template + + + + + + Back to Templates + +
+ + +
+
+
{{ object.usage_count|default:0 }}
+
Total Usage
+
+
+
{{ object.field_count|default:0 }}
+
Form Fields
+
+
+
{{ object.version|default:"1.0" }}
+
Version
+
+
+
{{ object.rating|default:"4.5" }}
+
Rating
+
+
+ + +
+ +
+
+

+ + Template Details +

+
+ + +
+
Tags
+
+ {% for tag in object.tags.all %} + {{ tag.name }} + {% empty %} + General + Surgery + Documentation + {% endfor %} +
+
+ + +
+
Description
+

{{ object.description|default:"No description provided." }}

+
+ + +
+
Template Fields
+
+
+
Patient Information
+ Required +
Basic patient demographics and identifiers
+
+
+
Procedure Details
+ Required +
Surgical procedure name and codes
+
+
+
Preoperative Diagnosis
+ Required +
Diagnosis before surgery
+
+
+
Postoperative Diagnosis
+ Required +
Diagnosis after surgery
+
+
+
Operative Technique
+ Required +
Detailed surgical procedure description
+
+
+
Complications
+ Optional +
Any complications during surgery
+
+
+
+ + +
+
Template Structure
+
+SURGICAL NOTE TEMPLATE - {{ object.name|upper }} + +PATIENT INFORMATION: +- Name: [Patient Name] +- DOB: [Date of Birth] +- MRN: [Medical Record Number] +- Date of Surgery: [Surgery Date] + +PROCEDURE INFORMATION: +- Primary Surgeon: [Surgeon Name] +- Assistant Surgeon(s): [Assistant Names] +- Anesthesiologist: [Anesthesiologist Name] +- Anesthesia Type: [Anesthesia Type] + +DIAGNOSES: +- Preoperative Diagnosis: [Pre-op Diagnosis] +- Postoperative Diagnosis: [Post-op Diagnosis] + +PROCEDURE PERFORMED: +[Procedure Name and Details] + +OPERATIVE TECHNIQUE: +[Detailed description of surgical technique and steps] + +FINDINGS: +[Surgical findings and observations] + +COMPLICATIONS: +[Any complications encountered] + +ESTIMATED BLOOD LOSS: [Amount] mL + +SPECIMENS SENT: +[Specimens sent to pathology] + +POSTOPERATIVE INSTRUCTIONS: +[Post-operative care instructions] + +SURGEON SIGNATURE: ________________________ +Date: _______________ +
+
+
+ + +
+ +
+
+
+ + Usage Statistics +
+
+
+ +
+
+ + +
+
+
+ + Version History +
+
+ +
+
+ v{{ object.version|default:"1.0" }} + {{ object.updated_at|date:"M d, Y" }} +
+
+ Current version - {{ object.description|truncatewords:10 }} +
+
+ +
+
+ v0.9 + {{ object.created_at|date:"M d, Y" }} +
+
+ Initial template creation with basic fields +
+
+
+ + +
+
+
+ + Quick Stats +
+
+ +
+
+ Completion Rate + 95% +
+
+
+
+
+ +
+
+ User Satisfaction + 4.8/5 +
+
+
+
+
+ +
+
+ Error Rate + 2% +
+
+
+
+
+ +
+ Based on last 30 days usage +
+
+
+
+ + +
+
+

+ + Template Preview +

+
+ +
+
+
+
Patient Information
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
Procedure Details
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
Surgical Documentation
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + + +{% endblock %} + +{% block extra_js %} + + + + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_template_form (1).html b/operating_theatre/templates/operating_theatre/others/surgical_note_template_form (1).html new file mode 100644 index 00000000..8ba4e857 --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_template_form (1).html @@ -0,0 +1,207 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}{% if object %}Edit Surgical Note Template{% else %}Create Surgical Note Template{% endif %}{% endblock %} + +{% block extra_css %} + + +{% endblock %} + +{% block content %} + + + + + +

+ + {% if object %}Edit Surgical Note Template{% else %}Create New Surgical Note Template{% endif %} + {% if object %}Update existing template{% else %}Define a new reusable template for surgical notes{% endif %} +

+ + + +
+
+

+ Template Details +

+ +
+ +
+
+ {% csrf_token %} + + +
+
Template Information
+ +
+ + {{ form.name }} + {% if form.name.errors %} +
{{ form.name.errors.0 }}
+ {% endif %} +
+ +
+
+
+ + {{ form.specialty }} + {% if form.specialty.errors %} +
{{ form.specialty.errors.0 }}
+ {% endif %} +
+
+
+
+ + {{ form.procedure_type }} + {% if form.procedure_type.errors %} +
{{ form.procedure_type.errors.0 }}
+ {% endif %} +
+
+
+ +
+ + {{ form.description }} + {% if form.description.errors %} +
{{ form.description.errors.0 }}
+ {% endif %} +
+
+ + +
+
Template Content (Pre-fill fields for new notes)
+ +
+ + {{ form.pre_operative_diagnosis }} + {% if form.pre_operative_diagnosis.errors %} +
{{ form.pre_operative_diagnosis.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.anesthesia_type }} + {% if form.anesthesia_type.errors %} +
{{ form.anesthesia_type.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.post_operative_diagnosis }} + {% if form.post_operative_diagnosis.errors %} +
{{ form.post_operative_diagnosis.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.procedure_description }} + {% if form.procedure_description.errors %} +
{{ form.procedure_description.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.post_operative_orders }} + {% if form.post_operative_orders.errors %} +
{{ form.post_operative_orders.errors.0 }}
+ {% endif %} +
+ +
+ + {{ form.follow_up_plan }} + {% if form.follow_up_plan.errors %} +
{{ form.follow_up_plan.errors.0 }}
+ {% endif %} +
+
+ + +
+
+
+ + Cancel + +
+ +
+
+
+
+
+
+
+ +{% endblock %} + +{% block extra_js %} + + + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_template_form.html b/operating_theatre/templates/operating_theatre/others/surgical_note_template_form.html new file mode 100644 index 00000000..818a0333 --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_template_form.html @@ -0,0 +1,1110 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}{% if object %}Edit Template{% else %}Create Template{% endif %}{% endblock %} + +{% block extra_css %} + + + + + +{% endblock %} + +{% block content %} +
+
+ +
+
+
+
+

+ + {% if object %}Edit Template{% else %}Template Builder{% endif %} +

+

+ {% if object %} + Update and modify your surgical note template + {% else %} + Create a comprehensive surgical note template with custom fields and structure + {% endif %} +

+
+
+ +
+
+
+ +
+ +
+
+
+
1
+
Basic Info
+
+
+
2
+
Fields
+
+
+
3
+
Layout
+
+
+
4
+
Review
+
+
+
+
+
+
+ +
+ {% csrf_token %} + + +
+
+

+ + Basic Information +

+ +
+ +
+
+
+
+ + {{ form.name }} + {% if form.name.errors %} +
{{ form.name.errors.0 }}
+ {% endif %} +
Choose a descriptive name for your template
+
+
+
+
+ + {{ form.specialty }} + {% if form.specialty.errors %} +
{{ form.specialty.errors.0 }}
+ {% endif %} +
+
+
+ +
+ + {{ form.description }} + {% if form.description.errors %} +
{{ form.description.errors.0 }}
+ {% endif %} +
Provide a brief description of when and how to use this template
+
+ +
+
+
+ + {{ form.tags }} + {% if form.tags.errors %} +
{{ form.tags.errors.0 }}
+ {% endif %} +
Add tags to help organize and find this template
+
+
+
+
+ + {{ form.status }} + {% if form.status.errors %} +
{{ form.status.errors.0 }}
+ {% endif %} +
+
+
+
+
+ + + {% if not object %} +
+
+

+ + Start from Template +

+
+ +
+

Choose a base template to start with, or create from scratch:

+
+
+
+
General Surgery
+ Standard surgical note template +
+
+
+
+
Cardiac Surgery
+ Specialized for cardiac procedures +
+
+
+
+
Orthopedic Surgery
+ Tailored for orthopedic procedures +
+
+
+
+
+ {% endif %} + + +
+
+

+ + Template Builder +

+
+ +
+ + + +
+ + +
+
+
+
Available Fields
+
+
+ Text +
+
+ Textarea +
+
+ Select +
+
+ Date +
+
+ Time +
+
+ Number +
+
+ Checkbox +
+
+ Signature +
+
+ + +
+ +
+
Template Fields
+
+
+ +

Click on field types to add them to your template

+
+
+
+
+
+ + +
+
+ + +
Use HTML and Django template syntax to create your template
+
+
+ + +
+
+
+ +
Template Preview
+

Build your template to see the preview here

+
+
+
+
+
+
+
+ + +
+
+
+ + +
+
+ + Cancel + + +
+
+
+
+
+ + + +{% endblock %} + +{% block extra_js %} + + + + + + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_template_list (1).html b/operating_theatre/templates/operating_theatre/others/surgical_note_template_list (1).html new file mode 100644 index 00000000..55e6550a --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_template_list (1).html @@ -0,0 +1,135 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Surgical Note Templates{% endblock %} + +{% block extra_css %} + + + +{% endblock %} + +{% block content %} + + + + + +

+ + Surgical Note Templates + Manage reusable templates for surgical notes +

+ + + +
+ +
+

+ Available Templates +

+ +
+ + + +
+
+ + + + + + + + + + + + + {% for template in object_list %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
#Template NameSpecialtyProcedure TypeLast UpdatedActions
{{ forloop.counter }}{{ template.name }}{{ template.specialty }}{{ template.procedure_type }}{{ template.updated_at|date:"M d, Y H:i" }} + +
+
+ +

No surgical note templates found.

+ + Create First Template + +
+
+
+
+ +
+ +{% endblock %} + +{% block extra_js %} + + + + + + + + + + +{% endblock %} + diff --git a/operating_theatre/templates/operating_theatre/others/surgical_note_template_list.html b/operating_theatre/templates/operating_theatre/others/surgical_note_template_list.html new file mode 100644 index 00000000..e4a0ffe0 --- /dev/null +++ b/operating_theatre/templates/operating_theatre/others/surgical_note_template_list.html @@ -0,0 +1,947 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}Surgical Note Templates{% endblock %} + +{% block extra_css %} + + + +{% endblock %} + +{% block content %} +
+
+ +
+
+
+

+ + Surgical Note Templates +

+

+ Manage and organize surgical note templates for consistent documentation across all procedures +

+
+ +
+
+ + +
+
+
+ +
+
{{ total_templates|default:0 }}
+
Total Templates
+
+ +
+
+ +
+
{{ active_templates|default:0 }}
+
Active Templates
+
+ +
+
+ +
+
{{ popular_templates|default:0 }}
+
Most Used
+
+ +
+
+ +
+
{{ recent_templates|default:0 }}
+
Recent Updates
+
+
+ + +
+
+ + Quick Actions +
+
+
+
+ +
+
Create New Template
+
Build a new surgical note template from scratch
+
+ +
+
+ +
+
Import Template
+
Import templates from external sources or files
+
+ +
+
+ +
+
Export Templates
+
Export selected templates for backup or sharing
+
+ +
+
+ +
+
Usage Analytics
+
View detailed usage statistics and insights
+
+
+
+ + +
+
+ + Filter Templates +
+
+
+ +
+ + + + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+ + +
+
+

+ + Template Library +

+
+ + +
+
+ + + + + +
+
+ + + + + + + + + + + + + + {% for template in templates %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
Template NameSpecialtyStatusUsageCreatedLast UpdatedActions
+
+
+ +
+
+
{{ template.name }}
+ {{ template.description|truncatewords:10 }} +
+
+
+ + {{ template.get_specialty_display }} + + + {% if template.is_active %} + Active + {% elif template.is_draft %} + Draft + {% else %} + Archived + {% endif %} + +
+ {{ template.usage_count|default:0 }} +
+
+
+
+
+
{{ template.created_at|date:"M d, Y" }}
+ {{ template.created_by.get_full_name }} +
{{ template.updated_at|date:"M d, Y g:i A" }} + +
+ +
No Templates Found
+

Create your first surgical note template to get started.

+ + Create Template + +
+
+
+
+
+
+ + + +{% endblock %} + +{% block extra_js %} + + + + + +{% endblock %} + diff --git a/patients/.DS_Store b/patients/.DS_Store index b83f52ef9edd52e161312a29ef96b73534420534..795fc496135cf869becbbe8c2a62804bc15e4428 100644 GIT binary patch delta 108 zcmZoMXfc=|#>B!ku~2NHo+2aL#(>?7iwl^U7}+-SFu5>J4q`e!*_K&eo|B=3A(bJQ zp@1QWArZ_jE(d~iE%SK2R{eUEFkAQ^JIPzM-D~?1}301!{!K) GHOv6Vtr>*? delta 71 zcmZoMXfc=|#>B)qu~2NHo+2a5#(>?7j4YdZSX>w + + + +{% endblock %} + +{% block content %} + + + + + +

+ + Patient Management Dashboard + Comprehensive overview of patient care and management +

+ + + +
+
+
+
+
+
+
Total Patients
+

{{ stats.total_patients|default:0 }}

+ Active registrations +
+
+ +
+
+
+
+
+
+
+
+
+
+
Active Inpatients
+

{{ stats.active_inpatients|default:0 }}

+ Currently admitted +
+
+ +
+
+
+
+
+
+
+
+
+
+
Today's Visits
+

{{ stats.todays_visits|default:0 }}

+ Outpatient visits +
+
+ +
+
+
+
+
+
+
+
+
+
+
Critical Alerts
+

{{ stats.critical_alerts|default:0 }}

+ Require attention +
+
+ +
+
+
+
+
+
+ + + + + + + +
+ +
+ +
+
+

+ Recent Admissions +

+ +
+
+
+ + + + + + + + + + + + + {% for admission in recent_admissions %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
PatientRoomAdmission DateAttending PhysicianStatusActions
+
+
+ {{ admission.patient.first_name|first }}{{ admission.patient.last_name|first }} +
+
+
{{ admission.patient.get_full_name }}
+ MRN: {{ admission.patient.mrn }} +
+
+
+ {{ admission.room.number }} + {{ admission.room.ward.name }} + {{ admission.admission_date|date:"M d, Y H:i" }}{{ admission.attending_physician.get_full_name }} + {% if admission.status == 'ACTIVE' %} + Active + {% elif admission.status == 'PENDING' %} + Pending + {% else %} + {{ admission.get_status_display }} + {% endif %} + + +
+ +

No recent admissions

+
+
+
+
+ + +
+
+

+ Patient Activity Trends +

+
+ +
+
+
+
+ +
+
+
+
+ + +
+ +
+
+

+ Critical Alerts +

+
+ +
+
+
+ {% for alert in critical_alerts %} +
+
+
+
{{ alert.title }}
+

{{ alert.message }}

+ + {{ alert.created_at|timesince }} ago + +
+ +
+
+ {% empty %} +
+ +

No critical alerts

+
+ {% endfor %} +
+
+ + +
+
+

+ Today's Schedule +

+ +
+
+ {% for appointment in todays_appointments %} +
+
+ {{ appointment.patient.first_name|first }}{{ appointment.patient.last_name|first }} +
+
+
{{ appointment.patient.get_full_name }}
+ {{ appointment.get_appointment_type_display }} +
+ {{ appointment.appointment_time|time:"g:i A" }} +
+
+
+ + {{ appointment.get_status_display }} + +
+
+ {% empty %} +
+ +

No appointments scheduled for today

+
+ {% endfor %} +
+
+ + +
+
+

+ Department Occupancy +

+
+
+ {% for dept in department_occupancy %} +
+
+ {{ dept.name }} + {{ dept.occupied }}/{{ dept.capacity }} +
+
+
+
+ {{ dept.occupancy_rate }}% occupancy +
+ {% empty %} +
+ +

No department data available

+
+ {% endfor %} +
+
+
+
+ +{% endblock %} + +{% block extra_js %} + + + + + +{% endblock %} +