HH/apps/core/views.py

364 lines
11 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
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
# ============================================================================
@require_GET
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
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
send_mail(
f'New Public Inquiry - {reference_number}',
f'Inquiry from {name}\n\nSubject: {subject}\n\nMessage:\n{message}',
settings.DEFAULT_FROM_EMAIL,
[settings.DEFAULT_FROM_EMAIL], # Send to admin
fail_silently=True,
)
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.
Stores the selected language in session and redirects back to referring page.
"""
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
translation.activate(language)
request.session['django_language'] = language
# 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 = '/'
return redirect(redirect_url)
@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
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:
# 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_id=category_id if category_id else None,
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)