diff --git a/api/app/application/lenses_repository.py b/api/app/application/lenses_repository.py index 6d5878d..f087fdd 100644 --- a/api/app/application/lenses_repository.py +++ b/api/app/application/lenses_repository.py @@ -1,6 +1,6 @@ from typing import Sequence, Optional -from sqlalchemy import select, Row, RowMapping +from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.domain.models import Lens @@ -31,14 +31,7 @@ class LensesRepository: await self.db.commit() return lens - async def delete(self, lens_id: int) -> Row[Lens] | RowMapping | None: - stmt = select(Lens).filter(Lens.id == lens_id) - result = await self.db.execute(stmt) - lens = result.scalars().first() - - if lens: - await self.db.delete(lens) - await self.db.commit() - return lens - - return None + async def delete(self, lens: Lens) -> Lens: + await self.db.delete(lens) + await self.db.commit() + return lens diff --git a/api/app/application/patients_repository.py b/api/app/application/patients_repository.py index 8b70cc9..3d823a5 100644 --- a/api/app/application/patients_repository.py +++ b/api/app/application/patients_repository.py @@ -1,6 +1,6 @@ -from typing import Sequence +from typing import Sequence, Optional -from sqlalchemy import select, Row, RowMapping +from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.domain.models import Patient @@ -15,7 +15,7 @@ class PatientsRepository: result = await self.db.execute(stmt) return result.scalars().all() - async def get_by_id(self, patient_id: int) -> Patient: + async def get_by_id(self, patient_id: int) -> Optional[Patient]: stmt = select(Patient).filter(Patient.id == patient_id) result = await self.db.execute(stmt) return result.scalars().first() @@ -31,14 +31,7 @@ class PatientsRepository: await self.db.commit() return patient - async def delete(self, patient_id: int) -> Row[Patient] | RowMapping | None: - stmt = select(Patient).filter(Patient.id == patient_id) - result = await self.db.execute(stmt) - patient = result.scalars().first() - - if patient: - await self.db.delete(patient) - await self.db.commit() - return patient - - return None + async def delete(self, patient: Patient) -> Patient: + await self.db.delete(patient) + await self.db.commit() + return patient diff --git a/api/app/application/sets_repository.py b/api/app/application/sets_repository.py new file mode 100644 index 0000000..db3451f --- /dev/null +++ b/api/app/application/sets_repository.py @@ -0,0 +1,37 @@ +from typing import Optional, Sequence + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from app.domain.models import Set + + +class SetsRepository: + def __init__(self, db: AsyncSession): + self.db = db + + async def get_all(self) -> Sequence[Set]: + stmt = select(Set) + result = await self.db.execute(stmt) + return result.scalars().all() + + async def get_by_id(self, set_id: int) -> Optional[Set]: + stmt = select(Set).filter(Set.id == set_id) + result = await self.db.execute(stmt) + return result.scalars().first() + + async def create(self, _set: Set) -> Set: + self.db.add(_set) + await self.db.commit() + await self.db.refresh(_set) + return _set + + async def update(self, _set: Set) -> Set: + await self.db.merge(_set) + await self.db.commit() + return _set + + async def delete(self, _set: Set) -> Set: + await self.db.delete(_set) + await self.db.commit() + return _set diff --git a/api/app/controllers/lenses_router.py b/api/app/controllers/lenses_router.py index 5a26b67..b83a081 100644 --- a/api/app/controllers/lenses_router.py +++ b/api/app/controllers/lenses_router.py @@ -56,7 +56,7 @@ async def update_lens( @router.delete( "/lenses/{lens_id}/", - response_model=bool, + response_model=LensEntity, summary="Delete lens", description="Deletes an existing lens", ) diff --git a/api/app/controllers/patients_router.py b/api/app/controllers/patients_router.py index 161a597..d482b0e 100644 --- a/api/app/controllers/patients_router.py +++ b/api/app/controllers/patients_router.py @@ -56,7 +56,7 @@ async def update_patient( @router.delete( "/patients/{patient_id}/", - response_model=bool, + response_model=PatientEntity, summary="Delete a patient", description="Deletes a patient", ) diff --git a/api/app/controllers/sets_router.py b/api/app/controllers/sets_router.py new file mode 100644 index 0000000..299fb40 --- /dev/null +++ b/api/app/controllers/sets_router.py @@ -0,0 +1,69 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.ext.asyncio import AsyncSession + +from app.database.session import get_db +from app.domain.entities.set import SetEntity +from app.infrastructure.dependencies import get_current_user +from app.infrastructure.sets_service import SetsService + +router = APIRouter() + + +@router.get( + '/sets/', + response_model=list[SetEntity], + summary='Get all sets', + description='Returns a list of all sets', +) +async def get_all_sets( + db: AsyncSession = Depends(get_db), + user=Depends(get_current_user), +): + sets_service = SetsService(db) + return await sets_service.get_all_sets() + + +@router.post( + '/sets/', + response_model=SetEntity, + summary='Create a new set', + description='Create a new set', +) +async def create_set( + _set: SetEntity, + db: AsyncSession = Depends(get_db), + user=Depends(get_current_user), +): + sets_service = SetsService(db) + return await sets_service.create_set(_set) + + +@router.put( + '/sets/{set_id}/', + response_model=SetEntity, + summary='Update a set', + description='Update a set,' +) +async def update_set( + set_id: int, + _set: SetEntity, + db: AsyncSession = Depends(get_db), + user=Depends(get_current_user), +): + sets_service = SetsService(db) + return sets_service.update_set(set_id, _set) + + +@router.delete( + '/sets/{set_id}/', + response_model=SetEntity, + summary='Delete set', + description='Delete an existing set', +) +async def delete_set( + set_id: int, + db: AsyncSession = Depends(get_db), + user=Depends(get_current_user), +): + sets_service = SetsService(db) + return await sets_service.delete_set(set_id) diff --git a/api/app/database/migrations/versions/0249759985c3_убрал_поле_количество_у_таблицы_наборов.py b/api/app/database/migrations/versions/0249759985c3_убрал_поле_количество_у_таблицы_наборов.py new file mode 100644 index 0000000..9e36f4f --- /dev/null +++ b/api/app/database/migrations/versions/0249759985c3_убрал_поле_количество_у_таблицы_наборов.py @@ -0,0 +1,30 @@ +"""Убрал поле Количество у таблицы наборов + +Revision ID: 0249759985c3 +Revises: 27fa11120115 +Create Date: 2025-02-20 10:59:11.341413 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '0249759985c3' +down_revision: Union[str, None] = '27fa11120115' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('sets', 'count') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('sets', sa.Column('count', sa.INTEGER(), autoincrement=False, nullable=False)) + # ### end Alembic commands ### diff --git a/api/app/domain/entities/lens.py b/api/app/domain/entities/lens.py index 778dadd..2b29218 100644 --- a/api/app/domain/entities/lens.py +++ b/api/app/domain/entities/lens.py @@ -13,6 +13,6 @@ class LensEntity(BaseModel): diameter: float periphery_toricity: float side: str - issued: bool + issued: bool = False type_id: int diff --git a/api/app/domain/entities/set.py b/api/app/domain/entities/set.py new file mode 100644 index 0000000..1988f55 --- /dev/null +++ b/api/app/domain/entities/set.py @@ -0,0 +1,8 @@ +from typing import Optional + +from pydantic import BaseModel + + +class SetEntity(BaseModel): + id: Optional[int] = None + title: str diff --git a/api/app/domain/entities/set_content.py b/api/app/domain/entities/set_content.py new file mode 100644 index 0000000..5281346 --- /dev/null +++ b/api/app/domain/entities/set_content.py @@ -0,0 +1,18 @@ +from typing import Optional + +from pydantic import BaseModel + + +class SetContentEntity(BaseModel): + id: Optional[int] = None + tor: float + trial: float + esa: float + fvc: float + preset_refraction: float + diameter: float + periphery_toricity: float + side: str + count: int + + set_id: int diff --git a/api/app/domain/models/sets.py b/api/app/domain/models/sets.py index d7a066b..ac7bf14 100644 --- a/api/app/domain/models/sets.py +++ b/api/app/domain/models/sets.py @@ -8,7 +8,6 @@ class Set(BaseModel): __tablename__ = 'sets' title = Column(VARCHAR(150), nullable=False, unique=True) - count = Column(Integer, nullable=False) contents = relationship('SetContent', back_populates='set') lens = relationship('SetLens', back_populates='set') diff --git a/api/app/infrastructure/lenses_service.py b/api/app/infrastructure/lenses_service.py index 5fa39ef..109b99f 100644 --- a/api/app/infrastructure/lenses_service.py +++ b/api/app/infrastructure/lenses_service.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, Any, Coroutine from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession @@ -8,6 +8,7 @@ from app.application.lens_types_repository import LensTypesRepository from app.application.lenses_repository import LensesRepository from app.domain.entities.lens import LensEntity from app.domain.models import Lens +from app.domain.models.lens import SideEnum class LensesService: @@ -35,7 +36,7 @@ class LensesService: ] async def create_lens(self, lens: LensEntity) -> LensEntity: - lens_type = self.lens_types_repository.get_by_id(lens.type_id) + lens_type = await self.lens_types_repository.get_by_id(lens.type_id) if not lens_type: raise HTTPException( @@ -43,6 +44,14 @@ class LensesService: detail='The lens type with this ID was not found' ) + try: + side_enum = SideEnum(lens.side) + except ValueError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Invalid side value: {lens.side}. Must be 'левая' or 'правая'." + ) + lens_model = Lens( tor=lens.tor, trial=lens.trial, @@ -51,20 +60,22 @@ class LensesService: preset_refraction=lens.preset_refraction, diameter=lens.diameter, periphery_toricity=lens.periphery_toricity, - side=lens.side, + side=side_enum, type_id=lens.type_id, ) + await self.lenses_repository.create(lens_model) + return LensEntity( id=lens_model.id, tor=lens_model.tor, trial=lens_model.trial, esa=lens_model.esa, - fvc=lens_model.fvs, + fvc=lens_model.fvc, preset_refraction=lens_model.preset_refraction, diameter=lens_model.diameter, periphery_toricity=lens_model.periphery_toricity, - side=lens_model.side, + side=lens_model.side.value, issued=lens_model.issued, type_id=lens_model.type_id, ) @@ -72,38 +83,62 @@ class LensesService: async def update_lens(self, lens_id: int, lens: LensEntity) -> LensEntity: lens_model = await self.lenses_repository.get_by_id(lens_id) - if lens_model: - lens_model.tor = lens.tor - lens_model.trial = lens.trial - lens_model.esa = lens.esa - lens_model.fvc = lens.fvc - lens_model.preset_refraction = lens.preset_refraction - lens_model.diameter = lens.diameter - lens_model.periphery_toricity = lens.periphery_toricity - lens_model.side = lens.side - lens_model.issued = lens.issued - lens_model.type_id = lens.type_id - await self.lenses_repository.update(lens_model) - return LensEntity( - id=lens_model.id, - tor=lens_model.tor, - trial=lens_model.trial, - esa=lens_model.esa, - fvc=lens_model.fvs, - preset_refraction=lens_model.preset_refraction, - diameter=lens_model.diameter, - periphery_toricity=lens_model.periphery_toricity, - side=lens_model.side, - issued=lens_model.issued, - type_id=lens_model.type_id, - ) - - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Lens not found") - - async def delete_lens(self, lens_id: int) -> Optional[bool]: - result = await self.lenses_repository.delete(lens_id) is not None - - if not result: + if not lens_model: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Lens not found") - return result + try: + side_enum = SideEnum(lens.side) + except ValueError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Invalid side value: {lens.side}. Must be 'левая' or 'правая'." + ) + + lens_model.tor = lens.tor + lens_model.trial = lens.trial + lens_model.esa = lens.esa + lens_model.fvc = lens.fvc + lens_model.preset_refraction = lens.preset_refraction + lens_model.diameter = lens.diameter + lens_model.periphery_toricity = lens.periphery_toricity + lens_model.side = side_enum + lens_model.issued = lens.issued + lens_model.type_id = lens.type_id + + await self.lenses_repository.update(lens_model) + + return LensEntity( + id=lens_model.id, + tor=lens_model.tor, + trial=lens_model.trial, + esa=lens_model.esa, + fvc=lens_model.fvc, + preset_refraction=lens_model.preset_refraction, + diameter=lens_model.diameter, + periphery_toricity=lens_model.periphery_toricity, + side=lens_model.side.value, + issued=lens_model.issued, + type_id=lens_model.type_id, + ) + + async def delete_lens(self, lens_id: int) -> Optional[LensEntity]: + lens = await self.lenses_repository.get_by_id(lens_id) + + if not lens: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Lens not found") + + result = await self.lenses_repository.delete(lens) + + return LensEntity( + id=result.id, + tor=result.tor, + trial=result.trial, + esa=result.esa, + fvc=result.fvc, + preset_refraction=result.preset_refraction, + diameter=result.diameter, + periphery_toricity=result.periphery_toricity, + side=result.side.value, + issued=result.issued, + type_id=result.type_id, + ) diff --git a/api/app/infrastructure/patients_service.py b/api/app/infrastructure/patients_service.py index 846711c..b4f070f 100644 --- a/api/app/infrastructure/patients_service.py +++ b/api/app/infrastructure/patients_service.py @@ -59,36 +59,51 @@ class PatientsService: async def update_patient(self, patient_id: int, patient: PatientEntity) -> Optional[PatientEntity]: patient_model = await self.patient_repository.get_by_id(patient_id) - if patient_model: - patient_model.first_name = patient.first_name - patient_model.last_name = patient.last_name - patient_model.patronymic = patient.patronymic - patient_model.birthday = patient.birthday - patient_model.address = patient.address - patient_model.email = patient.email - patient_model.phone = patient.phone - patient_model.diagnosis = patient.diagnosis - patient_model.correction = patient.correction - await self.patient_repository.update(patient_model) - return PatientEntity( - id=patient_model.id, - first_name=patient_model.first_name, - last_name=patient_model.last_name, - patronymic=patient_model.patronymic, - birthday=patient_model.birthday, - address=patient_model.address, - email=patient_model.email, - phone=patient_model.phone, - diagnosis=patient_model.diagnosis, - correction=patient_model.correction, - ) - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Patient not found") - - async def delete_patient(self, patient_id: int) -> Optional[bool]: - result = await self.patient_repository.delete(patient_id) is not None - - if not result: + if not patient_model: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Patient not found") - return result + patient_model.first_name = patient.first_name + patient_model.last_name = patient.last_name + patient_model.patronymic = patient.patronymic + patient_model.birthday = patient.birthday + patient_model.address = patient.address + patient_model.email = patient.email + patient_model.phone = patient.phone + patient_model.diagnosis = patient.diagnosis + patient_model.correction = patient.correction + await self.patient_repository.update(patient_model) + return PatientEntity( + id=patient_model.id, + first_name=patient_model.first_name, + last_name=patient_model.last_name, + patronymic=patient_model.patronymic, + birthday=patient_model.birthday, + address=patient_model.address, + email=patient_model.email, + phone=patient_model.phone, + diagnosis=patient_model.diagnosis, + correction=patient_model.correction, + ) + + + async def delete_patient(self, patient_id: int) -> Optional[PatientEntity]: + patient = await self.patient_repository.get_by_id(patient_id) + + if not patient: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Patient not found") + + result = await self.patient_repository.delete(patient) + + return PatientEntity( + id=result.id, + first_name=result.first_name, + last_name=result.last_name, + patronymic=result.patronymic, + birthday=result.birthday, + address=result.address, + email=result.email, + phone=result.phone, + diagnosis=result.diagnosis, + correction=result.correction, + ) diff --git a/api/app/infrastructure/sets_service.py b/api/app/infrastructure/sets_service.py new file mode 100644 index 0000000..d8a8fc6 --- /dev/null +++ b/api/app/infrastructure/sets_service.py @@ -0,0 +1,62 @@ +from typing import Optional + +from fastapi import HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from starlette import status + +from app.application.sets_repository import SetsRepository +from app.domain.entities.set import SetEntity +from app.domain.models import Set + + +class SetsService: + def __init__(self, db: AsyncSession): + self.sets_repository = SetsRepository(db) + + async def get_all_sets(self) -> list[SetEntity]: + sets = await self.sets_repository.get_all() + return [ + SetEntity( + id=_set.id, + title=_set.title, + ) + for _set in sets + ] + + async def create_set(self, _set: SetEntity) -> SetEntity: + set_model = Set( + title=_set.title, + ) + await self.sets_repository.create(set_model) + return SetEntity( + id=set_model.id, + title=set_model.id, + ) + + async def update_set(self, set_id: int, _set: SetEntity) -> SetEntity: + set_model = await self.sets_repository.get_by_id(set_id) + + if not set_model: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Set not found") + + set_model.title = _set.title + + await self.sets_repository.update(set_model) + + return SetEntity( + id=set_model.id, + title=set_model.title, + ) + + async def delete_set(self, set_id: int) -> Optional[SetEntity]: + _set = await self.sets_repository.get_by_id(set_id) + + if not _set: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Set not found") + + result = await self.sets_repository.delete(_set) + + return SetEntity( + id=result.id, + title=result.title, + ) diff --git a/api/app/main.py b/api/app/main.py index 1503558..400c731 100644 --- a/api/app/main.py +++ b/api/app/main.py @@ -2,10 +2,11 @@ from fastapi import FastAPI from starlette.middleware.cors import CORSMiddleware from app.controllers.auth_router import router as auth_router -from app.controllers.register_routes import router as register_router -from app.controllers.patients_router import router as patients_router -from app.controllers.lenses_router import router as lenses_router from app.controllers.lens_types_router import router as lens_types_router +from app.controllers.lenses_router import router as lenses_router +from app.controllers.patients_router import router as patients_router +from app.controllers.register_routes import router as register_router +from app.controllers.sets_router import router as sets_router from app.settings import settings @@ -25,6 +26,7 @@ def start_app(): api_app.include_router(patients_router, prefix=settings.APP_PREFIX, tags=['patients']) api_app.include_router(lenses_router, prefix=settings.APP_PREFIX, tags=['lenses']) api_app.include_router(lens_types_router, prefix=settings.APP_PREFIX, tags=['lens_types']) + api_app.include_router(sets_router, prefix=settings.APP_PREFIX, tags=['sets']) return api_app