HH/apps/organizations/views.py
2026-01-12 12:19:19 +03:00

445 lines
15 KiB
Python

"""
Organizations views and viewsets
"""
from django.db import models
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from apps.accounts.permissions import (
CanAccessDepartmentData,
CanAccessHospitalData,
IsPXAdminOrHospitalAdmin,
IsPXAdmin
)
from .models import Department, Hospital, Organization, Patient, Staff
from .models import Staff as StaffModel
from .serializers import (
DepartmentSerializer,
HospitalSerializer,
OrganizationSerializer,
PatientListSerializer,
PatientSerializer,
StaffSerializer,
)
class OrganizationViewSet(viewsets.ModelViewSet):
"""
ViewSet for Organization model.
Permissions:
- PX Admins can manage organizations
- Others can view organizations
"""
queryset = Organization.objects.all()
serializer_class = OrganizationSerializer
permission_classes = [IsAuthenticated]
filterset_fields = ['status', 'city']
search_fields = ['name', 'name_ar', 'code', 'license_number']
ordering_fields = ['name', 'created_at']
ordering = ['name']
def get_queryset(self):
"""Filter organizations based on user role"""
queryset = super().get_queryset().prefetch_related('hospitals')
user = self.request.user
# PX Admins see all organizations
if user.is_px_admin():
return queryset
# Hospital Admins and others see their organization
if user.is_hospital_admin() and user.hospital and user.hospital.organization:
return queryset.filter(id=user.hospital.organization.id)
# Others with hospital see their organization
if user.hospital and user.hospital.organization:
return queryset.filter(id=user.hospital.organization.id)
return queryset.none()
class HospitalViewSet(viewsets.ModelViewSet):
"""
ViewSet for Hospital model.
Permissions:
- PX Admins and Hospital Admins can manage hospitals
- Others can view hospitals they belong to
"""
queryset = Hospital.objects.all()
serializer_class = HospitalSerializer
permission_classes = [IsAuthenticated, CanAccessHospitalData]
filterset_fields = ['status', 'city', 'organization']
search_fields = ['name', 'name_ar', 'code', 'city']
ordering_fields = ['name', 'created_at']
ordering = ['name']
def get_queryset(self):
"""Filter hospitals based on user role"""
queryset = super().get_queryset()
user = self.request.user
# PX Admins see all hospitals
if user.is_px_admin():
return queryset
# Hospital Admins see their hospital
if user.is_hospital_admin() and user.hospital:
return queryset.filter(id=user.hospital.id)
# Department Managers see their hospital
if user.is_department_manager() and user.hospital:
return queryset.filter(id=user.hospital.id)
# Others see hospitals they're associated with
if user.hospital:
return queryset.filter(id=user.hospital.id)
return queryset.none()
class DepartmentViewSet(viewsets.ModelViewSet):
"""
ViewSet for Department model.
Permissions:
- PX Admins and Hospital Admins can manage departments
- Department Managers can view their department
"""
queryset = Department.objects.all()
serializer_class = DepartmentSerializer
permission_classes = [IsAuthenticated, CanAccessDepartmentData]
filterset_fields = ['status', 'hospital', 'parent', 'hospital__organization']
search_fields = ['name', 'name_ar', 'code']
ordering_fields = ['name', 'created_at']
ordering = ['hospital', 'name']
def get_queryset(self):
"""Filter departments based on user role"""
queryset = super().get_queryset().select_related('hospital', 'parent', 'manager')
user = self.request.user
# PX Admins see all departments
if user.is_px_admin():
return queryset
# Hospital Admins see departments in their hospital
if user.is_hospital_admin() and user.hospital:
return queryset.filter(hospital=user.hospital)
# Department Managers see their department and sub-departments
if user.is_department_manager() and user.department:
return queryset.filter(
hospital=user.hospital
).filter(
models.Q(id=user.department.id) | models.Q(parent=user.department)
)
# Others see departments in their hospital
if user.hospital:
return queryset.filter(hospital=user.hospital)
return queryset.none()
class StaffViewSet(viewsets.ModelViewSet):
"""
ViewSet for Staff model.
Permissions:
- PX Admins and Hospital Admins can manage staff
- Others can view staff
"""
queryset = StaffModel.objects.all()
serializer_class = StaffSerializer
permission_classes = [IsAuthenticated]
filterset_fields = ['status', 'hospital', 'department', 'staff_type', 'specialization', 'job_title', 'hospital__organization']
search_fields = ['first_name', 'last_name', 'first_name_ar', 'last_name_ar', 'employee_id', 'license_number', 'job_title']
ordering_fields = ['last_name', 'created_at']
ordering = ['last_name', 'first_name']
def get_permissions(self):
"""Set permissions based on action"""
if self.action in ['create_user_account', 'link_user', 'unlink_user', 'send_invitation']:
return [IsAuthenticated()]
return super().get_permissions()
def get_queryset(self):
"""Filter staff based on user role"""
queryset = super().get_queryset().select_related('hospital', 'department', 'user')
user = self.request.user
# PX Admins see all staff
if user.is_px_admin():
return queryset
# Hospital Admins see staff in their hospital
if user.is_hospital_admin() and user.hospital:
return queryset.filter(hospital=user.hospital)
# Department Managers see staff in their department
if user.is_department_manager() and user.department:
return queryset.filter(department=user.department)
# Others see staff in their hospital
if user.hospital:
return queryset.filter(hospital=user.hospital)
return queryset.none()
@action(detail=True, methods=['post'])
def create_user_account(self, request, pk=None):
"""
Create a user account for a staff member.
Auto-generates username, password, and sends email.
"""
staff = self.get_object()
if staff.user:
return Response(
{'error': 'Staff member already has a user account'},
status=status.HTTP_400_BAD_REQUEST
)
# Check permissions
user = request.user
if not user.is_px_admin() and not user.is_hospital_admin():
return Response(
{'error': 'You do not have permission to create user accounts'},
status=status.HTTP_403_FORBIDDEN
)
# Hospital Admins can only create accounts for staff in their hospital
if user.is_hospital_admin() and staff.hospital != user.hospital:
return Response(
{'error': 'You can only create accounts for staff in your hospital'},
status=status.HTTP_403_FORBIDDEN
)
# Get role from request or use default based on staff_type
from .services import StaffService
role = request.data.get('role', StaffService.get_staff_type_role(staff.staff_type))
try:
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()
# Send email
try:
StaffService.send_credentials_email(staff, password, request)
message = 'User account created and credentials emailed successfully'
except Exception as e:
message = f'User account created. Email sending failed: {str(e)}'
serializer = self.get_serializer(staff)
return Response({
'message': message,
'staff': serializer.data,
'email': user_account.email
}, status=status.HTTP_201_CREATED)
except ValueError as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
@action(detail=True, methods=['post'])
def link_user(self, request, pk=None):
"""
Link an existing user account to a staff member.
"""
staff = self.get_object()
if staff.user:
return Response(
{'error': 'Staff member already has a user account'},
status=status.HTTP_400_BAD_REQUEST
)
# Check permissions
user = request.user
if not user.is_px_admin() and not user.is_hospital_admin():
return Response(
{'error': 'You do not have permission to link user accounts'},
status=status.HTTP_403_FORBIDDEN
)
# Hospital Admins can only link accounts for staff in their hospital
if user.is_hospital_admin() and staff.hospital != user.hospital:
return Response(
{'error': 'You can only link accounts for staff in your hospital'},
status=status.HTTP_403_FORBIDDEN
)
user_id = request.data.get('user_id')
if not user_id:
return Response(
{'error': 'user_id is required'},
status=status.HTTP_400_BAD_REQUEST
)
from .services import StaffService
try:
StaffService.link_user_to_staff(staff, user_id, request=request)
serializer = self.get_serializer(staff)
return Response({
'message': 'User account linked successfully',
'staff': serializer.data
})
except ValueError as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
@action(detail=True, methods=['post'])
def unlink_user(self, request, pk=None):
"""
Remove user account association from a staff member.
"""
staff = self.get_object()
if not staff.user:
return Response(
{'error': 'Staff member has no user account'},
status=status.HTTP_400_BAD_REQUEST
)
# Check permissions
user = request.user
if not user.is_px_admin() and not user.is_hospital_admin():
return Response(
{'error': 'You do not have permission to unlink user accounts'},
status=status.HTTP_403_FORBIDDEN
)
# Hospital Admins can only unlink accounts for staff in their hospital
if user.is_hospital_admin() and staff.hospital != user.hospital:
return Response(
{'error': 'You can only unlink accounts for staff in your hospital'},
status=status.HTTP_403_FORBIDDEN
)
from .services import StaffService
try:
StaffService.unlink_user_from_staff(staff, request=request)
serializer = self.get_serializer(staff)
return Response({
'message': 'User account unlinked successfully',
'staff': serializer.data
})
except ValueError as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
@action(detail=True, methods=['post'])
def send_invitation(self, request, pk=None):
"""
Send credentials email to staff member.
Generates new password and emails it.
"""
staff = self.get_object()
if not staff.user:
return Response(
{'error': 'Staff member has no user account'},
status=status.HTTP_400_BAD_REQUEST
)
# Check permissions
user = request.user
if not user.is_px_admin() and not user.is_hospital_admin():
return Response(
{'error': 'You do not have permission to send invitations'},
status=status.HTTP_403_FORBIDDEN
)
# Hospital Admins can only send invitations to staff in their hospital
if user.is_hospital_admin() and staff.hospital != user.hospital:
return Response(
{'error': 'You can only send invitations to staff in your hospital'},
status=status.HTTP_403_FORBIDDEN
)
from .services import StaffService
try:
# Generate new password
password = StaffService.generate_password()
# Update user password
staff.user.set_password(password)
staff.user.save()
# Send email
StaffService.send_credentials_email(staff, password, request)
serializer = self.get_serializer(staff)
return Response({
'message': 'Invitation email sent successfully',
'staff': serializer.data
})
except ValueError as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
class PatientViewSet(viewsets.ModelViewSet):
"""
ViewSet for Patient model.
Permissions:
- All authenticated users can view patients
- PX Admins and Hospital Admins can manage patients
"""
queryset = Patient.objects.all()
permission_classes = [IsAuthenticated]
filterset_fields = ['status', 'gender', 'primary_hospital', 'city', 'primary_hospital__organization']
search_fields = ['mrn', 'national_id', 'first_name', 'last_name', 'phone', 'email']
ordering_fields = ['last_name', 'created_at']
ordering = ['last_name', 'first_name']
def get_serializer_class(self):
"""Use simplified serializer for list view"""
if self.action == 'list':
return PatientListSerializer
return PatientSerializer
def get_queryset(self):
"""Filter patients based on user role"""
queryset = super().get_queryset().select_related('primary_hospital')
user = self.request.user
# PX Admins see all patients
if user.is_px_admin():
return queryset
# Hospital Admins see patients in their hospital
if user.is_hospital_admin() and user.hospital:
return queryset.filter(primary_hospital=user.hospital)
# Others see patients in their hospital
if user.hospital:
return queryset.filter(primary_hospital=user.hospital)
return queryset