accounts.views
1from datetime import timezone 2from drf_spectacular.utils import extend_schema, OpenApiTypes, OpenApiResponse 3from django.shortcuts import render, redirect 4from django.urls import reverse 5from .models import SiteUser, Hospital, User,Role 6from rest_framework.views import APIView 7from rest_framework.response import Response 8from rest_framework import status 9from django.contrib.auth import authenticate 10from rest_framework.authtoken.models import Token 11from .serializers import UserSerializer, AuthTokenRequestSerializer, AuthTokenResponseSerializer,HospitalSerializer 12from django.db.models import Q 13from django.db import IntegrityError 14from rest_framework.permissions import IsAuthenticated 15from datetime import datetime 16 17""" 18Accounts app views for admin dashboard, hospital/user management, and authentication. 19 20Endpoints documented: 21- Admin login/logout/dashboard/profile management 22- Hospital CRUD and user management via admin dashboard 23- Custom authentication token API 24- Logout and refresh user token API 25 26All API endpoints are documented with drf-spectacular's @extend_schema for OpenAPI generation. 27""" 28 29def admin_login(request): 30 """ 31 Admin login view. 32 GET: Render login page. 33 POST: Authenticate admin and start session. 34 """ 35 if request.method == 'POST': 36 name = request.POST.get('name') 37 password = request.POST.get('password') 38 user = SiteUser.objects.filter(name=name).first() 39 if user and user.check_password(password): 40 request.session['admin'] = True 41 request.session['admin_name'] = name 42 request.session['admin_id'] = user.id 43 return redirect('admin_page') 44 else: 45 return render(request, 'accounts/admin_login.html', {'error': 'Invalid credentials'}) 46 return render(request, 'accounts/admin_login.html') 47 48def admin_logout(request): 49 """ 50 Admin logout view. Flushes session and redirects to login. 51 """ 52 request.session.flush() 53 return redirect('admin_login') 54 55def admin_dashboard(request): 56 """ 57 Admin dashboard view. Requires admin session. 58 """ 59 if not request.session.get('admin'): 60 return redirect('admin_login') 61 return render(request, 'accounts/admin_dashboard.html') 62 63def admin_page(request): 64 """ 65 Admin management page. 66 GET: List all admins. 67 POST: Create new admin. 68 """ 69 if not request.session.get('admin'): 70 return redirect('admin_login') 71 if request.method == 'POST': 72 name = request.POST.get('name') 73 password = request.POST.get('password') 74 user = SiteUser.objects.create(name=name, password=password) 75 return redirect('admin_page') 76 admins = SiteUser.objects.all() 77 return render(request, 'accounts/admin_page.html', {'admins': admins}) 78 79def delete_siteuser(request, admin_id): 80 """ 81 Delete an admin user. 82 """ 83 if not request.session.get('admin'): 84 return redirect('admin_login') 85 SiteUser.objects.filter(id=admin_id).delete() 86 return redirect('admin_page') 87 88def admin_profile(request): 89 """ 90 Admin profile view. 91 GET: Render profile page. 92 POST: Update admin profile. 93 """ 94 if not request.session.get('admin'): 95 return redirect('admin_login') 96 if request.method == 'POST': 97 name = request.POST.get('name') 98 password = request.POST.get('password') 99 user = SiteUser.objects.filter(name=name).first() 100 if user and user.check_password(password): 101 user.name = name 102 user.password = password 103 user.save() 104 return redirect('admin_profile') 105 else: 106 return render(request, 'accounts/admin_profile.html', {'error': 'Invalid credentials'}) 107 return render(request, 'accounts/admin_profile.html') 108 109def hospitals(request): 110 """ 111 Hospital management view. 112 GET: List all hospitals. 113 POST: Create new hospital and default ADMIN role. 114 """ 115 if not request.session.get('admin'): 116 return redirect('admin_login') 117 if request.method == 'POST': 118 name = request.POST.get('name') 119 address = request.POST.get('address') 120 hospital = Hospital.objects.create(name=name, address=address) 121 Role.objects.get_or_create(name="ADMIN",is_protected=True,hospital=hospital,is_approver=True,is_responder=True,is_creator=True) 122 return redirect('hospitals') 123 hospitals = Hospital.objects.all() 124 return render(request, 'accounts/hospitals.html', {'hospitals': hospitals}) 125 126def delete_hospital(request, hospital_id): 127 """ 128 Delete a hospital. 129 """ 130 if not request.session.get('admin'): 131 return redirect('admin_login') 132 Hospital.objects.filter(id=hospital_id).delete() 133 return redirect('hospitals') 134 135def switch_hospital(request, hospital_id): 136 """ 137 Switch current hospital for a user (hardcoded phone number). 138 """ 139 if not request.session.get('admin'): 140 return redirect('admin_login') 141 User.objects.filter(phone_number=1234567890).update(hospital_id=hospital_id,role=Role.objects.get(name="ADMIN",hospital_id=hospital_id)) 142 return redirect('http://localhost:3000/logout/') 143 144def hospital_users(request , hospital_id): 145 """ 146 Hospital user management view. 147 GET: List hospital staff/superusers. 148 POST: Create new hospital user with ADMIN role. 149 """ 150 if not request.session.get('admin'): 151 return redirect('admin_login') 152 hospital = Hospital.objects.get(id=hospital_id) 153 if request.method == 'POST': 154 try: 155 institution_id = request.POST.get('institution_id') 156 phone_number = request.POST.get('phone_number') 157 user = User.objects.create_user(institution_id=institution_id, role=Role.objects.get(name="ADMIN",hospital=hospital), phone_number=phone_number, hospital=hospital) 158 except Role.DoesNotExist as e: 159 return render(request, 'accounts/hospital_users.html', {'error': "it seems that the role does not exist, please create a role first"}) 160 except IntegrityError as e: 161 return render(request, 'accounts/hospital_users.html', {'error': "it seems that the phone number or institution ID already exists, please try again with a different one"}) 162 163 return redirect('hospital_users', hospital_id=hospital_id) 164 users = User.objects.filter(hospital=hospital).filter(Q(is_staff=True) | Q(is_superuser=True)) 165 return render(request, 'accounts/hospital_users.html', {'users': users}) 166 167def delete_hospital_user(request, hospital_id, user_id): 168 """ 169 Delete a hospital user. 170 """ 171 if not request.session.get('admin'): 172 return redirect('admin_login') 173 User.objects.filter(id=user_id, hospital_id=hospital_id).delete() 174 return redirect('hospital_users', hospital_id=hospital_id) 175 176# ---- custom auth token view 177 178class CustomAuthToken(APIView): 179 """ 180 Custom authentication token API. 181 182 POST: 183 - Authenticates user using phone and password. 184 - Returns token, user info, and hospital info. 185 """ 186 permission_classes = [] 187 @extend_schema( 188 request=AuthTokenRequestSerializer, 189 responses={ 190 200: AuthTokenResponseSerializer, 191 400: OpenApiTypes.OBJECT, 192 404: OpenApiTypes.OBJECT, 193 }, 194 summary="Custom Auth Token", 195 description="Authenticate using institution_id and password under a specific hospital ID (passed in URL)." 196 ) 197 def post(self, request, *args, **kwargs): 198 try: 199 phone = request.data.get('phone') 200 password = request.data.get('password') 201 fcm_token = request.data.get('fcm_token', None) 202 print(f"request.data: {request.data}") 203 if not phone or not password: 204 return Response({'error': 'Phone number and password are required.'}, status=status.HTTP_400_BAD_REQUEST) 205 user = authenticate(request, phone_number=phone, password=password) 206 if user is not None: 207 token, created = Token.objects.get_or_create(user=user) 208 user.is_logged_in = True 209 user.fcm_token = fcm_token if fcm_token else user.fcm_token 210 user.last_login = datetime.now() 211 user.fcm_token_updated_at = datetime.now() if fcm_token else user.fcm_token_updated_at 212 user.save() 213 user_data = UserSerializer(user).data 214 data = {'token': token.key, 'user': user_data, 'hospital':HospitalSerializer(user.hospital).data} 215 return Response(data, status=status.HTTP_200_OK) 216 else: 217 return Response({'error': 'Invalid credentials.'}, status=status.HTTP_400_BAD_REQUEST) 218 except Hospital.DoesNotExist: 219 return Response({'error': 'Hospital does not exist.'}, status=status.HTTP_404_NOT_FOUND) 220 221class LogoutView(APIView): 222 """ 223 Logout API for authenticated users. 224 225 POST: 226 - Logs out the user and updates login status. 227 - Returns success or error message. 228 """ 229 permission_classes = [IsAuthenticated] 230 @extend_schema( 231 summary="Logout user", 232 description="Logs out the authenticated user.", 233 responses={ 234 200: OpenApiResponse(description="Logged out successfully"), 235 400: OpenApiResponse(description="Invalid token"), 236 } 237 ) 238 def post(self, request): 239 try: 240 if request.user.is_superuser: 241 request.user.hospital = None 242 request.user.save() 243 else: 244 request.user.is_logged_in = False 245 request.user.save() 246 return Response({"message": "Logged out successfully"}, status=status.HTTP_200_OK) 247 except (AttributeError, Token.DoesNotExist): 248 return Response({"message": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST) 249 250class RefreshUserView(APIView): 251 """ 252 Refresh user token API. 253 254 POST: 255 - Returns refreshed token and user info. 256 """ 257 permission_classes = [IsAuthenticated] 258 @extend_schema( 259 summary="Refresh user token", 260 description="Refreshes and returns the user's token and user's info.", 261 responses={ 262 200: AuthTokenResponseSerializer, 263 400: OpenApiResponse(description="Invalid token"), 264 } 265 ) 266 def post(self, request): 267 try: 268 user = request.user 269 token, created = Token.objects.get_or_create(user=user) 270 user_data = UserSerializer(user).data 271 data = {'token': token.key, 'user': user_data, 'hospital':HospitalSerializer(user.hospital).data} 272 return Response(data, status=status.HTTP_200_OK) 273 274 except (AttributeError, Token.DoesNotExist): 275 return Response({"message": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST)
30def admin_login(request): 31 """ 32 Admin login view. 33 GET: Render login page. 34 POST: Authenticate admin and start session. 35 """ 36 if request.method == 'POST': 37 name = request.POST.get('name') 38 password = request.POST.get('password') 39 user = SiteUser.objects.filter(name=name).first() 40 if user and user.check_password(password): 41 request.session['admin'] = True 42 request.session['admin_name'] = name 43 request.session['admin_id'] = user.id 44 return redirect('admin_page') 45 else: 46 return render(request, 'accounts/admin_login.html', {'error': 'Invalid credentials'}) 47 return render(request, 'accounts/admin_login.html')
Admin login view. GET: Render login page. POST: Authenticate admin and start session.
49def admin_logout(request): 50 """ 51 Admin logout view. Flushes session and redirects to login. 52 """ 53 request.session.flush() 54 return redirect('admin_login')
Admin logout view. Flushes session and redirects to login.
56def admin_dashboard(request): 57 """ 58 Admin dashboard view. Requires admin session. 59 """ 60 if not request.session.get('admin'): 61 return redirect('admin_login') 62 return render(request, 'accounts/admin_dashboard.html')
Admin dashboard view. Requires admin session.
64def admin_page(request): 65 """ 66 Admin management page. 67 GET: List all admins. 68 POST: Create new admin. 69 """ 70 if not request.session.get('admin'): 71 return redirect('admin_login') 72 if request.method == 'POST': 73 name = request.POST.get('name') 74 password = request.POST.get('password') 75 user = SiteUser.objects.create(name=name, password=password) 76 return redirect('admin_page') 77 admins = SiteUser.objects.all() 78 return render(request, 'accounts/admin_page.html', {'admins': admins})
Admin management page. GET: List all admins. POST: Create new admin.
80def delete_siteuser(request, admin_id): 81 """ 82 Delete an admin user. 83 """ 84 if not request.session.get('admin'): 85 return redirect('admin_login') 86 SiteUser.objects.filter(id=admin_id).delete() 87 return redirect('admin_page')
Delete an admin user.
89def admin_profile(request): 90 """ 91 Admin profile view. 92 GET: Render profile page. 93 POST: Update admin profile. 94 """ 95 if not request.session.get('admin'): 96 return redirect('admin_login') 97 if request.method == 'POST': 98 name = request.POST.get('name') 99 password = request.POST.get('password') 100 user = SiteUser.objects.filter(name=name).first() 101 if user and user.check_password(password): 102 user.name = name 103 user.password = password 104 user.save() 105 return redirect('admin_profile') 106 else: 107 return render(request, 'accounts/admin_profile.html', {'error': 'Invalid credentials'}) 108 return render(request, 'accounts/admin_profile.html')
Admin profile view. GET: Render profile page. POST: Update admin profile.
110def hospitals(request): 111 """ 112 Hospital management view. 113 GET: List all hospitals. 114 POST: Create new hospital and default ADMIN role. 115 """ 116 if not request.session.get('admin'): 117 return redirect('admin_login') 118 if request.method == 'POST': 119 name = request.POST.get('name') 120 address = request.POST.get('address') 121 hospital = Hospital.objects.create(name=name, address=address) 122 Role.objects.get_or_create(name="ADMIN",is_protected=True,hospital=hospital,is_approver=True,is_responder=True,is_creator=True) 123 return redirect('hospitals') 124 hospitals = Hospital.objects.all() 125 return render(request, 'accounts/hospitals.html', {'hospitals': hospitals})
Hospital management view. GET: List all hospitals. POST: Create new hospital and default ADMIN role.
127def delete_hospital(request, hospital_id): 128 """ 129 Delete a hospital. 130 """ 131 if not request.session.get('admin'): 132 return redirect('admin_login') 133 Hospital.objects.filter(id=hospital_id).delete() 134 return redirect('hospitals')
Delete a hospital.
136def switch_hospital(request, hospital_id): 137 """ 138 Switch current hospital for a user (hardcoded phone number). 139 """ 140 if not request.session.get('admin'): 141 return redirect('admin_login') 142 User.objects.filter(phone_number=1234567890).update(hospital_id=hospital_id,role=Role.objects.get(name="ADMIN",hospital_id=hospital_id)) 143 return redirect('http://localhost:3000/logout/')
Switch current hospital for a user (hardcoded phone number).
145def hospital_users(request , hospital_id): 146 """ 147 Hospital user management view. 148 GET: List hospital staff/superusers. 149 POST: Create new hospital user with ADMIN role. 150 """ 151 if not request.session.get('admin'): 152 return redirect('admin_login') 153 hospital = Hospital.objects.get(id=hospital_id) 154 if request.method == 'POST': 155 try: 156 institution_id = request.POST.get('institution_id') 157 phone_number = request.POST.get('phone_number') 158 user = User.objects.create_user(institution_id=institution_id, role=Role.objects.get(name="ADMIN",hospital=hospital), phone_number=phone_number, hospital=hospital) 159 except Role.DoesNotExist as e: 160 return render(request, 'accounts/hospital_users.html', {'error': "it seems that the role does not exist, please create a role first"}) 161 except IntegrityError as e: 162 return render(request, 'accounts/hospital_users.html', {'error': "it seems that the phone number or institution ID already exists, please try again with a different one"}) 163 164 return redirect('hospital_users', hospital_id=hospital_id) 165 users = User.objects.filter(hospital=hospital).filter(Q(is_staff=True) | Q(is_superuser=True)) 166 return render(request, 'accounts/hospital_users.html', {'users': users})
Hospital user management view. GET: List hospital staff/superusers. POST: Create new hospital user with ADMIN role.
168def delete_hospital_user(request, hospital_id, user_id): 169 """ 170 Delete a hospital user. 171 """ 172 if not request.session.get('admin'): 173 return redirect('admin_login') 174 User.objects.filter(id=user_id, hospital_id=hospital_id).delete() 175 return redirect('hospital_users', hospital_id=hospital_id)
Delete a hospital user.
179class CustomAuthToken(APIView): 180 """ 181 Custom authentication token API. 182 183 POST: 184 - Authenticates user using phone and password. 185 - Returns token, user info, and hospital info. 186 """ 187 permission_classes = [] 188 @extend_schema( 189 request=AuthTokenRequestSerializer, 190 responses={ 191 200: AuthTokenResponseSerializer, 192 400: OpenApiTypes.OBJECT, 193 404: OpenApiTypes.OBJECT, 194 }, 195 summary="Custom Auth Token", 196 description="Authenticate using institution_id and password under a specific hospital ID (passed in URL)." 197 ) 198 def post(self, request, *args, **kwargs): 199 try: 200 phone = request.data.get('phone') 201 password = request.data.get('password') 202 fcm_token = request.data.get('fcm_token', None) 203 print(f"request.data: {request.data}") 204 if not phone or not password: 205 return Response({'error': 'Phone number and password are required.'}, status=status.HTTP_400_BAD_REQUEST) 206 user = authenticate(request, phone_number=phone, password=password) 207 if user is not None: 208 token, created = Token.objects.get_or_create(user=user) 209 user.is_logged_in = True 210 user.fcm_token = fcm_token if fcm_token else user.fcm_token 211 user.last_login = datetime.now() 212 user.fcm_token_updated_at = datetime.now() if fcm_token else user.fcm_token_updated_at 213 user.save() 214 user_data = UserSerializer(user).data 215 data = {'token': token.key, 'user': user_data, 'hospital':HospitalSerializer(user.hospital).data} 216 return Response(data, status=status.HTTP_200_OK) 217 else: 218 return Response({'error': 'Invalid credentials.'}, status=status.HTTP_400_BAD_REQUEST) 219 except Hospital.DoesNotExist: 220 return Response({'error': 'Hospital does not exist.'}, status=status.HTTP_404_NOT_FOUND)
Custom authentication token API.
POST: - Authenticates user using phone and password. - Returns token, user info, and hospital info.
188 @extend_schema( 189 request=AuthTokenRequestSerializer, 190 responses={ 191 200: AuthTokenResponseSerializer, 192 400: OpenApiTypes.OBJECT, 193 404: OpenApiTypes.OBJECT, 194 }, 195 summary="Custom Auth Token", 196 description="Authenticate using institution_id and password under a specific hospital ID (passed in URL)." 197 ) 198 def post(self, request, *args, **kwargs): 199 try: 200 phone = request.data.get('phone') 201 password = request.data.get('password') 202 fcm_token = request.data.get('fcm_token', None) 203 print(f"request.data: {request.data}") 204 if not phone or not password: 205 return Response({'error': 'Phone number and password are required.'}, status=status.HTTP_400_BAD_REQUEST) 206 user = authenticate(request, phone_number=phone, password=password) 207 if user is not None: 208 token, created = Token.objects.get_or_create(user=user) 209 user.is_logged_in = True 210 user.fcm_token = fcm_token if fcm_token else user.fcm_token 211 user.last_login = datetime.now() 212 user.fcm_token_updated_at = datetime.now() if fcm_token else user.fcm_token_updated_at 213 user.save() 214 user_data = UserSerializer(user).data 215 data = {'token': token.key, 'user': user_data, 'hospital':HospitalSerializer(user.hospital).data} 216 return Response(data, status=status.HTTP_200_OK) 217 else: 218 return Response({'error': 'Invalid credentials.'}, status=status.HTTP_400_BAD_REQUEST) 219 except Hospital.DoesNotExist: 220 return Response({'error': 'Hospital does not exist.'}, status=status.HTTP_404_NOT_FOUND)
222class LogoutView(APIView): 223 """ 224 Logout API for authenticated users. 225 226 POST: 227 - Logs out the user and updates login status. 228 - Returns success or error message. 229 """ 230 permission_classes = [IsAuthenticated] 231 @extend_schema( 232 summary="Logout user", 233 description="Logs out the authenticated user.", 234 responses={ 235 200: OpenApiResponse(description="Logged out successfully"), 236 400: OpenApiResponse(description="Invalid token"), 237 } 238 ) 239 def post(self, request): 240 try: 241 if request.user.is_superuser: 242 request.user.hospital = None 243 request.user.save() 244 else: 245 request.user.is_logged_in = False 246 request.user.save() 247 return Response({"message": "Logged out successfully"}, status=status.HTTP_200_OK) 248 except (AttributeError, Token.DoesNotExist): 249 return Response({"message": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST)
Logout API for authenticated users.
POST: - Logs out the user and updates login status. - Returns success or error message.
231 @extend_schema( 232 summary="Logout user", 233 description="Logs out the authenticated user.", 234 responses={ 235 200: OpenApiResponse(description="Logged out successfully"), 236 400: OpenApiResponse(description="Invalid token"), 237 } 238 ) 239 def post(self, request): 240 try: 241 if request.user.is_superuser: 242 request.user.hospital = None 243 request.user.save() 244 else: 245 request.user.is_logged_in = False 246 request.user.save() 247 return Response({"message": "Logged out successfully"}, status=status.HTTP_200_OK) 248 except (AttributeError, Token.DoesNotExist): 249 return Response({"message": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST)
251class RefreshUserView(APIView): 252 """ 253 Refresh user token API. 254 255 POST: 256 - Returns refreshed token and user info. 257 """ 258 permission_classes = [IsAuthenticated] 259 @extend_schema( 260 summary="Refresh user token", 261 description="Refreshes and returns the user's token and user's info.", 262 responses={ 263 200: AuthTokenResponseSerializer, 264 400: OpenApiResponse(description="Invalid token"), 265 } 266 ) 267 def post(self, request): 268 try: 269 user = request.user 270 token, created = Token.objects.get_or_create(user=user) 271 user_data = UserSerializer(user).data 272 data = {'token': token.key, 'user': user_data, 'hospital':HospitalSerializer(user.hospital).data} 273 return Response(data, status=status.HTTP_200_OK) 274 275 except (AttributeError, Token.DoesNotExist): 276 return Response({"message": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST)
Refresh user token API.
POST: - Returns refreshed token and user info.
259 @extend_schema( 260 summary="Refresh user token", 261 description="Refreshes and returns the user's token and user's info.", 262 responses={ 263 200: AuthTokenResponseSerializer, 264 400: OpenApiResponse(description="Invalid token"), 265 } 266 ) 267 def post(self, request): 268 try: 269 user = request.user 270 token, created = Token.objects.get_or_create(user=user) 271 user_data = UserSerializer(user).data 272 data = {'token': token.key, 'user': user_data, 'hospital':HospitalSerializer(user.hospital).data} 273 return Response(data, status=status.HTTP_200_OK) 274 275 except (AttributeError, Token.DoesNotExist): 276 return Response({"message": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST)