from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator from django.db.models import Q from django.shortcuts import render, redirect, get_object_or_404 from django.contrib import messages from .models import Department, Hospital, Organization, Patient, Staff from .forms import StaffForm @login_required def hospital_list(request): """Hospitals list view""" queryset = Hospital.objects.all() # Apply RBAC filters user = request.user if not user.is_px_admin() and user.hospital: queryset = queryset.filter(id=user.hospital.id) # Apply filters status_filter = request.GET.get('status') if status_filter: queryset = queryset.filter(status=status_filter) # Search search_query = request.GET.get('search') if search_query: queryset = queryset.filter( Q(name__icontains=search_query) | Q(name_ar__icontains=search_query) | Q(code__icontains=search_query) ) # Ordering queryset = queryset.order_by('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) context = { 'page_obj': page_obj, 'hospitals': page_obj.object_list, 'filters': request.GET, } return render(request, 'organizations/hospital_list.html', context) @login_required def department_list(request): """Departments list view""" queryset = Department.objects.select_related('hospital', 'manager') # Apply RBAC filters user = request.user 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) status_filter = request.GET.get('status') if status_filter: queryset = queryset.filter(status=status_filter) # Search search_query = request.GET.get('search') if search_query: queryset = queryset.filter( Q(name__icontains=search_query) | Q(name_ar__icontains=search_query) | Q(code__icontains=search_query) ) # 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') if not user.is_px_admin() and user.hospital: hospitals = hospitals.filter(id=user.hospital.id) context = { 'page_obj': page_obj, 'departments': page_obj.object_list, 'hospitals': hospitals, 'filters': request.GET, } return render(request, 'organizations/department_list.html', context) @login_required def staff_list(request): """Staff list view""" queryset = Staff.objects.select_related('hospital', 'department', 'user') # Apply RBAC filters user = request.user 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) status_filter = request.GET.get('status') if status_filter: queryset = queryset.filter(status=status_filter) staff_type_filter = request.GET.get('staff_type') if staff_type_filter: queryset = queryset.filter(staff_type=staff_type_filter) # Search 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(employee_id__icontains=search_query) | Q(license_number__icontains=search_query) | Q(specialization__icontains=search_query) | Q(job_title__icontains=search_query) ) # Ordering queryset = queryset.order_by('last_name', 'first_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') if not user.is_px_admin() and user.hospital: hospitals = hospitals.filter(id=user.hospital.id) context = { 'page_obj': page_obj, 'staff': page_obj.object_list, 'hospitals': hospitals, 'filters': request.GET, } return render(request, 'organizations/staff_list.html', context) @login_required def organization_list(request): """Organizations list view""" queryset = Organization.objects.all() # Apply RBAC filters user = request.user if not user.is_px_admin() and user.hospital and user.hospital.organization: queryset = queryset.filter(id=user.hospital.organization.id) # Apply filters status_filter = request.GET.get('status') if status_filter: queryset = queryset.filter(status=status_filter) city_filter = request.GET.get('city') if city_filter: queryset = queryset.filter(city__icontains=city_filter) # Search search_query = request.GET.get('search') if search_query: queryset = queryset.filter( Q(name__icontains=search_query) | Q(name_ar__icontains=search_query) | Q(code__icontains=search_query) | Q(license_number__icontains=search_query) ) # Ordering queryset = queryset.order_by('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) context = { 'page_obj': page_obj, 'organizations': page_obj.object_list, 'filters': request.GET, } return render(request, 'organizations/organization_list.html', context) @login_required def organization_detail(request, pk): """Organization detail view""" organization = Organization.objects.get(pk=pk) # Apply RBAC filters user = request.user if not user.is_px_admin(): if user.hospital and user.hospital.organization: if organization.id != user.hospital.organization.id: # User doesn't have access to this organization from django.http import HttpResponseForbidden return HttpResponseForbidden("You don't have permission to view this organization") else: from django.http import HttpResponseForbidden return HttpResponseForbidden("You don't have permission to view this organization") hospitals = organization.hospitals.all() context = { 'organization': organization, 'hospitals': hospitals, } return render(request, 'organizations/organization_detail.html', context) @login_required def organization_create(request): """Create organization view""" # Only PX Admins can create organizations user = request.user if not user.is_px_admin(): from django.http import HttpResponseForbidden return HttpResponseForbidden("Only PX Admins can create organizations") if request.method == 'POST': name = request.POST.get('name') name_ar = request.POST.get('name_ar') code = request.POST.get('code') address = request.POST.get('address', '') city = request.POST.get('city', '') phone = request.POST.get('phone', '') email = request.POST.get('email', '') website = request.POST.get('website', '') license_number = request.POST.get('license_number', '') status = request.POST.get('status', 'active') if name and code: organization = Organization.objects.create( name=name, name_ar=name_ar or name, code=code, address=address, city=city, phone=phone, email=email, website=website, license_number=license_number, status=status ) # Redirect to organization detail from django.shortcuts import redirect return redirect('organizations:organization_detail', pk=organization.id) return render(request, 'organizations/organization_form.html') @login_required def patient_list(request): """Patients list view""" queryset = Patient.objects.select_related('primary_hospital') # Apply RBAC filters user = request.user if not user.is_px_admin() and user.hospital: queryset = queryset.filter(primary_hospital=user.hospital) # Apply filters hospital_filter = request.GET.get('hospital') if hospital_filter: queryset = queryset.filter(primary_hospital_id=hospital_filter) status_filter = request.GET.get('status') if status_filter: queryset = queryset.filter(status=status_filter) # Search search_query = request.GET.get('search') if search_query: queryset = queryset.filter( Q(mrn__icontains=search_query) | Q(first_name__icontains=search_query) | Q(last_name__icontains=search_query) | Q(national_id__icontains=search_query) | Q(phone__icontains=search_query) ) # Ordering queryset = queryset.order_by('last_name', 'first_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') if not user.is_px_admin() and user.hospital: hospitals = hospitals.filter(id=user.hospital.id) context = { 'page_obj': page_obj, 'patients': page_obj.object_list, 'hospitals': hospitals, 'filters': request.GET, } return render(request, 'organizations/patient_list.html', context) @login_required def staff_detail(request, pk): """Staff detail view""" staff = get_object_or_404(Staff.objects.select_related('user', 'hospital', 'department'), pk=pk) # Apply RBAC filters user = request.user if not user.is_px_admin() and staff.hospital != user.hospital: from django.http import HttpResponseForbidden return HttpResponseForbidden("You don't have permission to view this staff member") context = { 'staff': staff, } return render(request, 'organizations/staff_detail.html', context) @login_required def staff_create(request): """Create staff view""" # Only PX Admins and Hospital Admins can create staff user = request.user if not user.is_px_admin() and not user.is_hospital_admin(): from django.http import HttpResponseForbidden return HttpResponseForbidden("You don't have permission to create staff") if request.method == 'POST': form = StaffForm(request.POST) if form.is_valid(): staff = form.save(commit=False) # Handle user account creation create_user = request.POST.get('create_user') == 'on' if create_user and not staff.user and staff.email: from .services import StaffService try: role = StaffService.get_staff_type_role(staff.staff_type) user_account = StaffService.create_user_for_staff( staff, role=role, request=request ) # Generate password for email password = StaffService.generate_password() user_account.set_password(password) user_account.save() try: StaffService.send_credentials_email(staff, password, request) messages.success(request, 'Staff member created and credentials email sent successfully.') except Exception as e: messages.warning(request, f'Staff member created but email sending failed: {str(e)}') except Exception as e: messages.error(request, f'Staff member created but user account creation failed: {str(e)}') staff.save() # Send invitation email if requested if create_user and staff.user and request.POST.get('send_email') != 'false': from .services import StaffService try: password = StaffService.generate_password() staff.user.set_password(password) staff.user.save() StaffService.send_credentials_email(staff, password, request) messages.success(request, 'Credentials email sent successfully.') except Exception as e: messages.warning(request, f'Email sending failed: {str(e)}') messages.success(request, 'Staff member created successfully.') return redirect('organizations:staff_detail', pk=staff.id) else: form = StaffForm(user=request.user) context = { 'form': form, } return render(request, 'organizations/staff_form.html', context) @login_required def staff_update(request, pk): """Update staff view""" staff = get_object_or_404(Staff.objects.select_related('user'), pk=pk) # Apply RBAC filters user = request.user if not user.is_px_admin() and not user.is_hospital_admin(): from django.http import HttpResponseForbidden return HttpResponseForbidden("You don't have permission to update this staff member") if user.is_hospital_admin() and staff.hospital != user.hospital: from django.http import HttpResponseForbidden return HttpResponseForbidden("You don't have permission to update this staff member") if request.method == 'POST': form = StaffForm(request.POST, instance=staff) if form.is_valid(): staff = form.save(commit=False) # Handle user account creation create_user = request.POST.get('create_user') == 'on' if create_user and not staff.user and staff.email: from .services import StaffService try: role = StaffService.get_staff_type_role(staff.staff_type) user_account = StaffService.create_user_for_staff( staff, role=role, request=request ) # Generate password for email password = StaffService.generate_password() user_account.set_password(password) user_account.save() try: StaffService.send_credentials_email(staff, password, request) messages.success(request, 'User account created and credentials email sent.') except Exception as e: messages.warning(request, f'User account created but email sending failed: {str(e)}') except Exception as e: messages.error(request, f'User account creation failed: {str(e)}') staff.save() messages.success(request, 'Staff member updated successfully.') return redirect('organizations:staff_detail', pk=staff.id) else: form = StaffForm(instance=staff, user=request.user) context = { 'form': form, 'staff': staff, } return render(request, 'organizations/staff_form.html', context) @login_required def staff_hierarchy(request): """ Staff hierarchy tree view Shows organizational structure based on report_to relationships """ queryset = Staff.objects.select_related('hospital', 'department', 'report_to') # Apply RBAC filters user = request.user 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) # Search functionality search_query = request.GET.get('search') search_result = None if search_query: try: search_result = Staff.objects.get( Q(employee_id__iexact=search_query) | Q(first_name__icontains=search_query) | Q(last_name__icontains=search_query) ) # If search result exists and user has access, start hierarchy from that staff if search_result and (user.is_px_admin() or search_result.hospital == user.hospital): queryset = Staff.objects.filter( Q(id=search_result.id) | Q(hospital=search_result.hospital) ) except Staff.DoesNotExist: pass # Build hierarchy structure def build_hierarchy(staff_list, parent=None, level=0): """Recursively build hierarchy tree""" result = [] for staff in staff_list: if staff.report_to == parent: node = { 'staff': staff, 'level': level, 'direct_reports': build_hierarchy(staff_list, staff, level + 1), 'has_children': bool(staff.direct_reports.exists()) } result.append(node) return result # Get all staff for the current filter all_staff = list(queryset) # If searching, build hierarchy from search result up if search_result: # Get all managers up the chain manager_chain = [] current = search_result.report_to while current: if current in all_staff: manager_chain.insert(0, current) current = current.report_to # Add search result to chain if search_result not in manager_chain: manager_chain.append(search_result) # Build hierarchy for managers and their reports hierarchy = build_hierarchy(all_staff, parent=None) # Find and highlight search result def find_and_mark(node, target_id, path=None): if path is None: path = [] if node['staff'].id == target_id: node['is_search_result'] = True node['search_path'] = path + [node['staff'].id] return node for child in node['direct_reports']: result = find_and_mark(child, target_id, path + [node['staff'].id]) if result: return result return None search_result_node = None for root in hierarchy: result = find_and_mark(root, search_result.id) if result: search_result_node = result break else: # Build hierarchy starting from top-level (no report_to) hierarchy = build_hierarchy(all_staff, parent=None) # Get hospitals for filter hospitals = Hospital.objects.filter(status='active') if not user.is_px_admin() and user.hospital: hospitals = hospitals.filter(id=user.hospital.id) # Get departments for filter departments = Department.objects.filter(status='active') if not user.is_px_admin() and user.hospital: departments = departments.filter(hospital=user.hospital) # Calculate statistics total_staff = queryset.count() top_managers = len(hierarchy) context = { 'hierarchy': hierarchy, 'hospitals': hospitals, 'departments': departments, 'filters': request.GET, 'total_staff': total_staff, 'top_managers': top_managers, 'search_result': search_result, } return render(request, 'organizations/staff_hierarchy.html', context) @login_required def staff_hierarchy_d3(request): """ Staff hierarchy D3 visualization view Shows interactive organizational chart using D3.js """ # Get hospitals for filter (used by client-side filters) hospitals = Hospital.objects.filter(status='active') user = request.user if not user.is_px_admin() and user.hospital: hospitals = hospitals.filter(id=user.hospital.id) context = { 'hospitals': hospitals, } return render(request, 'organizations/staff_hierarchy_d3.html', context)