""" 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, "display_name": hospital.get_display_name(), "display_name_ar": hospital.get_display_name_ar(), "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)