418 lines
14 KiB
Python
418 lines
14 KiB
Python
"""
|
|
Accounts UI views - Handle HTML rendering for onboarding
|
|
"""
|
|
from django.shortcuts import redirect, render
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib.auth import get_user_model
|
|
from django.utils import timezone
|
|
from django.views.decorators.http import require_http_methods
|
|
from django.http import JsonResponse
|
|
from django.contrib import messages
|
|
|
|
from .models import (
|
|
AcknowledgementContent,
|
|
AcknowledgementChecklistItem,
|
|
UserAcknowledgement,
|
|
)
|
|
from .permissions import IsPXAdmin, CanManageOnboarding, CanViewOnboarding
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
# ==================== Onboarding Wizard Views ====================
|
|
|
|
def onboarding_welcome(request, token=None):
|
|
"""
|
|
Welcome page for onboarding wizard
|
|
"""
|
|
# If user is already authenticated and not provisional, redirect to dashboard
|
|
if request.user.is_authenticated and not request.user.is_provisional:
|
|
return redirect('/')
|
|
|
|
context = {
|
|
'page_title': 'Welcome to PX360',
|
|
}
|
|
return render(request, 'accounts/onboarding/welcome.html', context)
|
|
|
|
|
|
@login_required
|
|
def onboarding_step_content(request, step):
|
|
"""
|
|
Display content step of the onboarding wizard
|
|
"""
|
|
user = request.user
|
|
|
|
# Check if user is provisional
|
|
if not user.is_provisional:
|
|
return redirect('/')
|
|
|
|
# Get content for user's role
|
|
content_list = get_wizard_content_for_user(user)
|
|
|
|
# Get current step content
|
|
try:
|
|
current_content = content_list[step - 1]
|
|
except IndexError:
|
|
# Step doesn't exist, go to checklist
|
|
return redirect('/accounts/onboarding/wizard/checklist/')
|
|
|
|
# Get completed steps
|
|
completed_steps = user.wizard_completed_steps or []
|
|
|
|
# Calculate progress
|
|
progress_percentage = int((len(completed_steps) / len(content_list)) * 100)
|
|
|
|
# Get previous and next steps
|
|
previous_step = step - 1 if step > 1 else None
|
|
next_step = step + 1 if step < len(content_list) else None
|
|
|
|
context = {
|
|
'page_title': f'Onboarding - Step {step}',
|
|
'step': step,
|
|
'content': content_list,
|
|
'current_content': current_content,
|
|
'completed_steps': completed_steps,
|
|
'progress_percentage': progress_percentage,
|
|
'previous_step': previous_step,
|
|
'next_step': next_step,
|
|
'user': user,
|
|
}
|
|
return render(request, 'accounts/onboarding/step_content.html', context)
|
|
|
|
|
|
@login_required
|
|
def onboarding_step_checklist(request):
|
|
"""
|
|
Display checklist step of the onboarding wizard
|
|
"""
|
|
user = request.user
|
|
|
|
# Check if user is provisional
|
|
if not user.is_provisional:
|
|
return redirect('/dashboard/')
|
|
|
|
# Get checklist items for user's role
|
|
checklist_items = get_checklist_items_for_user(user)
|
|
|
|
# Get acknowledged items
|
|
acknowledged_ids = UserAcknowledgement.objects.filter(
|
|
user=user,
|
|
is_acknowledged=True
|
|
).values_list('checklist_item_id', flat=True)
|
|
|
|
# Add acknowledgement status to items
|
|
for item in checklist_items:
|
|
item.is_acknowledged = item.id in acknowledged_ids
|
|
|
|
# Get required items IDs
|
|
required_items = [
|
|
str(item.id) for item in checklist_items if item.is_required
|
|
]
|
|
|
|
# Calculate progress
|
|
total_count = len(checklist_items)
|
|
acknowledged_count = len([i for i in checklist_items if i.is_acknowledged])
|
|
progress_percentage = int((acknowledged_count / total_count) * 100) if total_count > 0 else 0
|
|
|
|
context = {
|
|
'page_title': 'Acknowledgement Checklist',
|
|
'checklist_items': checklist_items,
|
|
'acknowledged_count': acknowledged_count,
|
|
'total_count': total_count,
|
|
'progress_percentage': progress_percentage,
|
|
'required_items_json': required_items,
|
|
'user': user,
|
|
}
|
|
return render(request, 'accounts/onboarding/step_checklist.html', context)
|
|
|
|
|
|
@login_required
|
|
def onboarding_step_activation(request):
|
|
"""
|
|
Display account activation step
|
|
"""
|
|
user = request.user
|
|
|
|
# Check if user is provisional
|
|
if not user.is_provisional:
|
|
return redirect('/dashboard/')
|
|
|
|
# Check if all required acknowledgements are completed
|
|
required_items = get_checklist_items_for_user(user).filter(is_required=True)
|
|
acknowledged_items = UserAcknowledgement.objects.filter(
|
|
user=user,
|
|
checklist_item__in=required_items,
|
|
is_acknowledged=True
|
|
)
|
|
|
|
if required_items.count() != acknowledged_items.count():
|
|
messages.warning(request, 'Please complete all required acknowledgements first.')
|
|
return redirect('/accounts/onboarding/wizard/checklist/')
|
|
|
|
context = {
|
|
'page_title': 'Account Activation',
|
|
'user': user,
|
|
}
|
|
return render(request, 'accounts/onboarding/step_activation.html', context)
|
|
|
|
|
|
@login_required
|
|
def onboarding_complete(request):
|
|
"""
|
|
Display completion page
|
|
"""
|
|
user = request.user
|
|
|
|
# Check if user is not provisional (i.e., completed onboarding)
|
|
if user.is_provisional:
|
|
return redirect('/accounts/onboarding/wizard/step/1/')
|
|
|
|
context = {
|
|
'page_title': 'Onboarding Complete',
|
|
'user': user,
|
|
}
|
|
return render(request, 'accounts/onboarding/complete.html', context)
|
|
|
|
|
|
# ==================== Provisional User Management Views ====================
|
|
|
|
@login_required
|
|
@require_http_methods(["GET", "POST"])
|
|
def provisional_user_list(request):
|
|
"""
|
|
List and manage provisional users (PX Admin only)
|
|
"""
|
|
if not request.user.is_px_admin():
|
|
messages.error(request, 'You do not have permission to view this page.')
|
|
return redirect('/dashboard/')
|
|
|
|
if request.method == 'POST':
|
|
# Handle create provisional user
|
|
from .serializers import ProvisionalUserSerializer
|
|
from .services import OnboardingService, EmailService
|
|
|
|
serializer = ProvisionalUserSerializer(data=request.POST)
|
|
if serializer.is_valid():
|
|
user_data = serializer.validated_data.copy()
|
|
roles = request.POST.getlist('roles', [])
|
|
|
|
# Remove roles from user_data (not a User model field)
|
|
user_data.pop('roles', None)
|
|
|
|
# Create provisional user
|
|
user = OnboardingService.create_provisional_user(user_data)
|
|
|
|
# Assign roles
|
|
for role_name in roles:
|
|
try:
|
|
from .models import Role
|
|
role = Role.objects.get(name=role_name)
|
|
user.groups.add(role.group)
|
|
except Role.DoesNotExist:
|
|
pass
|
|
|
|
# Send invitation email
|
|
EmailService.send_invitation_email(user, request)
|
|
|
|
messages.success(request, f'Provisional user {user.email} created successfully.')
|
|
return redirect('accounts:provisional-user-list')
|
|
else:
|
|
messages.error(request, 'Failed to create provisional user. Please check the form.')
|
|
|
|
# Get all provisional users
|
|
provisional_users = User.objects.filter(
|
|
is_provisional=True
|
|
).select_related('hospital', 'department').order_by('-created_at')
|
|
|
|
# Calculate statistics
|
|
total_count = provisional_users.count()
|
|
completed_count = provisional_users.filter(acknowledgement_completed_at__isnull=False).count()
|
|
in_progress_count = total_count - completed_count
|
|
|
|
# Get available roles
|
|
from .models import Role
|
|
roles = Role.objects.all()
|
|
|
|
# Get hospitals and departments for the form
|
|
from apps.organizations.models import Hospital, Department
|
|
hospitals = Hospital.objects.filter(status='active').order_by('name')
|
|
departments = Department.objects.filter(status='active').order_by('name')
|
|
|
|
context = {
|
|
'page_title': 'Provisional Users',
|
|
'provisional_users': provisional_users,
|
|
'roles': roles,
|
|
'hospitals': hospitals,
|
|
'departments': departments,
|
|
'total_count': total_count,
|
|
'completed_count': completed_count,
|
|
'in_progress_count': in_progress_count,
|
|
}
|
|
return render(request, 'accounts/onboarding/provisional_list.html', context)
|
|
|
|
|
|
@login_required
|
|
def provisional_user_progress(request, user_id):
|
|
"""
|
|
View onboarding progress for a specific user
|
|
"""
|
|
if not (request.user.is_px_admin() or request.user.id == user_id):
|
|
messages.error(request, 'You do not have permission to view this page.')
|
|
return redirect('/dashboard/')
|
|
|
|
user = User.objects.get(id=user_id)
|
|
|
|
# Get checklist items
|
|
checklist_items = get_checklist_items_for_user(user)
|
|
|
|
# Get acknowledged items
|
|
acknowledged_items = UserAcknowledgement.objects.filter(
|
|
user=user,
|
|
is_acknowledged=True
|
|
).select_related('checklist_item')
|
|
|
|
# Create a lookup dict: checklist_item_id -> acknowledged_at timestamp
|
|
acknowledged_timestamps = {}
|
|
for ack in acknowledged_items:
|
|
if ack.checklist_item_id:
|
|
acknowledged_timestamps[ack.checklist_item_id] = ack.acknowledged_at
|
|
|
|
# Get logs
|
|
from .models import UserProvisionalLog
|
|
logs = UserProvisionalLog.objects.filter(
|
|
user=user
|
|
).order_by('-created_at')
|
|
|
|
# Calculate progress
|
|
total_items = checklist_items.filter(is_required=True).count()
|
|
acknowledged_count = acknowledged_items.filter(
|
|
checklist_item__is_required=True
|
|
).count()
|
|
progress_percentage = int((acknowledged_count / total_items) * 100) if total_items > 0 else 0
|
|
remaining_count = total_items - acknowledged_count
|
|
|
|
# Attach acknowledged_at timestamp to each checklist item
|
|
checklist_items_with_timestamps = []
|
|
for item in checklist_items:
|
|
item_dict = {
|
|
'item': item,
|
|
'acknowledged_at': acknowledged_timestamps.get(item.id),
|
|
}
|
|
checklist_items_with_timestamps.append(item_dict)
|
|
|
|
context = {
|
|
'page_title': f'Onboarding Progress - {user.email}',
|
|
'user': user,
|
|
'checklist_items': checklist_items_with_timestamps,
|
|
'logs': logs,
|
|
'total_items': total_items,
|
|
'acknowledged_count': acknowledged_count,
|
|
'remaining_count': remaining_count,
|
|
'progress_percentage': progress_percentage,
|
|
}
|
|
return render(request, 'accounts/onboarding/progress_detail.html', context)
|
|
|
|
|
|
# ==================== Acknowledgement Management Views ====================
|
|
|
|
@login_required
|
|
def acknowledgement_content_list(request):
|
|
"""
|
|
List acknowledgement content (PX Admin only)
|
|
"""
|
|
if not request.user.is_px_admin():
|
|
messages.error(request, 'You do not have permission to view this page.')
|
|
return redirect('/dashboard/')
|
|
|
|
# Get all content
|
|
content_list = AcknowledgementContent.objects.all().order_by('role', 'order')
|
|
|
|
context = {
|
|
'page_title': 'Acknowledgement Content',
|
|
'content_list': content_list,
|
|
}
|
|
return render(request, 'accounts/onboarding/content_list.html', context)
|
|
|
|
|
|
@login_required
|
|
def acknowledgement_checklist_list(request):
|
|
"""
|
|
List acknowledgement checklist items (PX Admin only)
|
|
"""
|
|
if not request.user.is_px_admin():
|
|
messages.error(request, 'You do not have permission to view this page.')
|
|
return redirect('/dashboard/')
|
|
|
|
# Get all checklist items
|
|
checklist_items = AcknowledgementChecklistItem.objects.select_related(
|
|
'content'
|
|
).order_by('role', 'order')
|
|
|
|
context = {
|
|
'page_title': 'Acknowledgement Checklist Items',
|
|
'checklist_items': checklist_items,
|
|
}
|
|
return render(request, 'accounts/onboarding/checklist_list.html', context)
|
|
|
|
|
|
# ==================== Helper Functions ====================
|
|
|
|
def get_wizard_content_for_user(user):
|
|
"""
|
|
Get wizard content based on user's role
|
|
"""
|
|
from .models import Role
|
|
|
|
# Get user's role
|
|
user_role = None
|
|
if user.groups.filter(name='PX Admin').exists():
|
|
user_role = 'px_admin'
|
|
elif user.groups.filter(name='Hospital Admin').exists():
|
|
user_role = 'hospital_admin'
|
|
elif user.groups.filter(name='Department Manager').exists():
|
|
user_role = 'department_manager'
|
|
elif user.groups.filter(name='Staff').exists():
|
|
user_role = 'staff'
|
|
elif user.groups.filter(name='Physician').exists():
|
|
user_role = 'physician'
|
|
|
|
# Get content for role or general content
|
|
if user_role:
|
|
content = AcknowledgementContent.objects.filter(
|
|
role__in=[user_role, 'all']
|
|
)
|
|
else:
|
|
content = AcknowledgementContent.objects.filter(role='all')
|
|
|
|
return content.filter(is_active=True).order_by('order')
|
|
|
|
|
|
def get_checklist_items_for_user(user):
|
|
"""
|
|
Get checklist items based on user's role
|
|
"""
|
|
from .models import Role
|
|
|
|
# Get user's role
|
|
user_role = None
|
|
if user.groups.filter(name='PX Admin').exists():
|
|
user_role = 'px_admin'
|
|
elif user.groups.filter(name='Hospital Admin').exists():
|
|
user_role = 'hospital_admin'
|
|
elif user.groups.filter(name='Department Manager').exists():
|
|
user_role = 'department_manager'
|
|
elif user.groups.filter(name='Staff').exists():
|
|
user_role = 'staff'
|
|
elif user.groups.filter(name='Physician').exists():
|
|
user_role = 'physician'
|
|
|
|
# Get checklist items for role or general items
|
|
if user_role:
|
|
items = AcknowledgementChecklistItem.objects.filter(
|
|
role__in=[user_role, 'all']
|
|
)
|
|
else:
|
|
items = AcknowledgementChecklistItem.objects.filter(role='all')
|
|
|
|
return items.filter(is_active=True).order_by('order')
|