425 lines
14 KiB
Python
425 lines
14 KiB
Python
"""
|
|
Core views - Health check and utility views
|
|
"""
|
|
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.db import connection
|
|
from django.http import JsonResponse
|
|
from django.shortcuts import redirect, render
|
|
from django.views.decorators.cache import never_cache
|
|
from django.views.decorators.http import require_GET, require_POST
|
|
|
|
|
|
@never_cache
|
|
@require_GET
|
|
def health_check(request):
|
|
"""
|
|
Health check endpoint for monitoring and load balancers.
|
|
Returns JSON with status of various services.
|
|
"""
|
|
health_status = {"status": "ok", "services": {}}
|
|
|
|
# Check database connection
|
|
try:
|
|
with connection.cursor() as cursor:
|
|
cursor.execute("SELECT 1")
|
|
health_status["services"]["database"] = "ok"
|
|
except Exception as e:
|
|
health_status["status"] = "error"
|
|
health_status["services"]["database"] = f"error: {str(e)}"
|
|
|
|
# Check Redis/Celery (optional - don't fail if not available)
|
|
try:
|
|
from django_celery_beat.models import PeriodicTask
|
|
|
|
PeriodicTask.objects.count()
|
|
health_status["services"]["celery_beat"] = "ok"
|
|
except Exception:
|
|
health_status["services"]["celery_beat"] = "not_configured"
|
|
|
|
# Return appropriate status code
|
|
status_code = 200 if health_status["status"] == "ok" else 503
|
|
|
|
return JsonResponse(health_status, status=status_code)
|
|
|
|
|
|
@login_required
|
|
def select_hospital(request):
|
|
"""
|
|
Hospital selection page for PX Admins.
|
|
|
|
Allows PX Admins to switch between hospitals.
|
|
Stores selected hospital in session.
|
|
"""
|
|
# Only PX Admins should access this page
|
|
if not request.user.is_px_admin():
|
|
return redirect("dashboard:dashboard")
|
|
|
|
from apps.organizations.models import Hospital
|
|
|
|
hospitals = Hospital.objects.all().order_by("name")
|
|
|
|
# Handle hospital selection
|
|
if request.method == "POST":
|
|
hospital_id = request.POST.get("hospital_id")
|
|
if hospital_id:
|
|
try:
|
|
hospital = Hospital.objects.get(id=hospital_id)
|
|
request.session["selected_hospital_id"] = str(hospital.id)
|
|
# Redirect to referring page or dashboard
|
|
next_url = request.POST.get("next", request.GET.get("next", "/"))
|
|
return redirect(next_url)
|
|
except Hospital.DoesNotExist:
|
|
pass
|
|
|
|
context = {
|
|
"hospitals": hospitals,
|
|
"selected_hospital_id": request.session.get("selected_hospital_id"),
|
|
"next": request.GET.get("next", "/"),
|
|
}
|
|
|
|
return render(request, "core/select_hospital.html", context)
|
|
|
|
|
|
@login_required
|
|
@require_POST
|
|
def switch_hospital(request):
|
|
"""
|
|
AJAX endpoint to switch hospitals for PX Admins.
|
|
|
|
Stores selected hospital in session and returns JSON response.
|
|
"""
|
|
# Only PX Admins can switch hospitals
|
|
if not request.user.is_px_admin():
|
|
return JsonResponse(
|
|
{"success": False, "error": "Permission denied. Only PX Admins can switch hospitals."}, status=403
|
|
)
|
|
|
|
from apps.organizations.models import Hospital
|
|
|
|
hospital_id = request.POST.get("hospital_id")
|
|
|
|
if not hospital_id:
|
|
return JsonResponse({"success": False, "error": "Hospital ID is required"}, status=400)
|
|
|
|
try:
|
|
hospital = Hospital.objects.get(id=hospital_id)
|
|
request.session["selected_hospital_id"] = str(hospital.id)
|
|
|
|
return JsonResponse(
|
|
{
|
|
"success": True,
|
|
"hospital": {
|
|
"id": str(hospital.id),
|
|
"name": hospital.name,
|
|
"code": hospital.code,
|
|
},
|
|
}
|
|
)
|
|
except Hospital.DoesNotExist:
|
|
return JsonResponse({"success": False, "error": "Hospital not found"}, status=404)
|
|
|
|
|
|
@login_required
|
|
def no_hospital_assigned(request):
|
|
"""
|
|
Error page for users without a hospital assigned.
|
|
|
|
Users without a hospital assignment cannot access the system.
|
|
"""
|
|
return render(request, "core/no_hospital_assigned.html", status=403)
|
|
|
|
|
|
# ============================================================================
|
|
# PUBLIC SUBMISSION VIEWS
|
|
# ============================================================================
|
|
|
|
|
|
def public_submit_landing(request):
|
|
"""
|
|
Landing page for public submissions.
|
|
|
|
Allows users to choose between Complaint, Observation, or Inquiry.
|
|
No authentication required.
|
|
"""
|
|
from apps.organizations.models import Hospital
|
|
|
|
if request.method == "POST":
|
|
# Return 405 Method Not Allowed with proper JSON response
|
|
from django.http import JsonResponse
|
|
|
|
return JsonResponse(
|
|
{"success": False, "error": "Method not allowed. Please use GET to access the landing page."}, status=405
|
|
)
|
|
|
|
hospitals = Hospital.objects.all().order_by("name")
|
|
|
|
context = {
|
|
"hospitals": hospitals,
|
|
}
|
|
|
|
return render(request, "core/public_submit.html", context)
|
|
|
|
|
|
@require_POST
|
|
def public_inquiry_submit(request):
|
|
"""
|
|
Handle public inquiry submissions.
|
|
|
|
Creates an inquiry from public submission.
|
|
Returns JSON response with reference number.
|
|
"""
|
|
from apps.complaints.models import Inquiry
|
|
from apps.organizations.models import Hospital
|
|
import uuid
|
|
|
|
# Get form data
|
|
name = request.POST.get("name", "").strip()
|
|
email = request.POST.get("email", "").strip()
|
|
phone = request.POST.get("phone", "").strip()
|
|
hospital_id = request.POST.get("hospital")
|
|
category = request.POST.get("category", "").strip()
|
|
subject = request.POST.get("subject", "").strip()
|
|
message = request.POST.get("message", "").strip()
|
|
|
|
# Validation
|
|
errors = []
|
|
|
|
if not name:
|
|
errors.append("Name is required")
|
|
if not email:
|
|
errors.append("Email is required")
|
|
if not phone:
|
|
errors.append("Phone number is required")
|
|
if not hospital_id:
|
|
errors.append("Hospital selection is required")
|
|
if not subject:
|
|
errors.append("Subject is required")
|
|
if not message:
|
|
errors.append("Message is required")
|
|
|
|
if errors:
|
|
return JsonResponse({"success": False, "errors": errors}, status=400)
|
|
|
|
try:
|
|
# Validate hospital
|
|
hospital = Hospital.objects.get(id=hospital_id)
|
|
|
|
# Create inquiry (using correct field names from model)
|
|
inquiry = Inquiry.objects.create(
|
|
hospital=hospital,
|
|
contact_name=name,
|
|
contact_email=email,
|
|
contact_phone=phone,
|
|
subject=subject,
|
|
message=message,
|
|
category=category,
|
|
status="open",
|
|
)
|
|
|
|
# Generate a simple reference for display
|
|
reference_number = f"INQ-{str(inquiry.id)[:8].upper()}"
|
|
|
|
# Send notification email (optional)
|
|
try:
|
|
from django.core.mail import send_mail
|
|
from django.conf import settings
|
|
from django.template.loader import render_to_string
|
|
|
|
subject = f"New Public Inquiry - {reference_number}"
|
|
html_message = render_to_string(
|
|
"emails/public_inquiry_notification.html",
|
|
{
|
|
"name": name,
|
|
"subject": subject,
|
|
"message": message,
|
|
"reference_number": reference_number,
|
|
},
|
|
)
|
|
plain_message = f"Inquiry from {name}\n\nSubject: {subject}\n\nMessage:\n{message}"
|
|
send_mail(
|
|
subject=subject,
|
|
message=plain_message,
|
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
|
recipient_list=[settings.DEFAULT_FROM_EMAIL],
|
|
fail_silently=True,
|
|
html_message=html_message,
|
|
)
|
|
except Exception:
|
|
pass # Don't fail if email doesn't send
|
|
|
|
return JsonResponse({"success": True, "reference_number": reference_number, "inquiry_id": str(inquiry.id)})
|
|
|
|
except Hospital.DoesNotExist:
|
|
return JsonResponse({"success": False, "errors": ["Invalid hospital selected"]}, status=400)
|
|
except Exception as e:
|
|
return JsonResponse({"success": False, "errors": [str(e)]}, status=500)
|
|
|
|
|
|
@require_GET
|
|
def api_hospitals(request):
|
|
"""
|
|
API endpoint to get hospitals list.
|
|
|
|
Used by public submission forms to populate hospital dropdown.
|
|
"""
|
|
from apps.organizations.models import Hospital
|
|
|
|
hospitals = Hospital.objects.all().order_by("name").values("id", "name")
|
|
|
|
return JsonResponse({"success": True, "hospitals": list(hospitals)})
|
|
|
|
|
|
@require_GET
|
|
def set_language(request):
|
|
"""
|
|
Set's language preference for the session and cookie.
|
|
|
|
Stores the selected language in session and sets a persistent cookie
|
|
for better reliability across page reloads and incognito sessions.
|
|
"""
|
|
from django.conf import settings
|
|
from django.utils import translation
|
|
from urllib.parse import urlparse
|
|
|
|
language = request.GET.get("language", "en")
|
|
|
|
# Validate language code
|
|
if language not in dict(settings.LANGUAGES):
|
|
language = "en"
|
|
|
|
# Activate and store the language in session
|
|
translation.activate(language)
|
|
request.session["django_language"] = language
|
|
|
|
# Explicitly save the session to ensure it persists
|
|
if hasattr(request, "session") and request.session.modified:
|
|
request.session.save()
|
|
|
|
# Get the referring URL or use a default
|
|
next_url = request.META.get("HTTP_REFERER", "/")
|
|
parsed_url = urlparse(next_url)
|
|
|
|
# Keep the path but remove query parameters if needed
|
|
redirect_url = parsed_url.path if parsed_url.path else "/"
|
|
|
|
# If there's no referer, redirect to home or public submit landing
|
|
if next_url == "/" or not next_url:
|
|
redirect_url = "/"
|
|
|
|
# Create response with redirect
|
|
response = redirect(redirect_url)
|
|
|
|
# Also set a persistent cookie as a fallback mechanism
|
|
# This ensures language persists even if session storage fails
|
|
response.set_cookie(
|
|
"django_language",
|
|
language,
|
|
max_age=365 * 24 * 60 * 60, # 1 year
|
|
httponly=False, # Allow JavaScript to read it if needed
|
|
secure=False, # Allow over HTTP for local development
|
|
samesite="Lax", # Standard SameSite policy
|
|
)
|
|
|
|
return response
|
|
|
|
|
|
@require_GET
|
|
def api_observation_categories(request):
|
|
"""
|
|
API endpoint to get observation categories list.
|
|
|
|
Used by public observation form to populate category dropdown.
|
|
"""
|
|
from apps.observations.models import ObservationCategory
|
|
|
|
categories = (
|
|
ObservationCategory.objects.filter(is_active=True)
|
|
.order_by("sort_order", "name_en")
|
|
.values("id", "name_en", "name_ar")
|
|
)
|
|
|
|
return JsonResponse({"success": True, "categories": list(categories)})
|
|
|
|
|
|
@require_POST
|
|
def public_observation_submit(request):
|
|
"""
|
|
Handle public observation submissions.
|
|
|
|
Creates an observation from public submission.
|
|
Returns JSON response with tracking code.
|
|
"""
|
|
from apps.observations.models import Observation, ObservationAttachment, ObservationCategory
|
|
from django.shortcuts import get_object_or_404
|
|
from apps.observations.services import ObservationService
|
|
import mimetypes
|
|
|
|
# Get form data
|
|
category_id = request.POST.get("category")
|
|
severity = request.POST.get("severity", "medium")
|
|
title = request.POST.get("title", "").strip()
|
|
description = request.POST.get("description", "").strip()
|
|
location_text = request.POST.get("location_text", "").strip()
|
|
incident_datetime = request.POST.get("incident_datetime", "")
|
|
reporter_staff_id = request.POST.get("reporter_staff_id", "").strip()
|
|
reporter_name = request.POST.get("reporter_name", "").strip()
|
|
reporter_phone = request.POST.get("reporter_phone", "").strip()
|
|
reporter_email = request.POST.get("reporter_email", "").strip()
|
|
|
|
# Validation
|
|
errors = []
|
|
|
|
if not description:
|
|
errors.append("Description is required")
|
|
|
|
if severity not in ["low", "medium", "high", "critical"]:
|
|
errors.append("Invalid severity selected")
|
|
|
|
if errors:
|
|
return JsonResponse({"success": False, "errors": errors}, status=400)
|
|
|
|
try:
|
|
category = None
|
|
if category_id:
|
|
category = get_object_or_404(ObservationCategory, id=category_id)
|
|
|
|
# Get client info
|
|
def get_client_ip(req):
|
|
x_forwarded_for = req.META.get("HTTP_X_FORWARDED_FOR")
|
|
if x_forwarded_for:
|
|
ip = x_forwarded_for.split(",")[0].strip()
|
|
else:
|
|
ip = req.META.get("REMOTE_ADDR")
|
|
return ip
|
|
|
|
client_ip = get_client_ip(request)
|
|
user_agent = request.META.get("HTTP_USER_AGENT", "")
|
|
|
|
# Handle file uploads
|
|
attachments = request.FILES.getlist("attachments")
|
|
|
|
# Create observation using service
|
|
observation = ObservationService.create_observation(
|
|
description=description,
|
|
severity=severity,
|
|
category=category,
|
|
title=title,
|
|
location_text=location_text,
|
|
incident_datetime=incident_datetime if incident_datetime else None,
|
|
reporter_staff_id=reporter_staff_id,
|
|
reporter_name=reporter_name,
|
|
reporter_phone=reporter_phone,
|
|
reporter_email=reporter_email,
|
|
client_ip=client_ip,
|
|
user_agent=user_agent,
|
|
attachments=attachments,
|
|
)
|
|
|
|
return JsonResponse(
|
|
{"success": True, "tracking_code": observation.tracking_code, "observation_id": str(observation.id)}
|
|
)
|
|
|
|
except Exception as e:
|
|
return JsonResponse({"success": False, "errors": [str(e)]}, status=500)
|