feat: Добавлена фильтрация по дате для приемов

Добавлена возможность фильтрации приемов и назначений по дате.
This commit is contained in:
Андрей Дувакин 2025-06-08 12:50:13 +05:00
parent 118ae84930
commit 3f3762c066
8 changed files with 125 additions and 106 deletions

View File

@ -1,8 +1,8 @@
from typing import Sequence, Optional
from sqlalchemy import select, desc
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload
from datetime import date
from app.domain.models import Appointment
@ -11,7 +11,7 @@ class AppointmentsRepository:
def __init__(self, db: AsyncSession):
self.db = db
async def get_all(self) -> Sequence[Appointment]:
async def get_all(self, start_date: date | None = None, end_date: date | None = None) -> Sequence[Appointment]:
stmt = (
select(Appointment)
.options(joinedload(Appointment.type))
@ -19,10 +19,15 @@ class AppointmentsRepository:
.options(joinedload(Appointment.doctor))
.order_by(desc(Appointment.appointment_datetime))
)
if start_date:
stmt = stmt.filter(Appointment.appointment_datetime >= start_date)
if end_date:
stmt = stmt.filter(Appointment.appointment_datetime <= end_date)
result = await self.db.execute(stmt)
return result.scalars().all()
async def get_by_doctor_id(self, doctor_id: int) -> Sequence[Appointment]:
async def get_by_doctor_id(self, doctor_id: int, start_date: date | None = None, end_date: date | None = None) -> \
Sequence[Appointment]:
stmt = (
select(Appointment)
.options(joinedload(Appointment.type))
@ -31,10 +36,15 @@ class AppointmentsRepository:
.filter_by(doctor_id=doctor_id)
.order_by(desc(Appointment.appointment_datetime))
)
if start_date:
stmt = stmt.filter(Appointment.appointment_datetime >= start_date)
if end_date:
stmt = stmt.filter(Appointment.appointment_datetime <= end_date)
result = await self.db.execute(stmt)
return result.scalars().all()
async def get_by_patient_id(self, patient_id: int) -> Sequence[Appointment]:
async def get_by_patient_id(self, patient_id: int, start_date: date | None = None, end_date: date | None = None) -> \
Sequence[Appointment]:
stmt = (
select(Appointment)
.options(joinedload(Appointment.type))
@ -43,6 +53,10 @@ class AppointmentsRepository:
.filter_by(patient_id=patient_id)
.order_by(desc(Appointment.appointment_datetime))
)
if start_date:
stmt = stmt.filter(Appointment.appointment_datetime >= start_date)
if end_date:
stmt = stmt.filter(Appointment.appointment_datetime <= end_date)
result = await self.db.execute(stmt)
return result.scalars().all()

View File

@ -1,8 +1,8 @@
from typing import Sequence
from sqlalchemy import select, desc
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload
from datetime import date
from app.domain.models import ScheduledAppointment
@ -11,7 +11,8 @@ class ScheduledAppointmentsRepository:
def __init__(self, db: AsyncSession):
self.db = db
async def get_all(self) -> Sequence[ScheduledAppointment]:
async def get_all(self, start_date: date | None = None, end_date: date | None = None) -> Sequence[
ScheduledAppointment]:
stmt = (
select(ScheduledAppointment)
.options(joinedload(ScheduledAppointment.type))
@ -20,10 +21,15 @@ class ScheduledAppointmentsRepository:
.filter_by(is_canceled=False)
.order_by(desc(ScheduledAppointment.scheduled_datetime))
)
if start_date:
stmt = stmt.filter(ScheduledAppointment.scheduled_datetime >= start_date)
if end_date:
stmt = stmt.filter(ScheduledAppointment.scheduled_datetime <= end_date)
result = await self.db.execute(stmt)
return result.scalars().all()
async def get_by_doctor_id(self, doctor_id: int) -> Sequence[ScheduledAppointment]:
async def get_by_doctor_id(self, doctor_id: int, start_date: date | None = None, end_date: date | None = None) -> \
Sequence[ScheduledAppointment]:
stmt = (
select(ScheduledAppointment)
.options(joinedload(ScheduledAppointment.type))
@ -32,10 +38,15 @@ class ScheduledAppointmentsRepository:
.filter_by(doctor_id=doctor_id, is_canceled=False)
.order_by(desc(ScheduledAppointment.scheduled_datetime))
)
if start_date:
stmt = stmt.filter(ScheduledAppointment.scheduled_datetime >= start_date)
if end_date:
stmt = stmt.filter(ScheduledAppointment.scheduled_datetime <= end_date)
result = await self.db.execute(stmt)
return result.scalars().all()
async def get_by_patient_id(self, patient_id: int) -> Sequence[ScheduledAppointment]:
async def get_by_patient_id(self, patient_id: int, start_date: date | None = None, end_date: date | None = None) -> \
Sequence[ScheduledAppointment]:
stmt = (
select(ScheduledAppointment)
.options(joinedload(ScheduledAppointment.type))
@ -44,6 +55,10 @@ class ScheduledAppointmentsRepository:
.filter_by(patient_id=patient_id, is_canceled=False)
.order_by(desc(ScheduledAppointment.scheduled_datetime))
)
if start_date:
stmt = stmt.filter(ScheduledAppointment.scheduled_datetime >= start_date)
if end_date:
stmt = stmt.filter(ScheduledAppointment.scheduled_datetime <= end_date)
result = await self.db.execute(stmt)
return result.scalars().all()

