diff --git a/NorahUniversity/__pycache__/settings.cpython-312.pyc b/NorahUniversity/__pycache__/settings.cpython-312.pyc index ef70d24..48c95df 100644 Binary files a/NorahUniversity/__pycache__/settings.cpython-312.pyc and b/NorahUniversity/__pycache__/settings.cpython-312.pyc differ diff --git a/recruitment/__pycache__/models.cpython-312.pyc b/recruitment/__pycache__/models.cpython-312.pyc index 098b384..fff953c 100644 Binary files a/recruitment/__pycache__/models.cpython-312.pyc and b/recruitment/__pycache__/models.cpython-312.pyc differ diff --git a/recruitment/__pycache__/urls.cpython-312.pyc b/recruitment/__pycache__/urls.cpython-312.pyc index 05d0cff..e468e4d 100644 Binary files a/recruitment/__pycache__/urls.cpython-312.pyc and b/recruitment/__pycache__/urls.cpython-312.pyc differ diff --git a/recruitment/__pycache__/views.cpython-312.pyc b/recruitment/__pycache__/views.cpython-312.pyc index 1b785c1..9e9649a 100644 Binary files a/recruitment/__pycache__/views.cpython-312.pyc and b/recruitment/__pycache__/views.cpython-312.pyc differ diff --git a/recruitment/__pycache__/views_frontend.cpython-312.pyc b/recruitment/__pycache__/views_frontend.cpython-312.pyc index 7c33a4c..4c1b0db 100644 Binary files a/recruitment/__pycache__/views_frontend.cpython-312.pyc and b/recruitment/__pycache__/views_frontend.cpython-312.pyc differ diff --git a/recruitment/models.py b/recruitment/models.py index 5e80ec2..4ed1316 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -679,6 +679,32 @@ class Candidate(Base): return future_meetings or today_future_meetings + @property + def check_and_retry_ai_scoring(self): + """ + Triggers an immediate save ONLY if: + 1. The resume hasn't been parsed yet. + 2. At least 5 minutes have passed since the last attempt. + Returns True if a save was performed, False otherwise. + """ + from datetime import timedelta + + min_delay = timedelta(minutes=5) + + time_since_last_attempt = timezone.now() - self.created_at + + if not self.is_resume_parsed and time_since_last_attempt >= min_delay: + + # 1. Update the retry timestamp + self.last_retry_attempt = timezone.now() + + + self.save() + + return True + + return False + # @property # def time_to_hire(self): # time_to_hire=self.hired_date-self.created_at diff --git a/recruitment/urls.py b/recruitment/urls.py index 49fb42a..9d61e6f 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -34,6 +34,7 @@ urlpatterns = [ path('candidate//view/', views_frontend.candidate_detail, name='candidate_detail'), path('candidate//resume-template/', views_frontend.candidate_resume_template_view, name='candidate_resume_template'), path('candidate//update-stage/', views_frontend.candidate_update_stage, name='candidate_update_stage'), + path('candidate//retry-scoring/', views_frontend.retry_scoring_view, name='candidate_retry_scoring'), # Training URLs path('training/', views_frontend.TrainingListView.as_view(), name='training_list'), @@ -201,23 +202,23 @@ urlpatterns = [ # API URLs for candidate management path('api/candidate//', views.api_candidate_detail, name='api_candidate_detail'), - # Admin Notification API - path('api/admin/notification-count/', views.api_notification_count, name='admin_notification_count'), + # # Admin Notification API + # path('api/admin/notification-count/', views.api_notification_count, name='admin_notification_count'), - # Agency Notification API - path('api/agency/notification-count/', views.api_notification_count, name='api_agency_notification_count'), + # # Agency Notification API + # path('api/agency/notification-count/', views.api_notification_count, name='api_agency_notification_count'), - # SSE Notification Stream - path('api/notifications/stream/', views.notification_stream, name='notification_stream'), + # # SSE Notification Stream + # path('api/notifications/stream/', views.notification_stream, name='notification_stream'), - # Notification URLs - path('notifications/', views.notification_list, name='notification_list'), - path('notifications//', views.notification_detail, name='notification_detail'), - path('notifications//mark-read/', views.notification_mark_read, name='notification_mark_read'), - path('notifications//mark-unread/', views.notification_mark_unread, name='notification_mark_unread'), - path('notifications//delete/', views.notification_delete, name='notification_delete'), - path('notifications/mark-all-read/', views.notification_mark_all_read, name='notification_mark_all_read'), - path('api/notification-count/', views.api_notification_count, name='api_notification_count'), + # # Notification URLs + # path('notifications/', views.notification_list, name='notification_list'), + # path('notifications//', views.notification_detail, name='notification_detail'), + # path('notifications//mark-read/', views.notification_mark_read, name='notification_mark_read'), + # path('notifications//mark-unread/', views.notification_mark_unread, name='notification_mark_unread'), + # path('notifications//delete/', views.notification_delete, name='notification_delete'), + # path('notifications/mark-all-read/', views.notification_mark_all_read, name='notification_mark_all_read'), + # path('api/notification-count/', views.api_notification_count, name='api_notification_count'), #participants urls diff --git a/recruitment/views.py b/recruitment/views.py index b2d40cf..eb4be46 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -2582,314 +2582,314 @@ def agency_delete(request, slug): # Notification Views -@login_required -def notification_list(request): - """List all notifications for the current user""" - # Get filter parameters - status_filter = request.GET.get('status', '') - type_filter = request.GET.get('type', '') +# @login_required +# def notification_list(request): +# """List all notifications for the current user""" +# # Get filter parameters +# status_filter = request.GET.get('status', '') +# type_filter = request.GET.get('type', '') - # Base queryset - notifications = Notification.objects.filter(recipient=request.user).order_by('-created_at') +# # Base queryset +# notifications = Notification.objects.filter(recipient=request.user).order_by('-created_at') - # Apply filters - if status_filter: - if status_filter == 'unread': - notifications = notifications.filter(status=Notification.Status.PENDING) - elif status_filter == 'read': - notifications = notifications.filter(status=Notification.Status.READ) - elif status_filter == 'sent': - notifications = notifications.filter(status=Notification.Status.SENT) +# # Apply filters +# if status_filter: +# if status_filter == 'unread': +# notifications = notifications.filter(status=Notification.Status.PENDING) +# elif status_filter == 'read': +# notifications = notifications.filter(status=Notification.Status.READ) +# elif status_filter == 'sent': +# notifications = notifications.filter(status=Notification.Status.SENT) - if type_filter: - if type_filter == 'in_app': - notifications = notifications.filter(notification_type=Notification.NotificationType.IN_APP) - elif type_filter == 'email': - notifications = notifications.filter(notification_type=Notification.NotificationType.EMAIL) +# if type_filter: +# if type_filter == 'in_app': +# notifications = notifications.filter(notification_type=Notification.NotificationType.IN_APP) +# elif type_filter == 'email': +# notifications = notifications.filter(notification_type=Notification.NotificationType.EMAIL) - # Pagination - paginator = Paginator(notifications, 20) # Show 20 notifications per page - page_number = request.GET.get('page') - page_obj = paginator.get_page(page_number) +# # Pagination +# paginator = Paginator(notifications, 20) # Show 20 notifications per page +# page_number = request.GET.get('page') +# page_obj = paginator.get_page(page_number) - # Statistics - total_notifications = notifications.count() - unread_notifications = notifications.filter(status=Notification.Status.PENDING).count() - email_notifications = notifications.filter(notification_type=Notification.NotificationType.EMAIL).count() +# # Statistics +# total_notifications = notifications.count() +# unread_notifications = notifications.filter(status=Notification.Status.PENDING).count() +# email_notifications = notifications.filter(notification_type=Notification.NotificationType.EMAIL).count() - context = { - 'page_obj': page_obj, - 'total_notifications': total_notifications, - 'unread_notifications': unread_notifications, - 'email_notifications': email_notifications, - 'status_filter': status_filter, - 'type_filter': type_filter, - } - return render(request, 'recruitment/notification_list.html', context) +# context = { +# 'page_obj': page_obj, +# 'total_notifications': total_notifications, +# 'unread_notifications': unread_notifications, +# 'email_notifications': email_notifications, +# 'status_filter': status_filter, +# 'type_filter': type_filter, +# } +# return render(request, 'recruitment/notification_list.html', context) -@login_required -def notification_detail(request, notification_id): - """View details of a specific notification""" - notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) +# @login_required +# def notification_detail(request, notification_id): +# """View details of a specific notification""" +# notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) - # Mark as read if it was pending - if notification.status == Notification.Status.PENDING: - notification.status = Notification.Status.READ - notification.save(update_fields=['status']) +# # Mark as read if it was pending +# if notification.status == Notification.Status.PENDING: +# notification.status = Notification.Status.READ +# notification.save(update_fields=['status']) - context = { - 'notification': notification, - } - return render(request, 'recruitment/notification_detail.html', context) +# context = { +# 'notification': notification, +# } +# return render(request, 'recruitment/notification_detail.html', context) -@login_required -def notification_mark_read(request, notification_id): - """Mark a notification as read""" - notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) +# @login_required +# def notification_mark_read(request, notification_id): +# """Mark a notification as read""" +# notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) - if notification.status == Notification.Status.PENDING: - notification.status = Notification.Status.READ - notification.save(update_fields=['status']) +# if notification.status == Notification.Status.PENDING: +# notification.status = Notification.Status.READ +# notification.save(update_fields=['status']) - if 'HX-Request' in request.headers: - return HttpResponse(status=200) # HTMX success response +# if 'HX-Request' in request.headers: +# return HttpResponse(status=200) # HTMX success response - return redirect('notification_list') +# return redirect('notification_list') -@login_required -def notification_mark_unread(request, notification_id): - """Mark a notification as unread""" - notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) +# @login_required +# def notification_mark_unread(request, notification_id): +# """Mark a notification as unread""" +# notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) - if notification.status == Notification.Status.READ: - notification.status = Notification.Status.PENDING - notification.save(update_fields=['status']) +# if notification.status == Notification.Status.READ: +# notification.status = Notification.Status.PENDING +# notification.save(update_fields=['status']) - if 'HX-Request' in request.headers: - return HttpResponse(status=200) # HTMX success response +# if 'HX-Request' in request.headers: +# return HttpResponse(status=200) # HTMX success response - return redirect('notification_list') +# return redirect('notification_list') -@login_required -def notification_delete(request, notification_id): - """Delete a notification""" - notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) +# @login_required +# def notification_delete(request, notification_id): +# """Delete a notification""" +# notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) - if request.method == 'POST': - notification.delete() - messages.success(request, 'Notification deleted successfully!') - return redirect('notification_list') +# if request.method == 'POST': +# notification.delete() +# messages.success(request, 'Notification deleted successfully!') +# return redirect('notification_list') - # For GET requests, show confirmation page - context = { - 'notification': notification, - 'title': 'Delete Notification', - 'message': f'Are you sure you want to delete this notification?', - 'cancel_url': reverse('notification_detail', kwargs={'notification_id': notification.id}), - } - return render(request, 'recruitment/notification_confirm_delete.html', context) +# # For GET requests, show confirmation page +# context = { +# 'notification': notification, +# 'title': 'Delete Notification', +# 'message': f'Are you sure you want to delete this notification?', +# 'cancel_url': reverse('notification_detail', kwargs={'notification_id': notification.id}), +# } +# return render(request, 'recruitment/notification_confirm_delete.html', context) -@login_required -def notification_mark_all_read(request): - """Mark all notifications as read for the current user""" - if request.method == 'POST': - Notification.objects.filter( - recipient=request.user, - status=Notification.Status.PENDING - ).update(status=Notification.Status.READ) +# @login_required +# def notification_mark_all_read(request): +# """Mark all notifications as read for the current user""" +# if request.method == 'POST': +# Notification.objects.filter( +# recipient=request.user, +# status=Notification.Status.PENDING +# ).update(status=Notification.Status.READ) - messages.success(request, 'All notifications marked as read!') - return redirect('notification_list') +# messages.success(request, 'All notifications marked as read!') +# return redirect('notification_list') - # For GET requests, show confirmation page - unread_count = Notification.objects.filter( - recipient=request.user, - status=Notification.Status.PENDING - ).count() +# # For GET requests, show confirmation page +# unread_count = Notification.objects.filter( +# recipient=request.user, +# status=Notification.Status.PENDING +# ).count() - context = { - 'unread_count': unread_count, - 'title': 'Mark All as Read', - 'message': f'Are you sure you want to mark all {unread_count} notifications as read?', - 'cancel_url': reverse('notification_list'), - } - return render(request, 'recruitment/notification_confirm_all_read.html', context) +# context = { +# 'unread_count': unread_count, +# 'title': 'Mark All as Read', +# 'message': f'Are you sure you want to mark all {unread_count} notifications as read?', +# 'cancel_url': reverse('notification_list'), +# } +# return render(request, 'recruitment/notification_confirm_all_read.html', context) -@login_required -def api_notification_count(request): - """API endpoint to get unread notification count and recent notifications""" - # Get unread notifications - unread_notifications = Notification.objects.filter( - recipient=request.user, - status=Notification.Status.PENDING - ).order_by('-created_at') +# @login_required +# def api_notification_count(request): +# """API endpoint to get unread notification count and recent notifications""" +# # Get unread notifications +# unread_notifications = Notification.objects.filter( +# recipient=request.user, +# status=Notification.Status.PENDING +# ).order_by('-created_at') - # Get recent notifications (last 5) - recent_notifications = Notification.objects.filter( - recipient=request.user - ).order_by('-created_at')[:5] +# # Get recent notifications (last 5) +# recent_notifications = Notification.objects.filter( +# recipient=request.user +# ).order_by('-created_at')[:5] - # Prepare recent notifications data - recent_data = [] - for notification in recent_notifications: - time_ago = '' - if notification.created_at: - from datetime import datetime, timezone - now = timezone.now() - diff = now - notification.created_at +# # Prepare recent notifications data +# recent_data = [] +# for notification in recent_notifications: +# time_ago = '' +# if notification.created_at: +# from datetime import datetime, timezone +# now = timezone.now() +# diff = now - notification.created_at - if diff.days > 0: - time_ago = f'{diff.days}d ago' - elif diff.seconds > 3600: - hours = diff.seconds // 3600 - time_ago = f'{hours}h ago' - elif diff.seconds > 60: - minutes = diff.seconds // 60 - time_ago = f'{minutes}m ago' - else: - time_ago = 'Just now' +# if diff.days > 0: +# time_ago = f'{diff.days}d ago' +# elif diff.seconds > 3600: +# hours = diff.seconds // 3600 +# time_ago = f'{hours}h ago' +# elif diff.seconds > 60: +# minutes = diff.seconds // 60 +# time_ago = f'{minutes}m ago' +# else: +# time_ago = 'Just now' - recent_data.append({ - 'id': notification.id, - 'message': notification.message[:100] + ('...' if len(notification.message) > 100 else ''), - 'type': notification.get_notification_type_display(), - 'status': notification.get_status_display(), - 'time_ago': time_ago, - 'url': reverse('notification_detail', kwargs={'notification_id': notification.id}) - }) +# recent_data.append({ +# 'id': notification.id, +# 'message': notification.message[:100] + ('...' if len(notification.message) > 100 else ''), +# 'type': notification.get_notification_type_display(), +# 'status': notification.get_status_display(), +# 'time_ago': time_ago, +# 'url': reverse('notification_detail', kwargs={'notification_id': notification.id}) +# }) - return JsonResponse({ - 'count': unread_notifications.count(), - 'recent_notifications': recent_data - }) +# return JsonResponse({ +# 'count': unread_notifications.count(), +# 'recent_notifications': recent_data +# }) -@login_required -def notification_stream(request): - """SSE endpoint for real-time notifications""" - from django.http import StreamingHttpResponse - import json - import time - from .signals import SSE_NOTIFICATION_CACHE +# @login_required +# def notification_stream(request): +# """SSE endpoint for real-time notifications""" +# from django.http import StreamingHttpResponse +# import json +# import time +# from .signals import SSE_NOTIFICATION_CACHE - def event_stream(): - """Generator function for SSE events""" - user_id = request.user.id - last_notification_id = 0 +# def event_stream(): +# """Generator function for SSE events""" +# user_id = request.user.id +# last_notification_id = 0 - # Get initial last notification ID - last_notification = Notification.objects.filter( - recipient=request.user - ).order_by('-id').first() - if last_notification: - last_notification_id = last_notification.id +# # Get initial last notification ID +# last_notification = Notification.objects.filter( +# recipient=request.user +# ).order_by('-id').first() +# if last_notification: +# last_notification_id = last_notification.id - # Send any cached notifications first - cached_notifications = SSE_NOTIFICATION_CACHE.get(user_id, []) - for cached_notification in cached_notifications: - if cached_notification['id'] > last_notification_id: - yield f"event: new_notification\n" - yield f"data: {json.dumps(cached_notification)}\n\n" - last_notification_id = cached_notification['id'] +# # Send any cached notifications first +# cached_notifications = SSE_NOTIFICATION_CACHE.get(user_id, []) +# for cached_notification in cached_notifications: +# if cached_notification['id'] > last_notification_id: +# yield f"event: new_notification\n" +# yield f"data: {json.dumps(cached_notification)}\n\n" +# last_notification_id = cached_notification['id'] - while True: - try: - # Check for new notifications from cache first - cached_notifications = SSE_NOTIFICATION_CACHE.get(user_id, []) - new_cached = [n for n in cached_notifications if n['id'] > last_notification_id] +# while True: +# try: +# # Check for new notifications from cache first +# cached_notifications = SSE_NOTIFICATION_CACHE.get(user_id, []) +# new_cached = [n for n in cached_notifications if n['id'] > last_notification_id] - for notification_data in new_cached: - yield f"event: new_notification\n" - yield f"data: {json.dumps(notification_data)}\n\n" - last_notification_id = notification_data['id'] +# for notification_data in new_cached: +# yield f"event: new_notification\n" +# yield f"data: {json.dumps(notification_data)}\n\n" +# last_notification_id = notification_data['id'] - # Also check database for any missed notifications - new_notifications = Notification.objects.filter( - recipient=request.user, - id__gt=last_notification_id - ).order_by('id') +# # Also check database for any missed notifications +# new_notifications = Notification.objects.filter( +# recipient=request.user, +# id__gt=last_notification_id +# ).order_by('id') - if new_notifications.exists(): - for notification in new_notifications: - # Prepare notification data - time_ago = '' - if notification.created_at: - now = timezone.now() - diff = now - notification.created_at +# if new_notifications.exists(): +# for notification in new_notifications: +# # Prepare notification data +# time_ago = '' +# if notification.created_at: +# now = timezone.now() +# diff = now - notification.created_at - if diff.days > 0: - time_ago = f'{diff.days}d ago' - elif diff.seconds > 3600: - hours = diff.seconds // 3600 - time_ago = f'{hours}h ago' - elif diff.seconds > 60: - minutes = diff.seconds // 60 - time_ago = f'{minutes}m ago' - else: - time_ago = 'Just now' +# if diff.days > 0: +# time_ago = f'{diff.days}d ago' +# elif diff.seconds > 3600: +# hours = diff.seconds // 3600 +# time_ago = f'{hours}h ago' +# elif diff.seconds > 60: +# minutes = diff.seconds // 60 +# time_ago = f'{minutes}m ago' +# else: +# time_ago = 'Just now' - notification_data = { - 'id': notification.id, - 'message': notification.message[:100] + ('...' if len(notification.message) > 100 else ''), - 'type': notification.get_notification_type_display(), - 'status': notification.get_status_display(), - 'time_ago': time_ago, - 'url': reverse('notification_detail', kwargs={'notification_id': notification.id}) - } +# notification_data = { +# 'id': notification.id, +# 'message': notification.message[:100] + ('...' if len(notification.message) > 100 else ''), +# 'type': notification.get_notification_type_display(), +# 'status': notification.get_status_display(), +# 'time_ago': time_ago, +# 'url': reverse('notification_detail', kwargs={'notification_id': notification.id}) +# } - # Send SSE event - yield f"event: new_notification\n" - yield f"data: {json.dumps(notification_data)}\n\n" +# # Send SSE event +# yield f"event: new_notification\n" +# yield f"data: {json.dumps(notification_data)}\n\n" - last_notification_id = notification.id +# last_notification_id = notification.id - # Update count after sending new notifications - unread_count = Notification.objects.filter( - recipient=request.user, - status=Notification.Status.PENDING - ).count() +# # Update count after sending new notifications +# unread_count = Notification.objects.filter( +# recipient=request.user, +# status=Notification.Status.PENDING +# ).count() - count_data = {'count': unread_count} - yield f"event: count_update\n" - yield f"data: {json.dumps(count_data)}\n\n" +# count_data = {'count': unread_count} +# yield f"event: count_update\n" +# yield f"data: {json.dumps(count_data)}\n\n" - # Send heartbeat every 30 seconds - yield f"event: heartbeat\n" - yield f"data: {json.dumps({'timestamp': int(time.time())})}\n\n" +# # Send heartbeat every 30 seconds +# yield f"event: heartbeat\n" +# yield f"data: {json.dumps({'timestamp': int(time.time())})}\n\n" - # Wait before next check - time.sleep(5) # Check every 5 seconds +# # Wait before next check +# time.sleep(5) # Check every 5 seconds - except Exception as e: - # Send error event and continue - error_data = {'error': str(e)} - yield f"event: error\n" - yield f"data: {json.dumps(error_data)}\n\n" - time.sleep(10) # Wait longer on error +# except Exception as e: +# # Send error event and continue +# error_data = {'error': str(e)} +# yield f"event: error\n" +# yield f"data: {json.dumps(error_data)}\n\n" +# time.sleep(10) # Wait longer on error - response = StreamingHttpResponse( - event_stream(), - content_type='text/event-stream' - ) +# response = StreamingHttpResponse( +# event_stream(), +# content_type='text/event-stream' +# ) - # Set SSE headers - response['Cache-Control'] = 'no-cache' - response['X-Accel-Buffering'] = 'no' # Disable buffering for nginx - response['Connection'] = 'keep-alive' +# # Set SSE headers +# response['Cache-Control'] = 'no-cache' +# response['X-Accel-Buffering'] = 'no' # Disable buffering for nginx +# response['Connection'] = 'keep-alive' - context = { - 'agency': agency, - 'page_obj': page_obj, - 'stage_filter': stage_filter, - 'total_candidates': candidates.count(), - } - return render(request, 'recruitment/agency_candidates.html', context) + # context = { + # 'agency': agency, + # 'page_obj': page_obj, + # 'stage_filter': stage_filter, + # 'total_candidates': candidates.count(), + # } + # return render(request, 'recruitment/agency_candidates.html', context) @login_required @@ -2976,7 +2976,7 @@ def agency_assignment_create(request,slug=None): try: from django.forms import HiddenInput form.initial['agency'] = agency - form.fields['agency'].widget = HiddenInput() + # form.fields['agency'].widget = HiddenInput() except HiringAgency.DoesNotExist: pass diff --git a/recruitment/views_frontend.py b/recruitment/views_frontend.py index 9b07a52..b403475 100644 --- a/recruitment/views_frontend.py +++ b/recruitment/views_frontend.py @@ -222,12 +222,11 @@ class CandidateDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): slug_url_kwarg = 'slug' -# def job_detail(request, slug): -# job = get_object_or_404(models.JobPosting, slug=slug, status='Published') -# form = forms.CandidateForm() - -# return render(request, 'jobs/job_detail.html', {'job': job, 'form': form}) - +def retry_scoring_view(request,slug): + if request.method == 'POST': + candidate = get_object_or_404(models.Candidate, slug=slug) + candidate.save() + return redirect('candidate_detail', slug=candidate.slug) diff --git a/templates/agency_base.html b/templates/agency_base.html index e5f2451..03b71e3 100644 --- a/templates/agency_base.html +++ b/templates/agency_base.html @@ -9,7 +9,7 @@ {% block title %}{% trans 'KAAUH Agency Portal' %}{% endblock %} - {% comment %} Load correct Bootstrap CSS file for RTL/LTR {% endcomment %} + {# Load correct Bootstrap CSS file for RTL/LTR #} {% if LANGUAGE_CODE == 'ar' %} {% else %} @@ -24,91 +24,94 @@ - {% comment %}
+
-
-
-
-
-
-
+
+
{% trans 'Saudi Vision 2030' %} - -
-
-
جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية
-
ومستشفى الملك عبدالله بن عبدالرحمن التخصصي
-
Princess Nourah bint Abdulrahman University
-
King Abdullah bin Abdulaziz University Hospital
+
+
+
+
+ {% if LANGUAGE_CODE == 'ar' %} + جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية +
+ ومستشفى الملك عبدالله بن عبدالعزيز التخصصي + {% else %} + Princess Nourah bint Abdulrahman University +
+ King Abdullah bin Abdulaziz University Hospital + {% endif %}
- KAAUH Logo + KAAUH Logo
-
{% endcomment %} +
+ + {# Using inline style for nav background color - replace with a dedicated CSS class (e.g., .bg-kaauh-nav) if defined in main.css #} +
+ +
+ {# Messages Block (Correct) #} {% if messages %} {% for message in messages %}
+ {# Footer (Correct) #}