app.infrastructure.views

Infrastructure API views for hospital management.

Endpoints documented:

  • /api/hospitals/ : Hospital CRUD
  • /api/hospitals/{hospital_pk}/roles/ : Role CRUD for a hospital
  • /api/hospitals/{hospital_pk}/users/ : Hospital user CRUD
  • /api/hospitals/{hospital_pk}/shifts/ : Shift CRUD for a hospital
  • /api/hospitals/{hospital_pk}/blocks/ : Block CRUD for a hospital
  • /api/hospitals/{hospital_pk}/hierarchies/ : Approver hierarchy CRUD
  • /api/hospitals/{hospital_pk}/wards/ : Ward CRUD for a hospital
  • /api/set-password/ : Change user password
  • /api/switch-block/ : Switch user's current block
  • /api/switch-ward/ : Switch user's current ward

Each endpoint is documented with drf-spectacular's @extend_schema for OpenAPI generation.

  1"""
  2Infrastructure API views for hospital management.
  3
  4Endpoints documented:
  5- /api/hospitals/                      : Hospital CRUD
  6- /api/hospitals/{hospital_pk}/roles/  : Role CRUD for a hospital
  7- /api/hospitals/{hospital_pk}/users/  : Hospital user CRUD
  8- /api/hospitals/{hospital_pk}/shifts/ : Shift CRUD for a hospital
  9- /api/hospitals/{hospital_pk}/blocks/ : Block CRUD for a hospital
 10- /api/hospitals/{hospital_pk}/hierarchies/ : Approver hierarchy CRUD
 11- /api/hospitals/{hospital_pk}/wards/  : Ward CRUD for a hospital
 12- /api/set-password/                   : Change user password
 13- /api/switch-block/                   : Switch user's current block
 14- /api/switch-ward/                    : Switch user's current ward
 15
 16Each endpoint is documented with drf-spectacular's @extend_schema for OpenAPI generation.
 17"""
 18
 19from rest_framework import viewsets, status
 20from rest_framework.views import APIView
 21from .serializers import HospitalSerializer, RoleSerializer, HospitalUserSerializer, ShiftSerializer, BlockSerializer, WardSerializer, ApproverHierarchySerializer
 22from accounts.models import Hospital, User, Role
 23from management.models import Shift, Blocks, Ward
 24from .models import ApproverHierarchy, ApproverLevel, BlockChange, WardChange
 25from .permissions import IsHospitalUserOrSuperUser, RoleEditPermission, UserPermission, WardPermission
 26from rest_framework.response import Response
 27from rest_framework.permissions import IsAuthenticated
 28from drf_spectacular.utils import extend_schema, OpenApiTypes, OpenApiParameter, OpenApiResponse
 29from rest_framework.decorators import action
 30from django_filters.rest_framework import DjangoFilterBackend
 31
 32@extend_schema(
 33    summary="Hospital CRUD",
 34    description="Create, retrieve, update, and list hospitals.",
 35    responses={200: HospitalSerializer(many=True)}
 36)
 37class HospitalViewSet(viewsets.ModelViewSet):
 38    """
 39    ViewSet for hospital CRUD operations.
 40    """
 41    serializer_class = HospitalSerializer
 42    permission_classes = [IsHospitalUserOrSuperUser]
 43
 44    def get_queryset(self):
 45        print(self.kwargs)
 46        user = self.request.user
 47        return Hospital.objects.filter()
 48
 49    def get_serializer_context(self):
 50        context = super().get_serializer_context()
 51        context['hospital_id'] = self.kwargs.get('pk')
 52        return context
 53
 54    def destroy(self, request, *args, **kwargs):
 55        return Response({"detail": "Deletion not allowed."}, status=405)
 56
 57@extend_schema(
 58    summary="Change user's password",
 59    description="Change password by providing phone and new password.",
 60    parameters=[
 61        OpenApiParameter(name="phone", type=str, location="query", required=True, description="User's phone number"),
 62        OpenApiParameter(name="password", type=str, location="query", required=True, description="New password"),
 63    ],
 64    responses={
 65        200: OpenApiResponse(description="Password changed successfully."),
 66        404: OpenApiResponse(description="User not found."),
 67        400: OpenApiResponse(description="Bad request."),
 68    },
 69)
 70class PasswordChangeViewSet(APIView):
 71    """
 72    APIView to change a user's password.
 73    """
 74    permission_classes = []
 75
 76    @extend_schema(
 77        summary="Change user's password",
 78        description="This endpoint allows a user to change their password by providing their phone number and new password.",
 79        parameters=[
 80            OpenApiParameter(name="phone", type=str, location="query", required=True, description="User's phone number"),
 81            OpenApiParameter(name="password", type=str, location="query", required=True, description="New password"),
 82        ],
 83        responses={
 84            200: OpenApiResponse(description="Password changed successfully."),
 85            404: OpenApiResponse(description="User not found."),
 86            400: OpenApiResponse(description="Bad request."),
 87        },
 88    )
 89    def post(self, request, *args, **kwargs):
 90        try:
 91            phone = request.data.get('phone')
 92            password = request.data.get('password')
 93
 94            user = User.objects.get(phone_number=phone)
 95            user.set_password(password)
 96            user.save()
 97            return Response({"detail": "Password changed successfully."}, status=200)
 98        except User.DoesNotExist:
 99            return Response({"detail": "User not found."}, status=404)