View File

@ -1,5 +1,6 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from datetime import date
from app.database.session import get_db
from app.domain.entities.appointment import AppointmentEntity
@ -18,9 +19,11 @@ router = APIRouter()
async def get_all_appointments(
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
start_date: date | None = Query(None, description="Start date for filtering (YYYY-MM-DD)"),
end_date: date | None = Query(None, description="End date for filtering (YYYY-MM-DD)"),
):
appointments_service = AppointmentsService(db)
return await appointments_service.get_all_appointments()
return await appointments_service.get_all_appointments(start_date=start_date, end_date=end_date)
@router.get(
@ -33,9 +36,11 @@ async def get_all_appointments_by_doctor_id(
doctor_id: int,
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
start_date: date | None = Query(None, description="Start date for filtering (YYYY-MM-DD)"),
end_date: date | None = Query(None, description="End date for filtering (YYYY-MM-DD)"),
):
appointments_service = AppointmentsService(db)
return await appointments_service.get_appointments_by_doctor_id(doctor_id)
return await appointments_service.get_appointments_by_doctor_id(doctor_id, start_date=start_date, end_date=end_date)
@router.get(
@ -48,9 +53,12 @@ async def get_all_appointments_by_patient_id(
patient_id: int,
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
start_date: date | None = Query(None, description="Start date for filtering (YYYY-MM-DD)"),
end_date: date | None = Query(None, description="End date for filtering (YYYY-MM-DD)"),
):
appointments_service = AppointmentsService(db)
return await appointments_service.get_appointments_by_patient_id(patient_id)
return await appointments_service.get_appointments_by_patient_id(patient_id, start_date=start_date,
end_date=end_date)
@router.post(

View File

@ -1,7 +1,7 @@
from typing import Optional
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from datetime import date
from app.database.session import get_db
from app.domain.entities.scheduled_appointment import ScheduledAppointmentEntity
@ -20,9 +20,11 @@ router = APIRouter()
async def get_all_scheduled_appointments(
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
start_date: date | None = Query(None, description="Start date for filtering (YYYY-MM-DD)"),
end_date: date | None = Query(None, description="End date for filtering (YYYY-MM-DD)"),
):
scheduled_appointments_service = ScheduledAppointmentsService(db)
return await scheduled_appointments_service.get_all_scheduled_appointments()
return await scheduled_appointments_service.get_all_scheduled_appointments(start_date=start_date, end_date=end_date)
@router.get(
@ -35,9 +37,12 @@ async def get_all_scheduled_appointments_by_doctor_id(
doctor_id: int,
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
start_date: date | None = Query(None, description="Start date for filtering (YYYY-MM-DD)"),
end_date: date | None = Query(None, description="End date for filtering (YYYY-MM-DD)"),
):
appointments_service = ScheduledAppointmentsService(db)
return await appointments_service.get_scheduled_appointments_by_doctor_id(doctor_id)
return await appointments_service.get_scheduled_appointments_by_doctor_id(doctor_id, start_date=start_date,
end_date=end_date)
@router.get(
@ -50,9 +55,12 @@ async def get_all_appointments_by_patient_id(
patient_id: int,
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
start_date: date | None = Query(None, description="Start date for filtering (YYYY-MM-DD)"),
end_date: date | None = Query(None, description="End date for filtering (YYYY-MM-DD)"),
):
appointments_service = ScheduledAppointmentsService(db)
return await appointments_service.get_scheduled_appointments_by_patient_id(patient_id)
return await appointments_service.get_scheduled_appointments_by_patient_id(patient_id, start_date=start_date,
end_date=end_date)
@router.post(

View File

@ -1,8 +1,8 @@
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.appointments_repository import AppointmentsRepository
@ -22,45 +22,34 @@ class AppointmentsService:
self.users_repository = UsersRepository(db)
self.patients_repository = PatientsRepository(db)
async def get_all_appointments(self) -> list[AppointmentEntity]:
appointments = await self.appointments_repository.get_all()
async def get_all_appointments(self, start_date: date | None = None, end_date: date | None = None) -> list[
AppointmentEntity]:
appointments = await self.appointments_repository.get_all(start_date=start_date, end_date=end_date)
return [self.model_to_entity(appointment) for appointment in appointments]
return [
self.model_to_entity(appointment)
for appointment in appointments
]
async def get_appointments_by_doctor_id(self, doctor_id: int) -> Optional[list[AppointmentEntity]]:
async def get_appointments_by_doctor_id(self, doctor_id: int, start_date: date | None = None,
end_date: date | None = None) -> Optional[list[AppointmentEntity]]:
doctor = await self.users_repository.get_by_id(doctor_id)
if not doctor:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail='Доктор с таким ID не найден',
)
appointments = await self.appointments_repository.get_by_doctor_id(doctor_id, start_date=start_date,
end_date=end_date)
return [self.model_to_entity(appointment) for appointment in appointments]
appointments = await self.appointments_repository.get_by_doctor_id(doctor_id)
return [
self.model_to_entity(appointment)
for appointment in appointments
]
async def get_appointments_by_patient_id(self, patient_id: int) -> Optional[list[AppointmentEntity]]:
async def get_appointments_by_patient_id(self, patient_id: int, start_date: date | None = None,
end_date: date | None = None) -> Optional[list[AppointmentEntity]]:
patient = await self.patients_repository.get_by_id(patient_id)
if not patient:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail='Пациент с таким ID не найден',
)
appointments = await self.appointments_repository.get_by_patient_id(patient_id)
return [
self.model_to_entity(appointment)
for appointment in appointments
]
appointments = await self.appointments_repository.get_by_patient_id(patient_id, start_date=start_date,
end_date=end_date)
return [self.model_to_entity(appointment) for appointment in appointments]
async def create_appointment(self, appointment: AppointmentEntity, doctor_id: int) -> Optional[AppointmentEntity]:
patient = await self.patients_repository.get_by_id(appointment.patient_id)

View File

@ -1,8 +1,8 @@
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
@ -19,54 +19,42 @@ class ScheduledAppointmentsService:
self.users_repository = UsersRepository(db)
self.patients_repository = PatientsRepository(db)
async def get_all_scheduled_appointments(self) -> list[ScheduledAppointmentEntity]:
scheduled_appointments = await self.scheduled_appointment_repository.get_all()
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) -> Optional[
list[ScheduledAppointmentEntity]
]:
doctor = self.users_repository.get_by_id(doctor_id)
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]
scheduled_appointments = await self.scheduled_appointment_repository.get_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) -> Optional[
list[ScheduledAppointmentEntity]
]:
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)
return [
self.model_to_entity(scheduled_appointment)
for scheduled_appointment in scheduled_appointments
]
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
]:
Optional[ScheduledAppointmentEntity]:
patient = await self.patients_repository.get_by_id(scheduled_appointment.patient_id)
if not patient:
@ -99,7 +87,7 @@ class ScheduledAppointmentsService:
return self.model_to_entity(scheduled_appointment_model)
async def cancel_scheduled_appointment(self, scheduled_appointment_id: int, doctor_id):
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:
@ -129,11 +117,9 @@ class ScheduledAppointmentsService:
return self.model_to_entity(scheduled_appointment_model)
async def update_scheduled_appointment(
self,
scheduled_appointment_id: int,
scheduled_appointment: ScheduledAppointmentEntity
) -> Optional[ScheduledAppointmentEntity]:
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:

