305 lines
10 KiB
Python
305 lines
10 KiB
Python
"""
|
|
Configuration Console UI views - System configuration management
|
|
"""
|
|
|
|
import json
|
|
|
|
from django.contrib import messages
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.core.paginator import Paginator
|
|
from django.db.models import Q
|
|
from django.http import JsonResponse
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
from django.template.loader import render_to_string
|
|
from django.conf import settings
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from apps.organizations.models import Department, Hospital
|
|
from apps.organizations.services import StaffService
|
|
from apps.px_action_center.models import PXActionSLAConfig, RoutingRule
|
|
from apps.complaints.models import OnCallAdminSchedule
|
|
from apps.callcenter.models import CallRecord
|
|
from apps.notifications.services import NotificationService
|
|
from apps.core.decorators import px_admin_required, admin_required
|
|
from apps.accounts.models import User
|
|
|
|
|
|
@px_admin_required
|
|
def config_dashboard(request):
|
|
"""Configuration dashboard - overview of system settings - PX Admin only"""
|
|
|
|
# Get counts
|
|
sla_configs_count = PXActionSLAConfig.objects.filter(is_active=True).count()
|
|
routing_rules_count = RoutingRule.objects.filter(is_active=True).count()
|
|
hospitals_count = Hospital.objects.filter(status="active").count()
|
|
oncall_schedules_count = OnCallAdminSchedule.objects.filter(is_active=True).count()
|
|
call_records_count = CallRecord.objects.count()
|
|
provisional_users_count = User.objects.filter(is_provisional=True).count()
|
|
active_users_count = User.objects.filter(is_active=True, is_superuser=False, is_provisional=False).count()
|
|
|
|
context = {
|
|
"sla_configs_count": sla_configs_count,
|
|
"routing_rules_count": routing_rules_count,
|
|
"hospitals_count": hospitals_count,
|
|
"oncall_schedules_count": oncall_schedules_count,
|
|
"call_records_count": call_records_count,
|
|
"provisional_users_count": provisional_users_count,
|
|
"active_users_count": active_users_count,
|
|
}
|
|
|
|
return render(request, "config/dashboard.html", context)
|
|
|
|
|
|
@px_admin_required
|
|
def sla_config_list(request):
|
|
"""SLA configurations list view - PX Admin only"""
|
|
|
|
queryset = PXActionSLAConfig.objects.select_related("hospital", "department")
|
|
|
|
# Apply filters
|
|
hospital_filter = request.GET.get("hospital")
|
|
if hospital_filter:
|
|
queryset = queryset.filter(hospital_id=hospital_filter)
|
|
|
|
is_active = request.GET.get("is_active")
|
|
if is_active == "true":
|
|
queryset = queryset.filter(is_active=True)
|
|
elif is_active == "false":
|
|
queryset = queryset.filter(is_active=False)
|
|
|
|
# Ordering
|
|
queryset = queryset.order_by("hospital", "name")
|
|
|
|
# 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)
|
|
|
|
# Get hospitals for filter
|
|
hospitals = Hospital.objects.filter(status="active")
|
|
|
|
context = {
|
|
"page_obj": page_obj,
|
|
"sla_configs": page_obj.object_list,
|
|
"hospitals": hospitals,
|
|
"filters": request.GET,
|
|
}
|
|
|
|
return render(request, "config/sla_config.html", context)
|
|
|
|
|
|
@px_admin_required
|
|
def routing_rules_list(request):
|
|
"""Routing rules list view - PX Admin only"""
|
|
|
|
queryset = RoutingRule.objects.select_related("hospital", "department", "assign_to_user", "assign_to_department")
|
|
|
|
# Apply filters
|
|
hospital_filter = request.GET.get("hospital")
|
|
if hospital_filter:
|
|
queryset = queryset.filter(hospital_id=hospital_filter)
|
|
|
|
is_active = request.GET.get("is_active")
|
|
if is_active == "true":
|
|
queryset = queryset.filter(is_active=True)
|
|
elif is_active == "false":
|
|
queryset = queryset.filter(is_active=False)
|
|
|
|
# Ordering
|
|
queryset = queryset.order_by("-priority", "name")
|
|
|
|
# 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)
|
|
|
|
# Get hospitals for filter
|
|
hospitals = Hospital.objects.filter(status="active")
|
|
|
|
context = {
|
|
"page_obj": page_obj,
|
|
"routing_rules": page_obj.object_list,
|
|
"hospitals": hospitals,
|
|
"filters": request.GET,
|
|
}
|
|
|
|
return render(request, "config/routing_rules.html", context)
|
|
|
|
|
|
@admin_required
|
|
def hospital_users_list(request):
|
|
"""Hospital users list view - PX Admin and Hospital Admin"""
|
|
|
|
queryset = User.objects.select_related("hospital", "department").filter(is_superuser=False, is_provisional=False)
|
|
|
|
if not request.user.is_px_admin():
|
|
if request.tenant_hospital:
|
|
queryset = queryset.filter(hospital=request.tenant_hospital)
|
|
else:
|
|
queryset = queryset.none()
|
|
|
|
hospital_filter = request.GET.get("hospital")
|
|
if hospital_filter:
|
|
queryset = queryset.filter(hospital_id=hospital_filter)
|
|
|
|
role_filter = request.GET.get("role")
|
|
if role_filter:
|
|
queryset = queryset.filter(groups__name=role_filter)
|
|
|
|
is_active = request.GET.get("is_active")
|
|
if is_active == "true":
|
|
queryset = queryset.filter(is_active=True)
|
|
elif is_active == "false":
|
|
queryset = queryset.filter(is_active=False)
|
|
|
|
search_query = request.GET.get("search")
|
|
if search_query:
|
|
queryset = queryset.filter(
|
|
Q(first_name__icontains=search_query)
|
|
| Q(last_name__icontains=search_query)
|
|
| Q(email__icontains=search_query)
|
|
| Q(employee_id__icontains=search_query)
|
|
)
|
|
|
|
queryset = queryset.order_by("-date_joined")
|
|
|
|
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)
|
|
|
|
if request.user.is_px_admin():
|
|
hospitals = Hospital.objects.filter(status="active").order_by("name")
|
|
elif request.tenant_hospital:
|
|
hospitals = Hospital.objects.filter(id=request.tenant_hospital.id)
|
|
else:
|
|
hospitals = Hospital.objects.none()
|
|
|
|
from django.contrib.auth.models import Group
|
|
|
|
roles = Group.objects.all().order_by("name")
|
|
|
|
total_users = paginator.count
|
|
active_count = queryset.filter(is_active=True).count()
|
|
inactive_count = queryset.filter(is_active=False).count()
|
|
|
|
context = {
|
|
"page_obj": page_obj,
|
|
"users": page_obj.object_list,
|
|
"hospitals": hospitals,
|
|
"roles": roles,
|
|
"filters": request.GET,
|
|
"total_users": total_users,
|
|
"active_count": active_count,
|
|
"inactive_count": inactive_count,
|
|
}
|
|
|
|
return render(request, "config/hospital_users.html", context)
|
|
|
|
|
|
@admin_required
|
|
def reset_user_password(request, user_id):
|
|
"""Reset a user's password and send them the new credentials via email."""
|
|
|
|
if request.method != "POST":
|
|
return JsonResponse({"error": "Method not allowed"}, status=405)
|
|
|
|
target_user = get_object_or_404(User, pk=user_id, is_superuser=False)
|
|
|
|
if not request.user.is_px_admin():
|
|
if request.tenant_hospital and target_user.hospital != request.tenant_hospital:
|
|
return JsonResponse({"error": "You can only reset passwords for users in your hospital."}, status=403)
|
|
|
|
new_password = StaffService.generate_password()
|
|
target_user.set_password(new_password)
|
|
target_user.save(update_fields=["password"])
|
|
|
|
login_url = f"{request.scheme}://{request.get_host()}/accounts/login/"
|
|
|
|
html_message = render_to_string(
|
|
"config/emails/reset_password_email.html",
|
|
{
|
|
"user": target_user,
|
|
"password": new_password,
|
|
"login_url": login_url,
|
|
},
|
|
request=request,
|
|
)
|
|
|
|
plain_message = (
|
|
f"Dear {target_user.get_full_name()},\n\n"
|
|
f"Your password has been reset by an administrator.\n\n"
|
|
f"Your new credentials:\n"
|
|
f"Email: {target_user.email}\n"
|
|
f"Password: {new_password}\n\n"
|
|
f"Please login and change your password immediately.\n"
|
|
f"Login URL: {login_url}"
|
|
)
|
|
|
|
NotificationService.send_email(
|
|
email=target_user.email,
|
|
subject=_("Your PX360 Password Has Been Reset"),
|
|
message=plain_message,
|
|
html_message=html_message,
|
|
user=target_user,
|
|
notification_type="system",
|
|
)
|
|
|
|
return JsonResponse(
|
|
{
|
|
"success": True,
|
|
"message": f"Password has been reset for {target_user.get_full_name()}. A new password has been sent to {target_user.email}.",
|
|
"password": new_password,
|
|
"user_name": target_user.get_full_name(),
|
|
"user_email": target_user.email,
|
|
}
|
|
)
|
|
|
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from rich import print
|
|
from django.contrib.auth.hashers import check_password as verify_password
|
|
|
|
|
|
@admin_required
|
|
def toggle_user_active(request, user_id):
|
|
if request.method != "POST":
|
|
return JsonResponse({"error": "Method not allowed"}, status=405)
|
|
|
|
target_user = get_object_or_404(User, pk=user_id, is_superuser=False, is_provisional=False)
|
|
|
|
if not request.user.is_px_admin():
|
|
return JsonResponse({"error": "Only PX Admins can activate/deactivate users."}, status=403)
|
|
|
|
data = json.loads(request.body) if request.body else {}
|
|
password = data.get("password", "")
|
|
|
|
if not verify_password(password, request.user.password):
|
|
return JsonResponse({"error": "Incorrect password. Please try again."}, status=400)
|
|
|
|
target_user.is_active = not target_user.is_active
|
|
target_user.save(update_fields=["is_active"])
|
|
|
|
status_label = "activated" if target_user.is_active else "deactivated"
|
|
|
|
return JsonResponse(
|
|
{
|
|
"success": True,
|
|
"is_active": target_user.is_active,
|
|
"message": f"User {target_user.get_full_name()} has been {status_label}.",
|
|
"user_name": target_user.get_full_name(),
|
|
}
|
|
)
|
|
|
|
|
|
@csrf_exempt
|
|
def test(request):
|
|
import json
|
|
from django.http import JsonResponse
|
|
|
|
print(json.loads(request.body))
|
|
|
|
return JsonResponse({"status": "ok"})
|