100        except Exception as e:
101            return Response({"detail": str(e)}, status=400)
102
103@extend_schema(
104    summary="Role CRUD",
105    description="Create, retrieve, update, and list roles for a hospital.",
106    responses={200: RoleSerializer(many=True)}
107)
108class RoleViewSet(viewsets.ModelViewSet):
109    """
110    ViewSet for role CRUD operations within a hospital.
111    """
112    serializer_class = RoleSerializer
113    permission_classes = [IsAuthenticated, RoleEditPermission]
114    lookup_field = 'id'
115
116    def get_queryset(self):
117        hospital_id = self.kwargs.get('hospital_pk')
118        print("KWARGS:", self.kwargs)
119        if self.request.query_params.get('is_responder') is not None:
120            return Role.objects.filter(
121                hospital__id=hospital_id,
122                is_responder=True
123            )
124        return Role.objects.filter(hospital__id=hospital_id)
125
126    def get_serializer_context(self):
127        context = super().get_serializer_context()
128        context['hospital_id'] = self.kwargs.get('hospital_pk')
129        return context
130
131    def perform_create(self, serializer):
132        hospital_id = self.kwargs.get('hospital_pk')
133        serializer.save(hospital_id=hospital_id)
134
135    def destroy(self, request, *args, **kwargs):
136        instance = self.get_object()
137        self.perform_destroy(instance)
138        return Response({"detail": "Role deleted successfully."}, status=status.HTTP_204_NO_CONTENT)
139
140@extend_schema(
141    summary="Hospital User CRUD",
142    description="Create, retrieve, update, and list hospital users.",
143    responses={200: HospitalUserSerializer(many=True)}
144)
145class HospitalUserViewSet(viewsets.ModelViewSet):
146    """
147    ViewSet for hospital user CRUD operations.
148    """
149    serializer_class = HospitalUserSerializer
150    permission_classes = [IsAuthenticated, UserPermission]
151    lookup_field = 'id'
152
153    def get_queryset(self):
154        return User.objects.filter(hospital__id=self.kwargs.get('hospital_pk')).exclude(is_superuser=True)
155
156    def get_serializer_context(self):
157        context = super().get_serializer_context()
158        context['hospital_id'] = self.kwargs.get('hospital_pk')
159        return context
160
161@extend_schema(
162    summary="Block CRUD",
163    description="Create, retrieve, update, and list blocks for a hospital.",
164    responses={200: BlockSerializer(many=True)}
165)
166class BlockViewSet(viewsets.ModelViewSet):
167    """
168    ViewSet for block CRUD operations within a hospital.
169    """
170    serializer_class = BlockSerializer
171    permission_classes = [IsAuthenticated, RoleEditPermission]
172    lookup_field = 'id'
173
174    def get_queryset(self):
175        return Blocks.objects.filter(hospital_id=self.kwargs.get('hospital_pk'))
176
177    def get_serializer_context(self):
178        context = super().get_serializer_context()
179        context['hospital_id'] = self.kwargs.get('hospital_pk')
180        return context
181
182    def perform_create(self, serializer):
183        serializer.save(
184            hospital_id=self.kwargs.get('hospital_pk'),
185            created_by=self.request.user
186        )
187
188@extend_schema(
189    summary="Shift CRUD",
190    description="Create, retrieve, update, and list shifts for a hospital.",
191    responses={200: ShiftSerializer(many=True)}
192)
193class ShiftViewSet(viewsets.ModelViewSet):
194    """
195    ViewSet for shift CRUD operations within a hospital.
196    """
197    serializer_class = ShiftSerializer
198    permission_classes = [IsAuthenticated, RoleEditPermission]
199    lookup_field = 'id'
200
201    def get_queryset(self):
202        return Shift.objects.filter(hospital_id=self.kwargs.get('hospital_pk'))
203
204    def get_serializer_context(self):
205        context = super().get_serializer_context()
206        context['hospital_id'] = self.kwargs.get('hospital_pk')
207        return context
208
209    def perform_create(self, serializer):
210        serializer.save(
211            hospital_id=self.kwargs.get('hospital_pk'),
212            created_by=self.request.user
213        )
214
215    def destroy(self, request, *args, **kwargs):
216        return Response({"detail": "Deletion not allowed."}, status=405)
217
218@extend_schema(
219    summary="Approver Hierarchy CRUD",
220    description="Create, retrieve, update, and list approver hierarchies for a hospital.",
221    responses={200: ApproverHierarchySerializer(many=True)}
222)
223class ApproverHierarchyViewSet(viewsets.ModelViewSet):
224    """
225    ViewSet for approver hierarchy CRUD operations within a hospital.
226    """
227    serializer_class = ApproverHierarchySerializer
228    permission_classes = [IsAuthenticated, RoleEditPermission]
229    lookup_field = 'id'
230
231    def get_queryset(self):
232        hospital_id = self.kwargs.get('hospital_pk')
233        return ApproverHierarchy.objects.filter(levels__role__hospital_id=hospital_id).distinct()
234
235    def perform_create(self, serializer):
236        serializer.save()
237
238    def get_serializer_context(self):
239        return super().get_serializer_context()
240
241@extend_schema(
242    summary="Ward CRUD",
243    description="Create, retrieve, update, and list wards for a hospital.",
244    responses={200: WardSerializer(many=True)}
245)
246class WardViewSet(viewsets.ModelViewSet):
247    """
248    ViewSet for ward CRUD operations within a hospital.
249    """
250    serializer_class = WardSerializer
251    permission_classes = [IsAuthenticated, WardPermission]
252    lookup_field = 'id'
253
254    def get_queryset(self):
255        hospital_id = self.kwargs.get('hospital_pk')
256        queryset = Ward.objects.filter(
257            deleted=False,
258            block__hospital_id=hospital_id
259        )
260
261        block_id = self.request.query_params.get('block')
262        if block_id:
263            queryset = queryset.filter(block_id=block_id)
264
265        floor = self.request.query_params.get('floor')
266        if floor is not None:
267            queryset = queryset.filter(floor=floor)
268
269        return queryset
270
271    def get_serializer_context(self):
272        context = super().get_serializer_context()
273        context['hospital_id'] = self.kwargs.get('hospital_pk')
274        return context
275
276    def perform_create(self, serializer):
277        serializer.save(created_by=self.request.user)
278
279    def destroy(self, request, *args, **kwargs):
280        instance = self.get_object()
281        if request.user.is_superuser:
282            instance.delete()
283            return Response({"detail": "Ward deleted successfully."}, status=status.HTTP_202_ACCEPTED)
284        instance.deleted = True
285        instance.save()
286        return Response({"detail": "Ward soft-deleted successfully."}, status=status.HTTP_202_ACCEPTED)
287
288    @action(detail=True, methods=['post'], url_path='restore')
289    def restore(self, request, *args, **kwargs):
290        instance = self.get_object()
291        instance.deleted = False
292        instance.save()
293        return Response({"detail": "Ward restored successfully."})
294
295@extend_schema(
296    summary="Switch Current Block",
297    description="Switch user's current block by providing block ID.",
298    parameters=[
299        OpenApiParameter(name="block_id", type=int, location="query", required=True, description="Block ID"),
300    ],
301    responses={
302        200: OpenApiResponse(description="Block switched successfully."),
303        400: OpenApiResponse(description="Bad request, block ID is required."),
304        404: OpenApiResponse(description="Block not found.")
305    }
306)
307class SwitchBlock(APIView):
308    """
309    APIView to switch user's current block.
310    """
311    permission_classes = [IsAuthenticated]
312
313    @extend_schema(
314        summary="Switch Current Block",
315        description="This endpoint allows a user to switch their current block by providing the block ID.",
316        parameters=[
317            OpenApiParameter(name="block_id", type=int, location="query", required=True, description="Block ID"),
318        ],
319        responses={
320            200: OpenApiResponse(description="Block switched successfully."),
321            400: OpenApiResponse(description="Bad request, block ID is required."),
322            404: OpenApiResponse(description="Block not found.")
323        }
324    )
325    def post(self, request, *args, **kwargs):
326        user = request.user
327        block_id = request.data.get('block_id')
328
329        if not block_id:
330            return Response({"detail": "Block ID is required."}, status=status.HTTP_400_BAD_REQUEST)
331
332        try:
333            block = Blocks.objects.get(id=block_id, hospital=user.hospital)
334            BlockChange.objects.create(
335                user=user,
336                from_block=user.current_block if user.current_block else None,
337                to_block=block
338            )
339            user.current_block = block
340            user.save()
341            return Response({"detail": "Block switched successfully."}, status=status.HTTP_200_OK)
342        except Blocks.DoesNotExist:
343            return Response({"detail": "Block not found."}, status=status.HTTP_404_NOT_FOUND)
344
345@extend_schema(
346    summary="Switch Current Ward",
347    description="Switch user's current ward by providing ward ID.",
348    parameters=[
349        OpenApiParameter(name="ward_id", type=int, location="query", required=True, description="Ward ID"),
350    ],
351    responses={
352        200: OpenApiResponse(description="Ward switched successfully."),
353        400: OpenApiResponse(description="Bad request, ward ID is required."),
354        404: OpenApiResponse(description="Ward not found.")
355    }
356)
357class SwitchWard(APIView):
358    """
359    APIView to switch user's current ward.
360    """
361    permission_classes = [IsAuthenticated]
362
363    @extend_schema(
364        summary="Switch Current Ward",
365        description="This endpoint allows a user to switch their current ward by providing the ward ID.",
366        parameters=[
367            OpenApiParameter(name="ward_id", type=int, location="query", required=True, description="Ward ID"),
368        ],
369        responses={
370            200: OpenApiResponse(description="Ward switched successfully."),
371            400: OpenApiResponse(description="Bad request, ward ID is required."),
372            404: OpenApiResponse(description="Ward not found.")
373        }
374    )
375    def post(self, request, *args, **kwargs):
376        user = request.user
377        ward_id = request.data.get('ward_id')
378
379        if not ward_id:
380            return Response({"detail": "Ward ID is required."}, status=status.HTTP_400_BAD_REQUEST)
381
382        try:
383            ward = Ward.objects.get(id=ward_id, block__hospital=user.hospital)
384            WardChange.objects.create(
385                user=user,
386                from_ward=user.current_ward if user.current_ward else None,
387                to_ward=ward
388            )
389            user.current_ward = ward
390            user.current_block = ward.block
391            user.save()
392            return Response({"detail": "Ward switched successfully."}, status=status.HTTP_200_OK)
393        except Ward.DoesNotExist:
394            return Response({"detail": "Ward not found."}, status=status.HTTP_404_NOT_FOUND)
@extend_schema(summary='Hospital CRUD', description='Create, retrieve, update, and list hospitals.', responses={200: HospitalSerializer(many=True)})
class HospitalViewSet(rest_framework.viewsets.ModelViewSet):
33@extend_schema(
34    summary="Hospital CRUD",
35    description="Create, retrieve, update, and list hospitals.",
36    responses={200: HospitalSerializer(many=True)}
37)
38class HospitalViewSet(viewsets.ModelViewSet):
39    """
40    ViewSet for hospital CRUD operations.
41    """
42    serializer_class = HospitalSerializer
43    permission_classes = [IsHospitalUserOrSuperUser]
44
45    def get_queryset(self):
46        print(self.kwargs)
47        user = self.request.user
48        return Hospital.objects.filter()
49
50    def get_serializer_context(self):
51        context = super().get_serializer_context()
52        context['hospital_id'] = self.kwargs.get('pk')
53        return context
54
55    def destroy(self, request, *args, **kwargs):
56        return Response({"detail": "Deletion not allowed."}, status=405)

