Coverage for apps/appointments/views.py: 35%

208 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2024-05-21 11:15 -0600

1from django.db.models import Count, Sum 

2from django.utils import timezone 

3from rest_framework import status 

4from rest_framework.decorators import action 

5from rest_framework.mixins import CreateModelMixin as Create 

6from rest_framework.mixins import DestroyModelMixin as Delete 

7from rest_framework.mixins import ListModelMixin as List 

8from rest_framework.mixins import RetrieveModelMixin as Detail 

9from rest_framework.mixins import UpdateModelMixin as Update 

10from rest_framework.response import Response 

11from rest_framework.viewsets import GenericViewSet 

12from rest_framework.validators import ValidationError 

13 

14from apps.catalogues.models import PaymentMethod 

15from apps.organizations.models import Organization 

16from apps.practitioners.models import Practitioner 

17from apps.products.models import ( 

18 ConsultationProduct, 

19 HospitalizationProduct, 

20 ProcedureProduct, 

21 SellableProduct, 

22 SurgicalProduct, 

23) 

24from apps.users.mixins import AdminMixin, UserMixin 

25from apps.payments.models import OrganizationPaymentMethod, PaymentAllocation 

26from services.pusher import PusherClient 

27 

28from .models import AppointmentCharge, AppointmentType 

29from .serializers import ( 

30 AppointmentChargeExportSerializer, 

31 AppointmentChargeSerializer, 

32 AppointmentDateCountSerializer, 

33 AppointmentExportSerializer, 

34 AppointmentPractitionerSerializer, 

35 AppointmentSerializer, 

36 AppointmentTypeSerializer, 

37 AppointmentSummarySerializer, 

38) 

39 

40 

41class AdminAppointmentViewSet(AdminMixin, GenericViewSet, Create, List, Detail, Update, Delete): 

42 """ 

43 Model ViewSet for Appointments 

44 """ 

45 

46 serializer_class = AppointmentSerializer 

47 

48 

49class AdminAppointmentChargeViewSet(AdminMixin, GenericViewSet, Create, List, Detail, Update, Delete): 

50 """ 

51 Model ViewSet for Appointment Charges 

52 """ 

53 

54 serializer_class = AppointmentChargeSerializer 

55 

56 

57class UserAppointmentViewSet(UserMixin, GenericViewSet, Create, List, Detail, Update, Delete): 

58 """ 

59 Model ViewSet for Appointments 

60 """ 

61 

62 serializer_class = AppointmentSerializer 

63 filterset_fields = { 

64 "practitioners__practitioner": ["exact"], 

65 "organization": ["exact"], 

66 "date": ["lte", "gte", "exact"], 

67 } 

68 

69 def _push_notification(self, channel: str, event: str = "appointment-status-update", data: dict = {}): 

70 pusher = PusherClient() 

71 pusher.trigger(channel, event, data) 

72 

73 def perform_update(self, serializer): 

74 appointment = serializer.save() 

75 try: 

76 self._push_notification( 

77 appointment.organization.random_slug, 

78 "appointment-update", 

79 { 

80 "user": getattr(self.request.user, "pk", None), 

81 }, 

82 ) 

83 except Exception as e: 

84 print(e) 

85 return appointment 

86 

87 def perform_create(self, serializer): 

88 appointment = serializer.save() 

89 try: 

90 self._push_notification( 

91 appointment.organization.random_slug, 

92 "appointment-creation", 

93 { 

94 "user": getattr(self.request.user, "pk", None), 

95 }, 

96 ) 

97 except Exception as e: 

98 print(e) 

99 return appointment 

100 

101 def perform_destroy(self, instance): 

102 appointment = instance 

103 instance.delete() 

104 try: 

105 self._push_notification( 

106 appointment.organization.random_slug, 

107 "appointment-deletion", 

108 { 

109 "user": getattr(self.request.user, "pk", None), 

110 }, 

111 ) 

112 except Exception as e: 

113 print(e) 

114 return appointment 

115 

