""" Appreciation UI views - Server-rendered templates for appreciation management """ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import csrf_exempt from django.contrib.contenttypes.models import ContentType from django.core.paginator import Paginator from django.db.models import Q, Count from django.http import JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.utils import timezone from django.utils.translation import gettext as _ from django.views.decorators.http import require_http_methods from apps.accounts.models import User from apps.accounts.services import StaffActivityService from apps.core.services import AuditService from apps.organizations.models import Department, Hospital, Staff from .models import ( Appreciation, AppreciationBadge, AppreciationCategory, AppreciationStatus, AppreciationVisibility, AppreciationStats, UserBadge, ) # ============================================================================ # APPRECIATION LIST & DETAIL VIEWS # ============================================================================ @login_required def appreciation_list(request): queryset = Appreciation.objects.select_related( "hospital", "department", "category" ).order_by("-created_at") user = request.user if user.is_px_admin(): pass elif user.hospital: queryset = queryset.filter(hospital=user.hospital) else: queryset = queryset.none() status_filter_val = request.GET.get("status", "") if status_filter_val: queryset = queryset.filter(status=status_filter_val) search_query = request.GET.get("search", "").strip() if search_query: queryset = queryset.filter( Q(message_en__icontains=search_query) | Q(message_ar__icontains=search_query) | Q(metadata__submitted_by_name__icontains=search_query) ) base_qs = Appreciation.objects.all() if not user.is_px_admin() and user.hospital: base_qs = base_qs.filter(hospital=user.hospital) elif not user.is_px_admin(): base_qs = base_qs.none() stats = { "total": base_qs.count(), "draft": base_qs.filter(status=AppreciationStatus.DRAFT).count(), "activated": base_qs.filter( status__in=[AppreciationStatus.ACTIVATED, AppreciationStatus.AI_ANALYZED] ).count(), "sent": base_qs.filter(status__in=[AppreciationStatus.SENT, AppreciationStatus.ACKNOWLEDGED]).count(), } paginator = Paginator(queryset, 25) page_number = request.GET.get("page", 1) page_obj = paginator.get_page(page_number) context = { "page_obj": page_obj, "appreciations": page_obj.object_list, "stats": stats, "status_filter": status_filter_val, "search_query": search_query, } return render(request, "appreciation/appreciation_list.html", context) @login_required def appreciation_detail(request, pk): appreciation = get_object_or_404( Appreciation.objects.select_related("hospital", "department", "category"), pk=pk, ) user = request.user if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_management()): if not (user.hospital and appreciation.hospital_id == user.hospital_id): messages.error(request, _("You don't have permission to view this appreciation.")) return redirect("appreciation:appreciation_list") metadata = appreciation.metadata or {} staff_queryset = Staff.objects.filter(status="active").select_related("department").order_by("first_name") if user.hospital and not user.is_px_admin(): staff_queryset = staff_queryset.filter(hospital=user.hospital) departments = Department.objects.filter(status="active").order_by("name") if user.hospital and not user.is_px_admin(): departments = departments.filter(hospital=user.hospital) categories = AppreciationCategory.objects.filter(is_active=True).order_by("order", "name_en") context = { "appreciation": appreciation, "metadata": metadata, "staff_list": staff_queryset, "departments": departments, "categories": categories, "can_activate": appreciation.status == AppreciationStatus.DRAFT, "can_send": appreciation.status in (AppreciationStatus.ACTIVATED, AppreciationStatus.AI_ANALYZED), "is_recipient": False, } return render(request, "appreciation/appreciation_detail.html", context) @login_required @require_http_methods(["POST"]) def appreciation_activate(request, pk): appreciation = get_object_or_404(Appreciation, pk=pk) if appreciation.status != AppreciationStatus.DRAFT: messages.error(request, _("This appreciation has already been processed.")) return redirect("appreciation:appreciation_detail", pk=appreciation.pk) user = request.user if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_management()): if not (user.hospital and appreciation.hospital_id == user.hospital_id): messages.error(request, _("Permission denied.")) return redirect("appreciation:appreciation_list") staff_id = request.POST.get("staff") department_id = request.POST.get("department") category_id = request.POST.get("category") if not staff_id: messages.error(request, _("Please select a staff member before activating.")) return redirect("appreciation:appreciation_detail", pk=appreciation.pk) staff = Staff.objects.filter(id=staff_id).select_related("department", "user").first() if staff: if staff.user: user_ct = ContentType.objects.get_for_model(staff.user) appreciation.recipient_content_type = user_ct appreciation.recipient_object_id = staff.user.id else: staff_ct = ContentType.objects.get_for_model(staff) appreciation.recipient_content_type = staff_ct appreciation.recipient_object_id = staff.id if not department_id and staff.department: department_id = str(staff.department.id) if department_id: appreciation.department_id = department_id if category_id: appreciation.category_id = category_id appreciation.status = AppreciationStatus.ACTIVATED appreciation.activated_at = timezone.now() appreciation.activated_by = user appreciation.save() StaffActivityService.log_from_request( request, activity_type="create", description=f"Activated appreciation {appreciation.pk}", content_object=appreciation, module="appreciation", ) AuditService.log_event( event_type="appreciation_activated", description=f"Appreciation {appreciation.pk} activated by {user.get_full_name()}", content_object=appreciation, ) try: from apps.core.ai_service import AIService analysis_result = AIService.chat_completion( messages=[ { "role": "system", "content": "You are analyzing patient appreciation messages. Always respond with valid JSON.", }, { "role": "user", "content": f'Analyze this patient appreciation message and provide:\n' f'1. A summary of what the patient appreciated (in English and Arabic)\n' f'2. Key themes mentioned\n' f'3. Suggested category if not already set\n' f'4. Tone analysis\n\n' f'Message: "{appreciation.message_en}"\n\n' f'Respond in JSON format with keys:\n' f'- summary_en: English summary\n' f'- summary_ar: Arabic summary\n' f'- themes: List of key themes\n' f'- tone: "warm", "formal", or "casual"\n' f'- suggested_response_en: Suggested response in English\n' f'- suggested_response_ar: Suggested response in Arabic', }, ], response_format={"type": "json_object"}, ) import json analysis_data = json.loads(analysis_result) appreciation.mark_ai_analyzed(analysis_data) except Exception as e: import logging logging.getLogger(__name__).error(f"AI analysis failed for appreciation {appreciation.pk}: {str(e)}") appreciation.status = AppreciationStatus.AI_ANALYZED appreciation.ai_analyzed_at = timezone.now() appreciation.save(update_fields=["status", "ai_analyzed_at"]) messages.success(request, _("Appreciation activated and AI analysis complete.")) return redirect("appreciation:appreciation_detail", pk=appreciation.pk) @login_required @require_http_methods(["POST"]) def appreciation_send(request, pk): appreciation = get_object_or_404(Appreciation, pk=pk) if appreciation.status not in (AppreciationStatus.ACTIVATED, AppreciationStatus.AI_ANALYZED): messages.error(request, _("This appreciation must be activated before sending.")) return redirect("appreciation:appreciation_detail", pk=pk) user = request.user if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_management()): if not (user.hospital and appreciation.hospital_id == user.hospital_id): messages.error(request, _("Permission denied.")) return redirect("appreciation:appreciation_list") send_to_manager = request.POST.get("send_to_manager") == "on" send_to_department = request.POST.get("send_to_department") == "on" custom_message = request.POST.get("custom_message", "").strip() cc_emails = request.POST.get("cc_emails", "").strip() appreciation.send_to_manager = send_to_manager appreciation.send_to_department = send_to_department appreciation.custom_message = custom_message cc_list = [e.strip() for e in cc_emails.split(",") if e.strip()] if cc_emails else [] appreciation.cc_list = cc_list appreciation.send() StaffActivityService.log_from_request( request, activity_type="send", description=f"Sent appreciation {appreciation.pk} to {appreciation.get_recipient_name()}", content_object=appreciation, module="appreciation", ) AuditService.log_event( event_type="appreciation_sent", description=f"Appreciation {appreciation.pk} sent to {appreciation.get_recipient_name()}", user=user, content_object=appreciation, metadata={ "send_to_manager": send_to_manager, "send_to_department": send_to_department, "cc_count": len(cc_list), }, ) messages.success(request, _("Appreciation sent successfully.")) return redirect("appreciation:appreciation_detail", pk=pk) # ============================================================================ # ACKNOWLEDGE # ============================================================================ @login_required @require_http_methods(["POST"]) def appreciation_acknowledge(request, pk): """Acknowledge appreciation""" appreciation = get_object_or_404(Appreciation, pk=pk) # Check if user is recipient user_content_type = ContentType.objects.get_for_model(request.user) if not ( appreciation.recipient_content_type == user_content_type and appreciation.recipient_object_id == request.user.id ): messages.error(request, "You can only acknowledge appreciations sent to you.") return redirect('appreciation:appreciation_detail', pk=pk) # Acknowledge appreciation.acknowledge() messages.success(request, "Appreciation acknowledged successfully.") return redirect('appreciation:appreciation_detail', pk=pk) # ============================================================================ # LEADERBOARD VIEWS # ============================================================================ @login_required def leaderboard_view(request): """ Appreciation leaderboard view. Features: - Monthly rankings - Hospital and department filters - Top recipients with badges """ user = request.user # Get date range now = timezone.now() year = int(request.GET.get('year', now.year)) month = int(request.GET.get('month', now.month)) # Build base query queryset = AppreciationStats.objects.filter(year=year, month=month) # Apply RBAC if not user.is_px_admin() and user.hospital: queryset = queryset.filter(hospital=user.hospital) # Apply filters hospital_filter = request.GET.get('hospital') if hospital_filter: queryset = queryset.filter(hospital_id=hospital_filter) department_filter = request.GET.get('department') if department_filter: queryset = queryset.filter(department_id=department_filter) # Order by received count queryset = queryset.order_by('-received_count') # Pagination page_size = int(request.GET.get('page_size', 50)) paginator = Paginator(queryset, page_size) page_number = request.GET.get('page', 1) page_obj = paginator.get_page(page_number) departments = Department.objects.filter(status='active') if not user.is_px_admin() and user.hospital: departments = departments.filter(hospital=user.hospital) # Get months for filter months = [(i, timezone.datetime(year=year, month=i, day=1).strftime('%B')) for i in range(1, 13)] years = range(now.year - 1, now.year + 2) context = { 'page_obj': page_obj, 'leaderboard': page_obj.object_list, 'departments': departments, 'months': months, 'years': years, 'selected_year': year, 'selected_month': month, 'filters': request.GET, } return render(request, 'appreciation/leaderboard.html', context) @login_required def my_badges_view(request): """ User's badges view. Features: - All earned badges - Badge details and criteria - Progress toward next badges """ user = request.user user_content_type = ContentType.objects.get_for_model(user) # Get user's badges queryset = UserBadge.objects.filter( recipient_content_type=user_content_type, recipient_object_id=user.id ).select_related('badge').order_by('-earned_at') # Pagination page_size = int(request.GET.get('page_size', 20)) paginator = Paginator(queryset, page_size) page_number = request.GET.get('page', 1) page_obj = paginator.get_page(page_number) # Get available badges for progress tracking available_badges = AppreciationBadge.objects.filter(is_active=True) if not request.user.is_px_admin() and request.user.hospital: available_badges = available_badges.filter(Q(hospital_id=request.user.hospital.id) | Q(hospital__isnull=True)) # Calculate progress for each badge badge_progress = [] total_received = Appreciation.objects.filter( recipient_content_type=user_content_type, recipient_object_id=user.id ).count() for badge in available_badges: earned = queryset.filter(badge=badge).exists() progress = 0 if badge.criteria_type == 'count': progress = min(100, int((total_received / badge.criteria_value) * 100)) badge_progress.append({ 'badge': badge, 'earned': earned, 'progress': progress, }) context = { 'page_obj': page_obj, 'badges': page_obj.object_list, 'total_received': total_received, 'badge_progress': badge_progress, } return render(request, 'appreciation/my_badges.html', context) # ============================================================================ # ADMIN: CATEGORY MANAGEMENT # ============================================================================ @login_required def category_list(request): """List and manage appreciation categories""" user = request.user # Check permission if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to manage categories.") return redirect('appreciation:appreciation_list') # Base queryset queryset = AppreciationCategory.objects.annotate( appreciation_count=Count('appreciations'), ) # Apply RBAC if not user.is_px_admin() and user.hospital: queryset = queryset.filter(Q(hospital_id=user.hospital.id) | Q(hospital__isnull=True)) # Search search_query = request.GET.get('search') if search_query: queryset = queryset.filter( Q(name_en__icontains=search_query) | Q(name_ar__icontains=search_query) | Q(code__icontains=search_query) ) # Ordering queryset = queryset.order_by('order', 'code') # Pagination page_size = int(request.GET.get('page_size', 25)) paginator = Paginator(queryset, page_size) page_number = request.GET.get('page', 1) page_obj = paginator.get_page(page_number) context = { 'page_obj': page_obj, 'categories': page_obj.object_list, } return render(request, 'appreciation/category_list.html', context) @login_required @require_http_methods(["GET", "POST"]) def category_create(request): """Create appreciation category""" user = request.user # Check permission if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to create categories.") return redirect('appreciation:appreciation_list') if request.method == 'POST': try: code = request.POST.get('code') name_en = request.POST.get('name_en') name_ar = request.POST.get('name_ar', '') description_en = request.POST.get('description_en', '') description_ar = request.POST.get('description_ar', '') icon = request.POST.get('icon', 'fa-heart') color = request.POST.get('color', '#FF5733') order = request.POST.get('order', 0) is_active = request.POST.get('is_active') == 'on' # Get hospital hospital = None if user.is_hospital_admin() and user.hospital: hospital = user.hospital # Validate if not all([code, name_en]): messages.error(request, "Please fill in all required fields.") return redirect('appreciation:category_create') # Create category AppreciationCategory.objects.create( code=code, name_en=name_en, name_ar=name_ar, description_en=description_en, description_ar=description_ar, icon=icon, color=color, order=order, is_active=is_active, hospital=hospital, ) messages.success(request, "Category created successfully.") return redirect('appreciation:category_list') except Exception as e: messages.error(request, f"Error creating category: {str(e)}") return redirect('appreciation:category_create') context = {} return render(request, 'appreciation/admin/category_form.html', context) @login_required @require_http_methods(["GET", "POST"]) def category_edit(request, pk): """Edit appreciation category""" user = request.user # Check permission if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to edit categories.") return redirect('appreciation:appreciation_list') category = get_object_or_404(AppreciationCategory, pk=pk) # Check access if not user.is_px_admin() and category.hospital != user.hospital: messages.error(request, "You don't have permission to edit this category.") return redirect('appreciation:category_list') if request.method == 'POST': try: category.code = request.POST.get('code') category.name_en = request.POST.get('name_en') category.name_ar = request.POST.get('name_ar', '') category.description_en = request.POST.get('description_en', '') category.description_ar = request.POST.get('description_ar', '') category.icon = request.POST.get('icon', 'fa-heart') category.color = request.POST.get('color', '#FF5733') category.order = request.POST.get('order', 0) category.is_active = request.POST.get('is_active') == 'on' category.save() messages.success(request, "Category updated successfully.") return redirect('appreciation:category_list') except Exception as e: messages.error(request, f"Error updating category: {str(e)}") return redirect('appreciation:category_edit', pk=pk) context = { 'category': category, } return render(request, 'appreciation/admin/category_form.html', context) @login_required @require_http_methods(["POST"]) def category_delete(request, pk): """Delete appreciation category""" user = request.user # Check permission if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to delete categories.") return redirect('appreciation:appreciation_list') category = get_object_or_404(AppreciationCategory, pk=pk) # Check if category is in use if Appreciation.objects.filter(category=category).exists(): messages.error(request, "Cannot delete category that is in use.") return redirect('appreciation:category_list') # Log audit AuditService.log_event( event_type='category_deleted', description=f"Appreciation category deleted: {category.name_en}", user=request.user, metadata={'category_code': category.code} ) category.delete() messages.success(request, "Category deleted successfully.") return redirect('appreciation:category_list') # ============================================================================ # ADMIN: BADGE MANAGEMENT # ============================================================================ @login_required def badge_list(request): """List and manage appreciation badges""" user = request.user # Check permission if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to manage badges.") return redirect('appreciation:appreciation_list') # Base queryset queryset = AppreciationBadge.objects.annotate( earned_count=Count('earned_by'), ) # Apply RBAC if not user.is_px_admin() and user.hospital: queryset = queryset.filter(Q(hospital_id=user.hospital.id) | Q(hospital__isnull=True)) # Search search_query = request.GET.get('search') if search_query: queryset = queryset.filter( Q(name_en__icontains=search_query) | Q(name_ar__icontains=search_query) | Q(code__icontains=search_query) ) # Ordering queryset = queryset.order_by('order', 'code') # Pagination page_size = int(request.GET.get('page_size', 25)) paginator = Paginator(queryset, page_size) page_number = request.GET.get('page', 1) page_obj = paginator.get_page(page_number) context = { 'page_obj': page_obj, 'badges': page_obj.object_list, } return render(request, 'appreciation/badge_list.html', context) @login_required @require_http_methods(["GET", "POST"]) def badge_create(request): """Create appreciation badge""" user = request.user # Check permission if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to create badges.") return redirect('appreciation:appreciation_list') if request.method == 'POST': try: code = request.POST.get('code') name_en = request.POST.get('name_en') name_ar = request.POST.get('name_ar', '') description_en = request.POST.get('description_en', '') description_ar = request.POST.get('description_ar', '') icon = request.POST.get('icon', 'fa-award') color = request.POST.get('color', '#FFD700') criteria_type = request.POST.get('criteria_type', 'count') criteria_value = request.POST.get('criteria_value', 5) order = request.POST.get('order', 0) is_active = request.POST.get('is_active') == 'on' # Get hospital hospital = None if user.is_hospital_admin() and user.hospital: hospital = user.hospital # Validate if not all([code, name_en, criteria_value]): messages.error(request, "Please fill in all required fields.") return redirect('appreciation:badge_create') # Create badge AppreciationBadge.objects.create( code=code, name_en=name_en, name_ar=name_ar, description_en=description_en, description_ar=description_ar, icon=icon, color=color, criteria_type=criteria_type, criteria_value=int(criteria_value), order=order, is_active=is_active, hospital=hospital, ) messages.success(request, "Badge created successfully.") return redirect('appreciation:badge_list') except Exception as e: messages.error(request, f"Error creating badge: {str(e)}") return redirect('appreciation:badge_create') context = {} return render(request, 'appreciation/admin/badge_form.html', context) @login_required @require_http_methods(["GET", "POST"]) def badge_edit(request, pk): """Edit appreciation badge""" user = request.user # Check permission if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to edit badges.") return redirect('appreciation:appreciation_list') badge = get_object_or_404(AppreciationBadge, pk=pk) # Check access if not user.is_px_admin() and badge.hospital != user.hospital: messages.error(request, "You don't have permission to edit this badge.") return redirect('appreciation:badge_list') if request.method == 'POST': try: badge.code = request.POST.get('code') badge.name_en = request.POST.get('name_en') badge.name_ar = request.POST.get('name_ar', '') badge.description_en = request.POST.get('description_en', '') badge.description_ar = request.POST.get('description_ar', '') badge.icon = request.POST.get('icon', 'fa-award') badge.color = request.POST.get('color', '#FFD700') badge.criteria_type = request.POST.get('criteria_type', 'count') badge.criteria_value = request.POST.get('criteria_value', 5) badge.order = request.POST.get('order', 0) badge.is_active = request.POST.get('is_active') == 'on' badge.save() messages.success(request, "Badge updated successfully.") return redirect('appreciation:badge_list') except Exception as e: messages.error(request, f"Error updating badge: {str(e)}") return redirect('appreciation:badge_edit', pk=pk) context = { 'badge': badge, } return render(request, 'appreciation/admin/badge_form.html', context) @login_required @require_http_methods(["POST"]) def badge_delete(request, pk): """Delete appreciation badge""" user = request.user # Check permission if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to delete badges.") return redirect('appreciation:appreciation_list') badge = get_object_or_404(AppreciationBadge, pk=pk) # Check if badge is in use if UserBadge.objects.filter(badge=badge).exists(): messages.error(request, "Cannot delete badge that has been earned.") return redirect('appreciation:badge_list') # Log audit AuditService.log_event( event_type='badge_deleted', description=f"Appreciation badge deleted: {badge.name_en}", user=request.user, metadata={'badge_code': badge.code} ) badge.delete() messages.success(request, "Badge deleted successfully.") return redirect('appreciation:badge_list') @login_required @require_http_methods(["POST"]) def appreciation_restore(request, pk): """Restore deleted appreciation""" user = request.user if not (user.is_px_admin() or user.is_hospital_admin()): messages.error(request, "You don't have permission to restore appreciation.") return redirect('config:deleted_items') appreciation = get_object_or_404(Appreciation.all_objects, pk=pk, is_deleted=True) appreciation.restore() messages.success(request, "Appreciation restored successfully.") return redirect('config:deleted_items') # ============================================================================ # AJAX/API HELPERS # ============================================================================ @login_required def get_users_by_hospital(request): """Get users for a hospital (AJAX)""" hospital_id = request.GET.get('hospital_id') if not hospital_id: return JsonResponse({'users': []}) users = User.objects.filter( hospital_id=hospital_id, is_active=True ).values('id', 'first_name', 'last_name') results = [ { 'id': str(u['id']), 'name': f"{u['first_name']} {u['last_name']}", } for u in users ] return JsonResponse({'users': results}) @login_required def get_staff_by_hospital(request): """Get staff for a hospital (AJAX)""" hospital_id = request.GET.get('hospital_id') if not hospital_id: return JsonResponse({'staff': []}) staff = Staff.objects.filter( hospital_id=hospital_id, status='active' ).values('id', 'user__first_name', 'user__last_name') results = [ { 'id': str(s['id']), 'name': f"{s['user__first_name']} {s['user__last_name']}", } for s in staff ] return JsonResponse({'staff': results}) @login_required def get_physicians_by_hospital(request): """Get physicians for a hospital (AJAX)""" hospital_id = request.GET.get('hospital_id') if not hospital_id: return JsonResponse({'physicians': []}) physicians = Staff.objects.filter( hospital_id=hospital_id, status='active', staff_type='physician' ).values('id', 'user__first_name', 'user__last_name') results = [ { 'id': str(p['id']), 'name': f"{p['user__first_name']} {p['user__last_name']}", } for p in physicians ] return JsonResponse({'physicians': results}) @login_required def get_departments_by_hospital(request): """Get departments for a hospital (AJAX)""" hospital_id = request.GET.get('hospital_id') if not hospital_id: return JsonResponse({'departments': []}) departments = Department.objects.filter( hospital_id=hospital_id, status='active' ).values('id', 'name', 'name_ar') results = [ { 'id': str(d['id']), 'name': d['name'], } for d in departments ] return JsonResponse({'departments': results}) @login_required def appreciation_summary_ajax(request): """Get appreciation summary for current user (AJAX)""" user = request.user user_content_type = ContentType.objects.get_for_model(user) now = timezone.now() current_year = now.year current_month = now.month summary = { 'total_received': Appreciation.objects.filter( recipient_content_type=user_content_type, recipient_object_id=user.id ).count(), 'total_sent': Appreciation.objects.filter(sender=user).count(), 'this_month_received': Appreciation.objects.filter( recipient_content_type=user_content_type, recipient_object_id=user.id, sent_at__year=current_year, sent_at__month=current_month ).count(), 'this_month_sent': Appreciation.objects.filter( sender=user, sent_at__year=current_year, sent_at__month=current_month ).count(), 'badges_earned': UserBadge.objects.filter( recipient_content_type=user_content_type, recipient_object_id=user.id ).count(), } # Get hospital rank if user.hospital: stats = AppreciationStats.objects.filter( recipient_content_type=user_content_type, recipient_object_id=user.id, year=current_year, month=current_month ).first() summary['hospital_rank'] = stats.hospital_rank if stats else None return JsonResponse(summary) @csrf_exempt @require_http_methods(["POST"]) def public_appreciation_submit(request): import json import logging logger = logging.getLogger(__name__) try: try: data = json.loads(request.body) if request.content_type == 'application/json' else request.POST except json.JSONDecodeError: data = request.POST contact_name = data.get("contact_name", "").strip() contact_phone = data.get("contact_phone", "").strip() message = data.get("message", "").strip() hospital_id = data.get("hospital", "") staff_name = data.get("staff_name", "").strip() location_id = data.get("location", "").strip() main_section_id = data.get("main_section", "").strip() subsection_id = data.get("subsection", "").strip() if not contact_name or not contact_phone or not message: return JsonResponse({"success": False, "message": "Name, phone, and message are required."}, status=400) if not hospital_id: return JsonResponse({"success": False, "message": "Please select a hospital."}, status=400) try: hospital = Hospital.objects.get(id=hospital_id) except Hospital.DoesNotExist: return JsonResponse({"success": False, "message": "Invalid hospital."}, status=400) from apps.organizations.models import Location, MainSection, SubSection location = Location.objects.filter(id=location_id).first() if location_id else None main_section = MainSection.objects.filter(id=main_section_id).first() if main_section_id else None subsection = SubSection.objects.filter(id=subsection_id).first() if subsection_id else None appreciation = Appreciation( hospital=hospital, location=location, main_section=main_section, subsection=subsection, category=None, message_en=message, message_ar="", is_anonymous=False, status=AppreciationStatus.DRAFT, visibility=AppreciationVisibility.PUBLIC, metadata={ "source": "public_form", "submitted_by_name": contact_name, "submitted_by_phone": contact_phone, "staff_name_mentioned": staff_name, }, ) appreciation.save() try: from apps.complaints.tasks import notify_staff_new_item notify_staff_new_item.delay("appreciation", str(appreciation.id)) except Exception: pass AuditService.log_event( event_type="public_appreciation_submitted", description=f"Public appreciation submitted by {contact_name}", content_object=appreciation, metadata={"hospital": str(hospital.id), "staff_mentioned": staff_name}, ) return JsonResponse({"success": True, "reference": f"APR-{str(appreciation.pk)[:8]}"}) except Exception as e: logger.exception("ERROR in public_appreciation_submit") return JsonResponse({"success": False, "message": str(e)}, status=500)