ViewSet for hospital CRUD operations.

def get_queryset(self):
45    def get_queryset(self):
46        print(self.kwargs)
47        user = self.request.user
48        return Hospital.objects.filter()

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

def get_serializer_context(self):
50    def get_serializer_context(self):
51        context = super().get_serializer_context()
52        context['hospital_id'] = self.kwargs.get('pk')
53        return context

Extra context provided to the serializer class.

def destroy(self, request, *args, **kwargs):
55    def destroy(self, request, *args, **kwargs):
56        return Response({"detail": "Deletion not allowed."}, status=405)
schema
name = None
description = None
suffix = None
detail = None
basename = None
@extend_schema(summary="Change user's password", description='Change password by providing phone and new password.', parameters=[OpenApiParameter(name='phone', type=str, location='query', required=True, description="User's phone number"), OpenApiParameter(name='password', type=str, location='query', required=True, description='New password')], responses={200: OpenApiResponse(description='Password changed successfully.'), 404: OpenApiResponse(description='User not found.'), 400: OpenApiResponse(description='Bad request.')})
class PasswordChangeViewSet(rest_framework.views.APIView):
 58@extend_schema(
 59    summary="Change user's password",
 60    description="Change password by providing phone and new password.",
 61    parameters=[
 62        OpenApiParameter(name="phone", type=str, location="query", required=True, description="User's phone number"),
 63        OpenApiParameter(name="password", type=str, location="query", required=True, description="New password"),
 64    ],
 65    responses={
 66        200: OpenApiResponse(description="Password changed successfully."),
 67        404: OpenApiResponse(description="User not found."),
 68        400: OpenApiResponse(description="Bad request."),
 69    },
 70)
 71class PasswordChangeViewSet(APIView):
 72    """
 73    APIView to change a user's password.
 74    """
 75    permission_classes = []
 76
 77    @extend_schema(
 78        summary="Change user's password",
 79        description="This endpoint allows a user to change their password by providing their phone number and new password.",
 80        parameters=[
 81            OpenApiParameter(name="phone", type=str, location="query", required=True, description="User's phone number"),
 82            OpenApiParameter(name="password", type=str, location="query", required=True, description="New password"),
 83        ],
 84        responses={
 85            200: OpenApiResponse(description="Password changed successfully."),
 86            404: OpenApiResponse(description="User not found."),
 87            400: OpenApiResponse(description="Bad request."),
 88        },
 89    )
 90    def post(self, request, *args, **kwargs):
 91        try:
 92            phone = request.data.get('phone')
 93            password = request.data.get('password')
 94
 95            user = User.objects.get(phone_number=phone)
 96            user.set_password(password)
 97            user.save()
 98            return Response({"detail": "Password changed successfully."}, status=200)
 99        except User.DoesNotExist:
100            return Response({"detail": "User not found."}, status=404)
101        except Exception as e:
102            return Response({"detail": str(e)}, status=400)

APIView to change a user's password.

permission_classes = []
@extend_schema(summary="Change user's password", description='This endpoint allows a user to change their password by providing their phone number and new password.', parameters=[OpenApiParameter(name='phone', type=str, location='query', required=True, description="User's phone number"), OpenApiParameter(name='password', type=str, location='query', required=True, description='New password')], responses={200: OpenApiResponse(description='Password changed successfully.'), 404: OpenApiResponse(description='User not found.'), 400: OpenApiResponse(description='Bad request.')})
def post(self, request, *args, **kwargs):
 77    @extend_schema(
 78        summary="Change user's password",
 79        description="This endpoint allows a user to change their password by providing their phone number and new password.",
 80        parameters=[
 81            OpenApiParameter(name="phone", type=str, location="query", required=True, description="User's phone number"),
 82            OpenApiParameter(name="password", type=str, location="query", required=True, description="New password"),
 83        ],
 84        responses={
 85            200: OpenApiResponse(description="Password changed successfully."),
 86            404: OpenApiResponse(description="User not found."),
 87            400: OpenApiResponse(description="Bad request."),
 88        },
 89    )
 90    def post(self, request, *args, **kwargs):
 91        try:
 92            phone = request.data.get('phone')
 93            password = request.data.get('password')
 94
 95            user = User.objects.get(phone_number=phone)
 96            user.set_password(password)
 97            user.save()
 98            return Response({"detail": "Password changed successfully."}, status=200)
 99        except User.DoesNotExist:
100            return Response({"detail": "User not found."}, status=404)
101        except Exception as e:
102            return Response({"detail": str(e)}, status=400)
schema
@extend_schema(summary='Role CRUD', description='Create, retrieve, update, and list roles for a hospital.', responses={200: RoleSerializer(many=True)})
class RoleViewSet(rest_framework.viewsets.ModelViewSet):
104@extend_schema(
105    summary="Role CRUD",
106    description="Create, retrieve, update, and list roles for a hospital.",
107    responses={200: RoleSerializer(many=True)}
108)
109class RoleViewSet(viewsets.ModelViewSet):
110    """
111    ViewSet for role CRUD operations within a hospital.
112    """
113    serializer_class = RoleSerializer
114    permission_classes = [IsAuthenticated, RoleEditPermission]
115    lookup_field = 'id'
116
117    def get_queryset(self):
118        hospital_id = self.kwargs.get('hospital_pk')
119        print("KWARGS:", self.kwargs)
120        if self.request.query_params.get('is_responder') is not None:
121            return Role.objects.filter(
122                hospital__id=hospital_id,
123                is_responder=True
124            )
125        return Role.objects.filter(hospital__id=hospital_id)
126
127    def get_serializer_context(self):
128        context = super().get_serializer_context()
129        context['hospital_id'] = self.kwargs.get('hospital_pk')
130        return context
131
132    def perform_create(self, serializer):
133        hospital_id = self.kwargs.get('hospital_pk')
134        serializer.save(hospital_id=hospital_id)
135
136    def destroy(self, request, *args, **kwargs):
137        instance = self.get_object()
138        self.perform_destroy(instance)
139        return Response({"detail": "Role deleted successfully."}, status=status.HTTP_204_NO_CONTENT)