116 @action(methods=["GET"], detail=False, url_path="daycount") 

117 def day_count(self, request, *args, **kwargs): 

118 self.serializer = AppointmentDateCountSerializer 

119 queryset = super().get_queryset().order_by("date").values("date").annotate(count=Count("pk")) 

120 queryset = self.filter_queryset(queryset) 

121 page = self.paginate_queryset(queryset) 

122 if page is not None: 

123 serializer = self.serializer(page, many=True) 

124 return self.get_paginated_response(serializer.data) 

125 serializer = self.serializer(queryset, many=True) 

126 return Response(serializer.data) 

127 

128 @action(methods=["GET"], detail=False) 

129 def today(self, request, *args, **kwargs): 

130 queryset = self.filter_queryset(self.get_queryset().filter(date=timezone.now().astimezone().date())) 

131 page = self.paginate_queryset(queryset) 

132 if page is not None: 

133 serializer = self.get_serializer(page, many=True) 

134 return self.get_paginated_response(serializer.data) 

135 serializer = self.get_serializer(queryset, many=True) 

136 return Response(serializer.data) 

137 

138 @action(methods=["PATCH"], detail=True) 

139 def cancel(self, request, *args, **kwargs): 

140 appointment = self.get_object() 

141 appointment.status = AppointmentSerializer.Meta.model.StatusChoices.CANCELED 

142 appointment.patient_canceled() 

143 appointment.save() 

144 self._push_notification( 

145 appointment.organization.random_slug, 

146 "appointment-deletion", 

147 { 

148 "user": getattr(self.request.user, "pk", None), 

149 }, 

150 ) 

151 return Response(self.serializer_class(appointment).data) 

152 

153 @action(methods=["PATCH"], detail=True) 

154 def confirm(self, request, *args, **kwargs): 

155 appointment = self.get_object() 

156 appointment.is_confirmed = True 

157 appointment.patient_confirmed() 

158 appointment.save() 

159 self._push_notification( 

160 appointment.organization.random_slug, 

161 "appointment-deletion", 

162 { 

163 "user": getattr(self.request.user, "pk", None), 

164 }, 

165 ) 

166 return Response(self.serializer_class(appointment).data) 

167 

168 @action(methods=["PATCH"], detail=True) 

169 def arrival(self, request, *args, **kwargs): 

170 # TODO: Whatsapp to practitioner 

171 appointment = self.get_object() 

172 appointment.arrival_timestamp = timezone.datetime.now() 

173 appointment.patient_arrived() 

174 appointment.save() 

175 self._push_notification( 

176 appointment.organization.random_slug, 

177 "appointment-deletion", 

178 { 

179 "user": getattr(self.request.user, "pk", None), 

180 }, 

181 ) 

182 return Response(self.serializer_class(appointment).data) 

183 

184 @action(methods=["PATCH"], detail=True) 

185 def start(self, request, *args, **kwargs): 

186 appointment = self.get_object() 

187 appointment.start_timestamp = timezone.datetime.now() 

188 appointment.patient_entered() 

189 appointment.save() 

190 self._push_notification( 

191 appointment.organization.random_slug, 

192 "appointment-deletion", 

193 { 

194 "user": getattr(self.request.user, "pk", None), 

195 }, 

196 ) 

197 return Response(self.serializer_class(appointment).data) 

198 

199 @action(methods=["PATCH"], detail=True) 

200 def end(self, request, *args, **kwargs): 

201 appointment = self.get_object() 

202 appointment.end_timestamp = timezone.datetime.now() 

203 appointment.patient_exited() 

204 appointment.save() 

205 self._push_notification( 

206 appointment.organization.random_slug, 

207 "appointment-deletion", 

208 { 

209 "user": getattr(self.request.user, "pk", None), 

210 }, 

211 ) 

212 return Response(self.serializer_class(appointment).data) 

213 

214 @action(methods=["PATCH"], detail=True) 

215 def departure(self, request, *args, **kwargs): 

216 # TODO: Whatsapp to practitioner and patient 

