from typing import Optional from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession from starlette import status from datetime import date from app.application.appointment_types_repository import AppointmentTypesRepository from app.application.patients_repository import PatientsRepository from app.application.scheduled_appointments_repository import ScheduledAppointmentsRepository from app.application.users_repository import UsersRepository from app.domain.entities.scheduled_appointment import ScheduledAppointmentEntity from app.domain.models import ScheduledAppointment class ScheduledAppointmentsService: def __init__(self, db: AsyncSession): self.scheduled_appointment_repository = ScheduledAppointmentsRepository(db) self.appointment_types_repository = AppointmentTypesRepository(db) self.users_repository = UsersRepository(db) self.patients_repository = PatientsRepository(db) async def get_all_scheduled_appointments(self, start_date: date | None = None, end_date: date | None = None) -> \ list[ScheduledAppointmentEntity]: scheduled_appointments = await self.scheduled_appointment_repository.get_all(start_date=start_date, end_date=end_date) return [self.model_to_entity(scheduled_appointment) for scheduled_appointment in scheduled_appointments] async def get_scheduled_appointments_by_doctor_id(self, doctor_id: int, start_date: date | None = None, end_date: date | None = None) -> Optional[ list[ScheduledAppointmentEntity]]: doctor = await self.users_repository.get_by_id(doctor_id) if not doctor: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Доктор с таким ID не найден', ) scheduled_appointments = await self.scheduled_appointment_repository.get_by_doctor_id(doctor_id, start_date=start_date, end_date=end_date) return [self.model_to_entity(scheduled_appointment) for scheduled_appointment in scheduled_appointments] async def get_upcoming_scheduled_appointments_by_doctor_id(self, doctor_id: int) -> Optional[ list[ScheduledAppointmentEntity]]: doctor = await self.users_repository.get_by_id(doctor_id) if not doctor: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Доктор с таким ID не найден', ) scheduled_appointments = await self.scheduled_appointment_repository.get_upcoming_by_doctor_id(doctor_id) return [self.model_to_entity(scheduled_appointment) for scheduled_appointment in scheduled_appointments] async def get_scheduled_appointments_by_patient_id(self, patient_id: int, start_date: date | None = None, end_date: date | None = None) -> Optional[ list[ScheduledAppointmentEntity]]: patient = await self.patients_repository.get_by_id(patient_id) if not patient: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Пациент с таким ID не найден', ) scheduled_appointments = await self.scheduled_appointment_repository.get_by_patient_id(patient_id, start_date=start_date, end_date=end_date) return [self.model_to_entity(scheduled_appointment) for scheduled_appointment in scheduled_appointments] async def create_scheduled_appointment(self, scheduled_appointment: ScheduledAppointmentEntity, doctor_id: int) -> \ Optional[ScheduledAppointmentEntity]: patient = await self.patients_repository.get_by_id(scheduled_appointment.patient_id) if not patient: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Пациент с таким ID не найден', ) doctor = await self.users_repository.get_by_id(doctor_id) if not doctor: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Доктор/пользователь с таким ID не найден', ) scheduled_appointment.doctor_id = doctor_id appointment_type = await self.appointment_types_repository.get_by_id(scheduled_appointment.type_id) if not appointment_type: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Тип приема с таким ID не найден', ) scheduled_appointment_model = self.entity_to_model(scheduled_appointment) await self.scheduled_appointment_repository.create(scheduled_appointment_model) return self.model_to_entity(scheduled_appointment_model) async def cancel_scheduled_appointment(self, scheduled_appointment_id: int, doctor_id: int): scheduled_appointment_model = await self.scheduled_appointment_repository.get_by_id(scheduled_appointment_id) if not scheduled_appointment_model: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Запланированный прием не найден") if scheduled_appointment_model.is_canceled: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Scheduled appointment already cancelled") doctor = await self.users_repository.get_by_id(doctor_id) if not doctor: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Доктор/пользователь с таким ID не найден', ) if scheduled_appointment_model.doctor_id != doctor_id and doctor.role.title != 'Администратор': raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail='Доступ запрещен', ) scheduled_appointment_model.is_canceled = True await self.scheduled_appointment_repository.update(scheduled_appointment_model) return self.model_to_entity(scheduled_appointment_model) async def update_scheduled_appointment(self, scheduled_appointment_id: int, scheduled_appointment: ScheduledAppointmentEntity) -> Optional[ ScheduledAppointmentEntity]: scheduled_appointment_model = await self.scheduled_appointment_repository.get_by_id(scheduled_appointment_id) if not scheduled_appointment_model: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Запланированный прием не найден") patient = await self.patients_repository.get_by_id(scheduled_appointment.patient_id) if not patient: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Пациент с таким ID не найден', ) doctor = await self.users_repository.get_by_id(scheduled_appointment.doctor_id) if not doctor: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Доктор/пользователь с таким ID не найден', ) appointment_type = await self.appointment_types_repository.get_by_id(scheduled_appointment.type_id) if not appointment_type: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail='Тип приема с таким ID не найден', ) scheduled_appointment_model.scheduled_datetime = scheduled_appointment.scheduled_datetime scheduled_appointment_model.patient_id = scheduled_appointment.patient_id scheduled_appointment_model.doctor_id = scheduled_appointment.doctor_id scheduled_appointment_model.type_id = scheduled_appointment.type_id scheduled_appointment_model.is_canceled = scheduled_appointment.is_canceled await self.scheduled_appointment_repository.update(scheduled_appointment_model) return self.model_to_entity(scheduled_appointment_model) @staticmethod def entity_to_model(scheduled_appointment: ScheduledAppointmentEntity) -> ScheduledAppointment: scheduled_appointment_model = ScheduledAppointment( scheduled_datetime=scheduled_appointment.scheduled_datetime, patient_id=scheduled_appointment.patient_id, doctor_id=scheduled_appointment.doctor_id, type_id=scheduled_appointment.type_id, is_canceled=scheduled_appointment.is_canceled, ) if scheduled_appointment.id: scheduled_appointment_model.id = scheduled_appointment.id return scheduled_appointment_model @staticmethod def model_to_entity(scheduled_appointment: ScheduledAppointment) -> ScheduledAppointmentEntity: scheduled_appointment_entity = ScheduledAppointmentEntity( id=scheduled_appointment.id, scheduled_datetime=scheduled_appointment.scheduled_datetime, patient_id=scheduled_appointment.patient_id, doctor_id=scheduled_appointment.doctor_id, type_id=scheduled_appointment.type_id, is_canceled=scheduled_appointment.is_canceled, ) if scheduled_appointment.patient is not None: scheduled_appointment_entity.patient = scheduled_appointment.patient if scheduled_appointment.doctor is not None: scheduled_appointment_entity.doctor = scheduled_appointment.doctor if scheduled_appointment.type is not None: scheduled_appointment_entity.type = scheduled_appointment.type return scheduled_appointment_entity