ViewSet for role CRUD operations within a hospital.

serializer_class = <class 'app.infrastructure.serializers.RoleSerializer'>
permission_classes = [<class 'rest_framework.permissions.IsAuthenticated'>, <class 'app.infrastructure.permissions.RoleEditPermission'>]
lookup_field = 'id'
def get_queryset(self):
117    def get_queryset(self):
118        hospital_id = self.kwargs.get('hospital_pk')
119        print("KWARGS:", self.kwargs)
120        if self.request.query_params.get('is_responder') is not None:
121            return Role.objects.filter(
122                hospital__id=hospital_id,
123                is_responder=True
124            )
125        return Role.objects.filter(hospital__id=hospital_id)

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

def get_serializer_context(self):
127    def get_serializer_context(self):
128        context = super().get_serializer_context()
129        context['hospital_id'] = self.kwargs.get('hospital_pk')
130        return context

Extra context provided to the serializer class.

def perform_create(self, serializer):
132    def perform_create(self, serializer):
133        hospital_id = self.kwargs.get('hospital_pk')
134        serializer.save(hospital_id=hospital_id)
def destroy(self, request, *args, **kwargs):
136    def destroy(self, request, *args, **kwargs):
137        instance = self.get_object()
138        self.perform_destroy(instance)
139        return Response({"detail": "Role deleted successfully."}, status=status.HTTP_204_NO_CONTENT)
schema
name = None
description = None
suffix = None
detail = None
basename = None
@extend_schema(summary='Hospital User CRUD', description='Create, retrieve, update, and list hospital users.', responses={200: HospitalUserSerializer(many=True)})
class HospitalUserViewSet(rest_framework.viewsets.ModelViewSet):
141@extend_schema(
142    summary="Hospital User CRUD",
143    description="Create, retrieve, update, and list hospital users.",
144    responses={200: HospitalUserSerializer(many=True)}
145)
146class HospitalUserViewSet(viewsets.ModelViewSet):
147    """
148    ViewSet for hospital user CRUD operations.
149    """
150    serializer_class = HospitalUserSerializer
151    permission_classes = [IsAuthenticated, UserPermission]
152    lookup_field = 'id'
153
154    def get_queryset(self):
155        return User.objects.filter(hospital__id=self.kwargs.get('hospital_pk')).exclude(is_superuser=True)
156
157    def get_serializer_context(self):
158        context = super().get_serializer_context()
159        context['hospital_id'] = self.kwargs.get('hospital_pk')
160        return context

ViewSet for hospital user CRUD operations.

permission_classes = [<class 'rest_framework.permissions.IsAuthenticated'>, <class 'app.infrastructure.permissions.UserPermission'>]
lookup_field = 'id'
def get_queryset(self):
154    def get_queryset(self):
155        return User.objects.filter(hospital__id=self.kwargs.get('hospital_pk')).exclude(is_superuser=True)

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

def get_serializer_context(self):
157    def get_serializer_context(self):
158        context = super().get_serializer_context()
159        context['hospital_id'] = self.kwargs.get('hospital_pk')
160        return context

Extra context provided to the serializer class.

schema
name = None
description = None
suffix = None
detail = None
basename = None
@extend_schema(summary='Block CRUD', description='Create, retrieve, update, and list blocks for a hospital.', responses={200: BlockSerializer(many=True)})
class BlockViewSet(rest_framework.viewsets.ModelViewSet):
162@extend_schema(
163    summary="Block CRUD",
164    description="Create, retrieve, update, and list blocks for a hospital.",
165    responses={200: BlockSerializer(many=True)}
166)
167class BlockViewSet(viewsets.ModelViewSet):
168    """
169    ViewSet for block CRUD operations within a hospital.
170    """
171    serializer_class = BlockSerializer
172    permission_classes = [IsAuthenticated, RoleEditPermission]
173    lookup_field = 'id'
174
175    def get_queryset(self):
176        return Blocks.objects.filter(hospital_id=self.kwargs.get('hospital_pk'))
177
178    def get_serializer_context(self):
179        context = super().get_serializer_context()
180        context['hospital_id'] = self.kwargs.get('hospital_pk')
181        return context
182
183    def perform_create(self, serializer):
184        serializer.save(
185            hospital_id=self.kwargs.get('hospital_pk'),
186            created_by=self.request.user
187        )

ViewSet for block CRUD operations within a hospital.

serializer_class = <class 'app.infrastructure.serializers.BlockSerializer'>
permission_classes = [<class 'rest_framework.permissions.IsAuthenticated'>, <class 'app.infrastructure.permissions.RoleEditPermission'>]
lookup_field = 'id'
def get_queryset(self):
175    def get_queryset(self):
176        return Blocks.objects.filter(hospital_id=self.kwargs.get('hospital_pk'))

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

def get_serializer_context(self):
178    def get_serializer_context(self):
179        context = super().get_serializer_context()
180        context['hospital_id'] = self.kwargs.get('hospital_pk')
181        return context

Extra context provided to the serializer class.

def perform_create(self, serializer):
183    def perform_create(self, serializer):
184        serializer.save(
185            hospital_id=self.kwargs.get('hospital_pk'),
186            created_by=self.request.user
187        )
schema
name = None
description = None
suffix = None
detail = None
basename = None
@extend_schema(summary='Shift CRUD', description='Create, retrieve, update, and list shifts for a hospital.', responses={200: ShiftSerializer(many=True)})
class ShiftViewSet(rest_framework.viewsets.ModelViewSet):
189@extend_schema(
190    summary="Shift CRUD",
191    description="Create, retrieve, update, and list shifts for a hospital.",
192    responses={200: ShiftSerializer(many=True)}
193)
194class ShiftViewSet(viewsets.ModelViewSet):
195    """
196    ViewSet for shift CRUD operations within a hospital.
197    """
198    serializer_class = ShiftSerializer
199    permission_classes = [IsAuthenticated, RoleEditPermission]
200    lookup_field = 'id'
201
202    def get_queryset(self):
203        return Shift.objects.filter(hospital_id=self.kwargs.get('hospital_pk'))
204
205    def get_serializer_context(self):
206        context = super().get_serializer_context()
207        context['hospital_id'] = self.kwargs.get('hospital_pk')
208        return context
209
210    def perform_create(self, serializer):
211        serializer.save(
212            hospital_id=self.kwargs.get('hospital_pk'),
213            created_by=self.request.user
214        )
215
216    def destroy(self, request, *args, **kwargs):
217        return Response({"detail": "Deletion not allowed."}, status=405)

ViewSet for shift CRUD operations within a hospital.

serializer_class = <class 'app.infrastructure.serializers.ShiftSerializer'>
permission_classes = [<class 'rest_framework.permissions.IsAuthenticated'>, <class 'app.infrastructure.permissions.RoleEditPermission'>]
lookup_field = 'id'
def get_queryset(self):
202    def get_queryset(self):
203        return Shift.objects.filter(hospital_id=self.kwargs.get('hospital_pk'))

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

def get_serializer_context(self):
205    def get_serializer_context(self):
206        context = super().get_serializer_context()
207        context['hospital_id'] = self.kwargs.get('hospital_pk')
208        return context

Extra context provided to the serializer class.

def perform_create(self, serializer):
210    def perform_create(self, serializer):
211        serializer.save(
212            hospital_id=self.kwargs.get('hospital_pk'),
213            created_by=self.request.user
214        )
def destroy(self, request, *args, **kwargs):
216    def destroy(self, request, *args, **kwargs):
217        return Response({"detail": "Deletion not allowed."}, status=405)
schema
name = None
description = None
suffix = None
detail = None
basename = None
@extend_schema(summary='Approver Hierarchy CRUD', description='Create, retrieve, update, and list approver hierarchies for a hospital.', responses={200: ApproverHierarchySerializer(many=True)})
class ApproverHierarchyViewSet(rest_framework.viewsets.ModelViewSet):
219@extend_schema(
220    summary="Approver Hierarchy CRUD",
221    description="Create, retrieve, update, and list approver hierarchies for a hospital.",
222    responses={200: ApproverHierarchySerializer(many=True)}
223)
224class ApproverHierarchyViewSet(viewsets.ModelViewSet):
225    """
226    ViewSet for approver hierarchy CRUD operations within a hospital.
227    """
228    serializer_class = ApproverHierarchySerializer
229    permission_classes = [IsAuthenticated, RoleEditPermission]
230    lookup_field = 'id'
231
232    def get_queryset(self):
233        hospital_id = self.kwargs.get('hospital_pk')
234        return ApproverHierarchy.objects.filter(levels__role__hospital_id=hospital_id).distinct()
235
236    def perform_create(self, serializer):
237        serializer.save()
238
239    def get_serializer_context(self):
240        return super().get_serializer_context()

ViewSet for approver hierarchy CRUD operations within a hospital.

permission_classes = [<class 'rest_framework.permissions.IsAuthenticated'>, <class 'app.infrastructure.permissions.RoleEditPermission'>]
lookup_field = 'id'
def get_queryset(self):
232    def get_queryset(self):
233        hospital_id = self.kwargs.get('hospital_pk')
234        return ApproverHierarchy.objects.filter(levels__role__hospital_id=hospital_id).distinct()

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

def perform_create(self, serializer):
236    def perform_create(self, serializer):
237        serializer.save()
def get_serializer_context(self):
239    def get_serializer_context(self):
240        return super().get_serializer_context()

Extra context provided to the serializer class.

schema
name = None
description = None
suffix = None
detail = None
basename = None
@extend_schema(summary='Ward CRUD', description='Create, retrieve, update, and list wards for a hospital.', responses={200: WardSerializer(many=True)})
class WardViewSet(rest_framework.viewsets.ModelViewSet):
242@extend_schema(
243    summary="Ward CRUD",
244    description="Create, retrieve, update, and list wards for a hospital.",
245    responses={200: WardSerializer(many=True)}
246)
247class WardViewSet(viewsets.ModelViewSet):
248    """
249    ViewSet for ward CRUD operations within a hospital.
250    """
251    serializer_class = WardSerializer
252    permission_classes = [IsAuthenticated, WardPermission]
253    lookup_field = 'id'
254
255    def get_queryset(self):
256        hospital_id = self.kwargs.get('hospital_pk')
257        queryset = Ward.objects.filter(
258            deleted=False,
259            block__hospital_id=hospital_id
260        )
261
262        block_id = self.request.query_params.get('block')
263        if block_id:
264            queryset = queryset.filter(block_id=block_id)
265
266        floor = self.request.query_params.get('floor')
267        if floor is not None:
268            queryset = queryset.filter(floor=floor)
269
270        return queryset
271
272    def get_serializer_context(self):
273        context = super().get_serializer_context()
274        context['hospital_id'] = self.kwargs.get('hospital_pk')
275        return context
276
277    def perform_create(self, serializer):
278        serializer.save(created_by=self.request.user)
279
280    def destroy(self, request, *args, **kwargs):
281        instance = self.get_object()
282        if request.user.is_superuser:
283            instance.delete()
284            return Response({"detail": "Ward deleted successfully."}, status=status.HTTP_202_ACCEPTED)
285        instance.deleted = True
286        instance.save()
287        return Response({"detail": "Ward soft-deleted successfully."}, status=status.HTTP_202_ACCEPTED)
288
289    @action(detail=True, methods=['post'], url_path='restore')
290    def restore(self, request, *args, **kwargs):
291        instance = self.get_object()
292        instance.deleted = False
293        instance.save()
294        return Response({"detail": "Ward restored successfully."})