217 appointment = self.get_object() 

218 appointment.departure_timestamp = timezone.datetime.now() 

219 appointment.save() 

220 appointment.patient_departed() 

221 appointment.save() 

222 self._push_notification( 

223 appointment.organization.random_slug, 

224 "appointment-deletion", 

225 { 

226 "user": getattr(self.request.user, "pk", None), 

227 }, 

228 ) 

229 return Response(self.serializer_class(appointment).data) 

230 

231 @action(methods=["GET"], detail=False) 

232 def summary(self, request, serializer_class=AppointmentSummarySerializer, *args, **kwargs): 

233 if getattr(self.request.user, "practitioner", None): 

234 practitioner = self.request.user.practitioner 

235 else: 

236 try: 

237 practitioner = Practitioner.objects.get(pk=request.query_params.get("practitioner")) 

238 except Practitioner.DoesNotExist: 

239 practitioner = None 

240 

241 serializer_data = request.query_params.dict() 

242 serializer_data.update({"practitioner": practitioner.pk if practitioner else practitioner}) 

243 

244 serializer = serializer_class(data=serializer_data) 

245 serializer.is_valid(raise_exception=True) 

246 from_date = serializer.validated_data.get("from_date") 

247 to_date = serializer.validated_data.get("to_date") 

248 

249 products = practitioner.products.all() 

250 payment_methods = PaymentMethod.objects.all() 

251 

252 products_mapping = { 

253 "Consultas": products.instance_of(ConsultationProduct), 

254 "Procedimientos": products.instance_of(ProcedureProduct), 

255 "Productos": products.instance_of(SellableProduct), 

256 "Cirugias": products.instance_of(SurgicalProduct), 

257 "Hospitalizaciones": products.instance_of(HospitalizationProduct), 

258 } 

259 

260 final_response_data = [] 

261 

262 appointment_charges = AppointmentCharge.objects.filter(date__gte=from_date, date__lte=to_date).filter( 

263 product__in=products.values_list("pk", flat=True) 

264 ) 

265 

266 for type_of, products in products_mapping.items(): 

267 appointment_charges_by_product = appointment_charges.filter( 

268 product__in=products.values_list("pk", flat=True) 

269 ) 

270 product_mapping = { 

271 "type_of": type_of, 

272 "total": appointment_charges_by_product.aggregate(total=Sum("price")).get("total") or 0, 

273 "by_payment_method": [], 

274 } 

275 payment_allocations = PaymentAllocation.objects.filter( 

276 appointment_charge__in=appointment_charges_by_product.values_list("pk", flat=True) 

277 ) 

278 for payment_method in payment_methods: 

279 payment_allocations_by_method = payment_allocations.filter( 

280 payment__payment_method__sat_id=payment_method.sat_id 

281 ) 

282 product_mapping["by_payment_method"].append( 

283 { 

284 "name": payment_method.name, 

285 "amount": payment_allocations_by_method.aggregate(total=Sum("amount")).get("total") or 0, 

286 } 

287 ) 

288 product_mapping["pending_amount"] = product_mapping["total"] - ( 

289 payment_allocations.aggregate(total=Sum("amount")).get("total") or 0 

290 ) 

291 product_mapping["products"] = [] 

292 for product in products: 

293 appointment_product_detail = appointment_charges_by_product.filter(product=product) 

294 amount = int(appointment_product_detail.aggregate(amount=Sum("quantity")).get("amount") or 0) 

295 product_mapping["products"].append( 

296 { 

297 "name": product.name, 

298 "total": (appointment_product_detail.aggregate(total=Sum("price")).get("total") or 0), 

299 "quantity": amount, 

300 } 

301 ) 

302 final_response_data.append(product_mapping) 

303 

304 return Response(final_response_data) 

305 

306 def get_queryset(self): 

307 return super().get_queryset().filter(organization__in=self.user.affiliations.values_list("organization")) 

308 

309 def destroy(self, request, *args, **kwargs): 

310 user = request.user 