View File

@ -27,14 +27,10 @@ import AppointmentsListModal from "./Components/AppointmentsListModal/Appointmen
const AppointmentsPage = () => {
const {
patients,
appointments,
scheduledAppointments,
isLoading,
isError,
collapsed,
siderWidth,
hovered,
showSplitterPanel,
siderButtonText,
splitterStyle,
@ -50,6 +46,8 @@ const AppointmentsPage = () => {
handleLeaveSider,
handleSetSiderWidth,
openCreateScheduledAppointmentModal,
currentMonth,
handleMonthChange,
} = useAppointments();
const dispatch = useDispatch();
@ -111,7 +109,10 @@ const AppointmentsPage = () => {
min="25%"
max="90%"
>
<AppointmentsCalendarTab />
<AppointmentsCalendarTab
currentMonth={currentMonth}
onMonthChange={handleMonthChange}
/>
</Splitter.Panel>
{showSplitterPanel && (

View File

@ -7,37 +7,37 @@ import {
} from "../../../Api/appointmentsApi.js";
import { useGetAllPatientsQuery } from "../../../Api/patientsApi.js";
import {
openModal,
openScheduledModal,
setHovered,
setSelectedAppointment,
setSelectedScheduledAppointment,
toggleSider
} from "../../../Redux/Slices/appointmentsSlice.js";
import { useGetScheduledAppointmentsQuery } from "../../../Api/scheduledAppointmentsApi.js";
import dayjs from "dayjs";
import {useGetScheduledAppointmentsQuery} from "../../../Api/scheduledAppointmentsApi.js";
const { useBreakpoint } = Grid;
const useAppointments = () => {
const dispatch = useDispatch();
const {
userData
} = useSelector(state => state.auth);
const {
collapsed,
siderWidth,
hovered,
selectedAppointment,
} = useSelector(state => state.appointmentsUI);
const { userData } = useSelector(state => state.auth);
const { collapsed, siderWidth, hovered, selectedAppointment } = useSelector(state => state.appointmentsUI);
const screens = useBreakpoint();
// Data fetching
const [currentMonth, setCurrentMonth] = useState(dayjs().startOf('month'));
const startDate = currentMonth.startOf('month').format('YYYY-MM-DD');
const endDate = currentMonth.endOf('month').format('YYYY-MM-DD');
const handleMonthChange = (newMonth) => {
setCurrentMonth(dayjs(newMonth).startOf('month'));
};
const {
data: appointments = [],
isLoading: isLoadingAppointments,
isError: isErrorAppointments,
} = useGetAppointmentsQuery(userData.id, {
} = useGetAppointmentsQuery({ doctor_id: userData.id, start_date: startDate, end_date: endDate }, {
pollingInterval: 20000,
});
@ -45,7 +45,7 @@ const useAppointments = () => {
data: scheduledAppointments = [],
isLoading: isLoadingScheduledAppointments,
isError: isErrorScheduledAppointments,
} = useGetScheduledAppointmentsQuery(userData.id, {
} = useGetScheduledAppointmentsQuery({ doctor_id: userData.id, start_date: startDate, end_date: endDate }, {
pollingInterval: 20000,
});
@ -57,7 +57,6 @@ const useAppointments = () => {
pollingInterval: 20000,
});
// UI state and styles
const [localSiderWidth, setLocalSiderWidth] = useState(siderWidth);
const splitterStyle = { flex: 1 };
@ -89,7 +88,6 @@ const useAppointments = () => {
width: "100%",
};
// Handlers
const handleToggleSider = () => dispatch(toggleSider());
const handleHoverSider = () => dispatch(setHovered(true));
const handleLeaveSider = () => dispatch(setHovered(false));
@ -107,7 +105,6 @@ const useAppointments = () => {
dispatch(openScheduledModal());
};
// Computed properties
const siderButtonText = useMemo(() =>
hovered ? (collapsed ? "Показать предстоящие события" : "Скрыть предстоящие события") : "",
[collapsed, hovered]
@ -122,7 +119,6 @@ const useAppointments = () => {
[appointments, scheduledAppointments]
);
// Effects
useEffect(() => {
document.title = "Приемы";
}, []);
@ -177,6 +173,8 @@ const useAppointments = () => {
handleLeaveSider,
handleSetSiderWidth,
openCreateScheduledAppointmentModal,
currentMonth,
handleMonthChange,
};
};