ViewSet for ward CRUD operations within a hospital.

serializer_class = <class 'app.infrastructure.serializers.WardSerializer'>
permission_classes = [<class 'rest_framework.permissions.IsAuthenticated'>, <class 'app.infrastructure.permissions.WardPermission'>]
lookup_field = 'id'
def get_queryset(self):
255    def get_queryset(self):
256        hospital_id = self.kwargs.get('hospital_pk')
257        queryset = Ward.objects.filter(
258            deleted=False,
259            block__hospital_id=hospital_id
260        )
261
262        block_id = self.request.query_params.get('block')
263        if block_id:
264            queryset = queryset.filter(block_id=block_id)
265
266        floor = self.request.query_params.get('floor')
267        if floor is not None:
268            queryset = queryset.filter(floor=floor)
269
270        return queryset

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

def get_serializer_context(self):
272    def get_serializer_context(self):
273        context = super().get_serializer_context()
274        context['hospital_id'] = self.kwargs.get('hospital_pk')
275        return context

Extra context provided to the serializer class.

def perform_create(self, serializer):
277    def perform_create(self, serializer):
278        serializer.save(created_by=self.request.user)
def destroy(self, request, *args, **kwargs):
280    def destroy(self, request, *args, **kwargs):
281        instance = self.get_object()
282        if request.user.is_superuser:
283            instance.delete()
284            return Response({"detail": "Ward deleted successfully."}, status=status.HTTP_202_ACCEPTED)
285        instance.deleted = True
286        instance.save()
287        return Response({"detail": "Ward soft-deleted successfully."}, status=status.HTTP_202_ACCEPTED)
@action(detail=True, methods=['post'], url_path='restore')
def restore(self, request, *args, **kwargs):
289    @action(detail=True, methods=['post'], url_path='restore')
290    def restore(self, request, *args, **kwargs):
291        instance = self.get_object()
292        instance.deleted = False
293        instance.save()
294        return Response({"detail": "Ward restored successfully."})
schema
name = None
description = None
suffix = None
detail = None
basename = None
@extend_schema(summary='Switch Current Block', description="Switch user's current block by providing block ID.", parameters=[OpenApiParameter(name='block_id', type=int, location='query', required=True, description='Block ID')], responses={200: OpenApiResponse(description='Block switched successfully.'), 400: OpenApiResponse(description='Bad request, block ID is required.'), 404: OpenApiResponse(description='Block not found.')})
class SwitchBlock(rest_framework.views.APIView):
296@extend_schema(
297    summary="Switch Current Block",
298    description="Switch user's current block by providing block ID.",
299    parameters=[
300        OpenApiParameter(name="block_id", type=int, location="query", required=True, description="Block ID"),
301    ],
302    responses={
303        200: OpenApiResponse(description="Block switched successfully."),
304        400: OpenApiResponse(description="Bad request, block ID is required."),
305        404: OpenApiResponse(description="Block not found.")
306    }
307)
308class SwitchBlock(APIView):
309    """
310    APIView to switch user's current block.
311    """
312    permission_classes = [IsAuthenticated]
313
314    @extend_schema(
315        summary="Switch Current Block",
316        description="This endpoint allows a user to switch their current block by providing the block ID.",
317        parameters=[
318            OpenApiParameter(name="block_id", type=int, location="query", required=True, description="Block ID"),
319        ],
320        responses={
321            200: OpenApiResponse(description="Block switched successfully."),
322            400: OpenApiResponse(description="Bad request, block ID is required."),
323            404: OpenApiResponse(description="Block not found.")
324        }
325    )
326    def post(self, request, *args, **kwargs):
327        user = request.user
328        block_id = request.data.get('block_id')
329
330        if not block_id:
331            return Response({"detail": "Block ID is required."}, status=status.HTTP_400_BAD_REQUEST)
332
333        try:
334            block = Blocks.objects.get(id=block_id, hospital=user.hospital)
335            BlockChange.objects.create(
336                user=user,
337                from_block=user.current_block if user.current_block else None,
338                to_block=block
339            )
340            user.current_block = block
341            user.save()
342            return Response({"detail": "Block switched successfully."}, status=status.HTTP_200_OK)
343        except Blocks.DoesNotExist:
344            return Response({"detail": "Block not found."}, status=status.HTTP_404_NOT_FOUND)

APIView to switch user's current block.