311 appointment = self.get_object() 

312 owner_practitioners = getattr(user, "practitioner", None) in appointment.owner_practitioners() 

313 ended_appointment = appointment.status in [ 

314 appointment.StatusChoices.FINISHED, 

315 appointment.StatusChoices.OVERDUE, 

316 appointment.StatusChoices.CLOSED, 

317 ] 

318 if not owner_practitioners and ended_appointment: 

319 content = {"error": "you dont have permissions to delete this appointment"} 

320 return Response(content, status=status.HTTP_403_FORBIDDEN) 

321 return super().destroy(request, *args, **kwargs) 

322 

323 

324class UserAppointmentPractitionerViewSet(UserMixin, GenericViewSet, Create, List, Update, Delete): 

325 """ 

326 Model ViewSet for Appointment Practitioner 

327 """ 

328 

329 serializer_class = AppointmentPractitionerSerializer 

330 

331 

332class UserAppointmentChargeViewSet(UserMixin, GenericViewSet, Create, List, Detail, Update, Delete): 

333 """ 

334 Model ViewSet for Appointment Charges 

335 """ 

336 

337 serializer_class = AppointmentChargeSerializer 

338 filterset_fields = { 

339 "appointment": ["exact"], 

340 "appointment__patient": ["exact"], 

341 # "pending_amount": ["exact", "lte", "gte", "gt"], 

342 } 

343 

344 def create(self, request, *args, **kwargs): 

345 request.data["created_by"] = request.user.pk 

346 return super().create(request, args, kwargs) 

347 

348 def get_queryset(self): 

349 return ( 

350 super() 

351 .get_queryset() 

352 .filter(appointment__organization__in=self.user.affiliations.values_list("organization")) 

353 ) 

354 

355 @action(methods=["GET"], detail=False) 

356 def summary(self, request, *args, **kwargs): 

357 date = request.GET.get("date", timezone.now().date()) 

358 qs = self.get_queryset().filter(appointment__date=date) 

359 

360 

361class UserAppointmentTypeViewSet(UserMixin, GenericViewSet, Create, List, Update): 

362 """ 

363 Appointment Type ViewSet for User 

364 """ 

365 

366 serializer_class = AppointmentTypeSerializer 

367 filterset_fields = { 

368 "practitioner": ["exact"], 

369 } 

370 

371 def get_queryset(self): 

372 if hasattr(self.user, "practitioner"): 

373 return super().get_queryset().filter(practitioner=self.user.practitioner, is_active=True) 

374 

375 # TODO: Refactor this 

376 b = Organization.objects.filter(affiliation__user=self.user) 

377 print(b) 

378 objs = AppointmentType.objects.none() 

379 for org in b: 

380 for aff in org.affiliation_set.all(): 

381 if hasattr(aff.user, "practitioner"): 

382 apptyp = AppointmentType.objects.filter(practitioner=aff.user.practitioner, is_active=True) 

383 objs = objs | apptyp 

384 return objs 

385 

386 

387class UserAppointmentExportViewSet(UserMixin, GenericViewSet, Create): 

388 """ 

389 ViewSet for AppointmentExport 

390 """ 

391 

392 serializer_class = AppointmentExportSerializer 

393 

394 def create(self, request, *args, **kwargs): 

395 if hasattr(self.user, "practitioner") and getattr(request.data, "practitioner", False): 

396 request.data["practitioner"] = self.user.practitioner.pk 

397 return super().create(request, args, kwargs) 

398 

399 

400class UserAppointmentChargeExportViewSet(UserMixin, GenericViewSet, Create): 

401 """ 

402 ViewSet for AppointmentChargeExport 

403 """ 

404 

405 serializer_class = AppointmentChargeExportSerializer 

406 

407 def create(self, request, *args, **kwargs): 

408 if hasattr(self.user, "practitioner") and getattr(request.data, "practitioner", False): 

409 request.data["practitioner"] = self.user.practitioner.pk 

410 return super().create(request, args, kwargs)