permission_classes = [<class 'rest_framework.permissions.IsAuthenticated'>]
@extend_schema(summary='Switch Current Block', description='This endpoint allows a user to switch their current block by providing the block ID.', parameters=[OpenApiParameter(name='block_id', type=int, location='query', required=True, description='Block ID')], responses={200: OpenApiResponse(description='Block switched successfully.'), 400: OpenApiResponse(description='Bad request, block ID is required.'), 404: OpenApiResponse(description='Block not found.')})
def post(self, request, *args, **kwargs):
314    @extend_schema(
315        summary="Switch Current Block",
316        description="This endpoint allows a user to switch their current block by providing the block ID.",
317        parameters=[
318            OpenApiParameter(name="block_id", type=int, location="query", required=True, description="Block ID"),
319        ],
320        responses={
321            200: OpenApiResponse(description="Block switched successfully."),
322            400: OpenApiResponse(description="Bad request, block ID is required."),
323            404: OpenApiResponse(description="Block not found.")
324        }
325    )
326    def post(self, request, *args, **kwargs):
327        user = request.user
328        block_id = request.data.get('block_id')
329
330        if not block_id:
331            return Response({"detail": "Block ID is required."}, status=status.HTTP_400_BAD_REQUEST)
332
333        try:
334            block = Blocks.objects.get(id=block_id, hospital=user.hospital)
335            BlockChange.objects.create(
336                user=user,
337                from_block=user.current_block if user.current_block else None,
338                to_block=block
339            )
340            user.current_block = block
341            user.save()
342            return Response({"detail": "Block switched successfully."}, status=status.HTTP_200_OK)
343        except Blocks.DoesNotExist:
344            return Response({"detail": "Block not found."}, status=status.HTTP_404_NOT_FOUND)
schema
@extend_schema(summary='Switch Current Ward', description="Switch user's current ward by providing ward ID.", parameters=[OpenApiParameter(name='ward_id', type=int, location='query', required=True, description='Ward ID')], responses={200: OpenApiResponse(description='Ward switched successfully.'), 400: OpenApiResponse(description='Bad request, ward ID is required.'), 404: OpenApiResponse(description='Ward not found.')})
class SwitchWard(rest_framework.views.APIView):
346@extend_schema(
347    summary="Switch Current Ward",
348    description="Switch user's current ward by providing ward ID.",
349    parameters=[
350        OpenApiParameter(name="ward_id", type=int, location="query", required=True, description="Ward ID"),
351    ],
352    responses={
353        200: OpenApiResponse(description="Ward switched successfully."),
354        400: OpenApiResponse(description="Bad request, ward ID is required."),
355        404: OpenApiResponse(description="Ward not found.")
356    }
357)
358class SwitchWard(APIView):
359    """
360    APIView to switch user's current ward.
361    """
362    permission_classes = [IsAuthenticated]
363
364    @extend_schema(
365        summary="Switch Current Ward",
366        description="This endpoint allows a user to switch their current ward by providing the ward ID.",
367        parameters=[
368            OpenApiParameter(name="ward_id", type=int, location="query", required=True, description="Ward ID"),
369        ],
370        responses={
371            200: OpenApiResponse(description="Ward switched successfully."),
372            400: OpenApiResponse(description="Bad request, ward ID is required."),
373            404: OpenApiResponse(description="Ward not found.")
374        }
375    )
376    def post(self, request, *args, **kwargs):
377        user = request.user
378        ward_id = request.data.get('ward_id')
379
380        if not ward_id:
381            return Response({"detail": "Ward ID is required."}, status=status.HTTP_400_BAD_REQUEST)
382
383        try:
384            ward = Ward.objects.get(id=ward_id, block__hospital=user.hospital)
385            WardChange.objects.create(
386                user=user,
387                from_ward=user.current_ward if user.current_ward else None,
388                to_ward=ward
389            )
390            user.current_ward = ward
391            user.current_block = ward.block
392            user.save()
393            return Response({"detail": "Ward switched successfully."}, status=status.HTTP_200_OK)
394        except Ward.DoesNotExist:
395            return Response({"detail": "Ward not found."}, status=status.HTTP_404_NOT_FOUND)

APIView to switch user's current ward.

permission_classes = [<class 'rest_framework.permissions.IsAuthenticated'>]
@extend_schema(summary='Switch Current Ward', description='This endpoint allows a user to switch their current ward by providing the ward ID.', parameters=[OpenApiParameter(name='ward_id', type=int, location='query', required=True, description='Ward ID')], responses={200: OpenApiResponse(description='Ward switched successfully.'), 400: OpenApiResponse(description='Bad request, ward ID is required.'), 404: OpenApiResponse(description='Ward not found.')})
def post(self, request, *args, **kwargs):
364    @extend_schema(
365        summary="Switch Current Ward",
366        description="This endpoint allows a user to switch their current ward by providing the ward ID.",
367        parameters=[
368            OpenApiParameter(name="ward_id", type=int, location="query", required=True, description="Ward ID"),
369        ],
370        responses={
371            200: OpenApiResponse(description="Ward switched successfully."),
372            400: OpenApiResponse(description="Bad request, ward ID is required."),
373            404: OpenApiResponse(description="Ward not found.")
374        }
375    )
376    def post(self, request, *args, **kwargs):
377        user = request.user
378        ward_id = request.data.get('ward_id')
379
380        if not ward_id:
381            return Response({"detail": "Ward ID is required."}, status=status.HTTP_400_BAD_REQUEST)
382
383        try:
384            ward = Ward.objects.get(id=ward_id, block__hospital=user.hospital)
385            WardChange.objects.create(
386                user=user,
387                from_ward=user.current_ward if user.current_ward else None,
388                to_ward=ward
389            )
390            user.current_ward = ward
391            user.current_block = ward.block
392            user.save()
393            return Response({"detail": "Ward switched successfully."}, status=status.HTTP_200_OK)
394        except Ward.DoesNotExist:
395            return Response({"detail": "Ward not found."}, status=status.HTTP_404_NOT_FOUND)
schema