создал слои для таблицы линз

This commit is contained in:
Андрей Дувакин 2025-02-18 11:59:46 +05:00
parent 2f39baa8d3
commit ddd551adb4
20 changed files with 464 additions and 32 deletions

View File

@ -0,0 +1,42 @@
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.domain.models import LensType
class LensTypesRepository:
def __init__(self, db: AsyncSession):
self.db = db
async def get_all(self):
stmt = select(LensType)
result = await self.db.execute(stmt)
return result.scalars().all()
async def get_by_id(self, lens_type_id: int):
stmt = select(LensType).filter(LensType.id == lens_type_id)
result = await self.db.execute(stmt)
return result.scalars().first()
async def create(self, lens_type: LensType):
self.db.add(lens_type)
await self.db.commit()
await self.db.refresh(lens_type)
return lens_type
async def update(self, lens_type: LensType):
await self.db.merge(lens_type)
await self.db.commit()
return lens_type
async def delete(self, lens_type_id: int):
stmt = select(LensType).filter(LensType.id == lens_type_id)
result = await self.db.execute(stmt)
lens_type = result.scalars().first()
if lens_type:
await self.db.delete(lens_type)
await self.db.commit()
return lens_type
return None

View File

@ -13,6 +13,11 @@ class LensesRepository:
result = await self.db.execute(stmt) result = await self.db.execute(stmt)
return result.scalars().all() return result.scalars().all()
async def get_by_id(self, lens_id: int):
stmt = select(Lens).filter(Lens.id == lens_id)
result = await self.db.execute(stmt)
return result.scalars().first()
async def create(self, lens: Lens): async def create(self, lens: Lens):
self.db.add(lens) self.db.add(lens)
await self.db.commit() await self.db.commit()

View File

@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException, Response from fastapi import APIRouter, Depends, Response
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.database.session import get_db from app.database.session import get_db
@ -22,15 +22,8 @@ async def auth_user(
db: AsyncSession = Depends(get_db) db: AsyncSession = Depends(get_db)
): ):
auth_service = AuthService(db) auth_service = AuthService(db)
token = await auth_service.authenticate_user(user_data.login, user_data.password) token = await auth_service.authenticate_user(user_data.login, user_data.password)
if token is None:
raise HTTPException(
status_code=401,
detail="Incorrect username or password"
)
response.set_cookie( response.set_cookie(
key="users_access_token", key="users_access_token",
value=token["access_token"], value=token["access_token"],

View File

@ -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.lens import LensEntity
from app.infrastructure.dependencies import get_current_user
from app.infrastructure.lenses_service import LensesService
router = APIRouter()
@router.get(
"/lenses/",
response_model=list[LensEntity],
summary="Get all lenses",
description="Returns a list of all lenses",
)
async def get_all_lenses(
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
):
lenses_service = LensesService(db)
return await lenses_service.get_all_lenses()
@router.post(
"/lenses/",
response_model=LensEntity,
summary="Create lens",
description="Creates a new lens",
)
async def create_lens(
lens: LensEntity,
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
):
lenses_service = LensesService(db)
return await lenses_service.create_lens(lens)
@router.put(
"/lenses/{lens_id}/",
response_model=LensEntity,
summary="Update lens",
description="Updates an existing lens",
)
async def update_lens(
lens_id: int,
lens: LensEntity,
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
):
lenses_service = LensesService(db)
return await lenses_service.update_lens(lens_id, lens)
@router.delete(
"/lenses/{lens_id}/",
response_model=bool,
summary="Delete lens",
description="Deletes an existing lens",
)
async def delete_lens(
lens_id: int,
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
):
lenses_service = LensesService(db)
return await lenses_service.delete_lens(lens_id)

View File

@ -9,40 +9,61 @@ from app.infrastructure.patients_service import PatientsService
router = APIRouter() router = APIRouter()
@router.get("/patients/") @router.get(
"/patients/",
response_model=list[PatientEntity],
summary="Get all patients",
description="Returns a list of all patients",
)
async def get_all_patients( async def get_all_patients(
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
user=Depends(get_current_user) user=Depends(get_current_user),
): ):
patients_service = PatientsService(db) patients_service = PatientsService(db)
return await patients_service.get_all_patients() return await patients_service.get_all_patients()
@router.post("/patients/") @router.post(
"/patients/",
response_model=PatientEntity,
summary="Create a new patient",
description="Creates a new patient",
)
async def create_patient( async def create_patient(
patient: PatientEntity, patient: PatientEntity,
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
user=Depends(get_current_user) user=Depends(get_current_user),
): ):
patients_service = PatientsService(db) patients_service = PatientsService(db)
return await patients_service.create_patient(patient) return await patients_service.create_patient(patient)
@router.put("/patients/{patient_id}/") @router.put(
"/patients/{patient_id}/",
response_model=PatientEntity,
summary="Update a patient",
description="Updates a patient",
)
async def update_patient( async def update_patient(
patient_id: int, patient_id: int,
patient: PatientEntity, patient: PatientEntity,
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
user=Depends(get_current_user) user=Depends(get_current_user),
): ):
patients_service = PatientsService(db) patients_service = PatientsService(db)
return await patients_service.update_patient(patient_id, patient) return await patients_service.update_patient(patient_id, patient)
@router.delete("/patients/{patient_id}/", response_model=bool)
@router.delete(
"/patients/{patient_id}/",
response_model=bool,
summary="Delete a patient",
description="Deletes a patient",
)
async def delete_patient( async def delete_patient(
patient_id: int, patient_id: int,
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
user=Depends(get_current_user) user=Depends(get_current_user),
): ):
patient_service = PatientsService(db) patient_service = PatientsService(db)
return await patient_service.delete_patient(patient_id) return await patient_service.delete_patient(patient_id)

View File

@ -0,0 +1,32 @@
"""Переименовал таблицу с типами линз - Шаг 3
Revision ID: 70eb3c307702
Revises: ee12ffac87d7
Create Date: 2025-02-18 11:41:53.344762
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '70eb3c307702'
down_revision: Union[str, None] = 'ee12ffac87d7'
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.add_column('lens', sa.Column('type_id', sa.Integer(), nullable=False))
op.create_foreign_key(None, 'lens', 'lens_types', ['type_id'], ['id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'lens', type_='foreignkey')
op.drop_column('lens', 'type_id')
# ### end Alembic commands ###

View File

@ -0,0 +1,44 @@
"""Добавил таблицу с содержанием набора
Revision ID: 7d74a745da7a
Revises: c0997c6bf2f1
Create Date: 2025-02-18 11:28:51.401555
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '7d74a745da7a'
down_revision: Union[str, None] = 'c0997c6bf2f1'
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.create_table('set_contents',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('tor', sa.Integer(), nullable=False),
sa.Column('trial', sa.Integer(), nullable=False),
sa.Column('esa', sa.Integer(), nullable=False),
sa.Column('fvc', sa.Integer(), nullable=False),
sa.Column('preset_refraction', sa.Integer(), nullable=False),
sa.Column('diameter', sa.Integer(), nullable=False),
sa.Column('periphery_toricity', sa.Integer(), nullable=False),
sa.Column('side', sa.Integer(), nullable=False),
sa.Column('count', sa.Integer(), nullable=False),
sa.Column('set_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['set_id'], ['sets.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('set_contents')
# ### end Alembic commands ###

View File

@ -0,0 +1,32 @@
"""Переименовал таблицу с типами линз - Шаг 1
Revision ID: 7f50bd3f0523
Revises: 7d74a745da7a
Create Date: 2025-02-18 11:41:03.926914
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '7f50bd3f0523'
down_revision: Union[str, None] = '7d74a745da7a'
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_constraint('lens_type_id_fkey', 'lens', type_='foreignkey')
op.drop_column('lens', 'type_id')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('lens', sa.Column('type_id', sa.INTEGER(), autoincrement=False, nullable=False))
op.create_foreign_key('lens_type_id_fkey', 'lens', 'lenses_types', ['type_id'], ['id'])
# ### end Alembic commands ###

View File

@ -0,0 +1,42 @@
"""Переименовал таблицу с типами линз - Шаг 2
Revision ID: ee12ffac87d7
Revises: 7f50bd3f0523
Create Date: 2025-02-18 11:41:15.961296
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'ee12ffac87d7'
down_revision: Union[str, None] = '7f50bd3f0523'
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.create_table('lens_types',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('title', sa.VARCHAR(length=150), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('title')
)
op.drop_table('lenses_types')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('lenses_types',
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column('title', sa.VARCHAR(length=150), autoincrement=False, nullable=False),
sa.PrimaryKeyConstraint('id', name='lenses_types_pkey'),
sa.UniqueConstraint('title', name='lenses_types_title_key')
)
op.drop_table('lens_types')
# ### end Alembic commands ###

View File

@ -1,3 +0,0 @@
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

View File

@ -0,0 +1,18 @@
from typing import Optional
from pydantic import BaseModel
class LensEntity(BaseModel):
id: Optional[int] = None
tor: float
trial: float
esa: float
fvc: float
preset_refraction: float
diameter: float
periphery_toricity: float
side: str
issued: bool
type_id: int

View File

@ -5,7 +5,7 @@ Base = declarative_base()
from app.domain.models.appointment_files import AppointmentFile from app.domain.models.appointment_files import AppointmentFile
from app.domain.models.appointments import Appointment from app.domain.models.appointments import Appointment
from app.domain.models.appointment_types import AppointmentType from app.domain.models.appointment_types import AppointmentType
from app.domain.models.lenses_types import LensesType from app.domain.models.lens_types import LensType
from app.domain.models.lens_issues import LensIssue from app.domain.models.lens_issues import LensIssue
from app.domain.models.lens import Lens from app.domain.models.lens import Lens
from app.domain.models.mailing_delivery_methods import MailingDeliveryMethod from app.domain.models.mailing_delivery_methods import MailingDeliveryMethod
@ -14,6 +14,7 @@ from app.domain.models.mailing import Mailing
from app.domain.models.patients import Patient from app.domain.models.patients import Patient
from app.domain.models.recipients import Recipient from app.domain.models.recipients import Recipient
from app.domain.models.roles import Role from app.domain.models.roles import Role
from app.domain.models.set_contents import SetContent
from app.domain.models.set_lens import SetLens from app.domain.models.set_lens import SetLens
from app.domain.models.sets import Set from app.domain.models.sets import Set
from app.domain.models.users import User from app.domain.models.users import User

View File

@ -24,9 +24,9 @@ class Lens(Base):
side = Column(Enum(SideEnum), nullable=False) side = Column(Enum(SideEnum), nullable=False)
issued = Column(Boolean, nullable=False, default=False) issued = Column(Boolean, nullable=False, default=False)
type_id = Column(Integer, ForeignKey('lenses_types.id'), nullable=False) type_id = Column(Integer, ForeignKey('lens_types.id'), nullable=False)
type = relationship('LensesType', back_populates='lenses') type = relationship('LensType', back_populates='lenses')
set = relationship('SetLens', back_populates='lens') set = relationship('SetLens', back_populates='lens')
lens_issues = relationship('LensIssue', back_populates='lens') lens_issues = relationship('LensIssue', back_populates='lens')

View File

@ -4,8 +4,8 @@ from sqlalchemy.orm import relationship
from app.domain.models import Base from app.domain.models import Base
class LensesType(Base): class LensType(Base):
__tablename__ = 'lenses_types' __tablename__ = 'lens_types'
id = Column(Integer, primary_key=True, autoincrement=True) id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(VARCHAR(150), nullable=False, unique=True) title = Column(VARCHAR(150), nullable=False, unique=True)

View File

@ -0,0 +1,23 @@
from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from app.domain.models import Base
class SetContent(Base):
__tablename__ = 'set_contents'
id = Column(Integer, primary_key=True, autoincrement=True)
tor = Column(Integer, nullable=False)
trial = Column(Integer, nullable=False)
esa = Column(Integer, nullable=False)
fvc = Column(Integer, nullable=False)
preset_refraction = Column(Integer, nullable=False)
diameter = Column(Integer, nullable=False)
periphery_toricity = Column(Integer, nullable=False)
side = Column(Integer, nullable=False)
count = Column(Integer, nullable=False)
set_id = Column(Integer, ForeignKey('sets.id'), nullable=False)
set = relationship('Set', back_populates='contents')

View File

@ -11,4 +11,5 @@ class Set(Base):
title = Column(VARCHAR(150), nullable=False, unique=True) title = Column(VARCHAR(150), nullable=False, unique=True)
count = Column(Integer, nullable=False) count = Column(Integer, nullable=False)
contents = relationship('SetContent', back_populates='set')
lens = relationship('SetLens', back_populates='set') lens = relationship('SetLens', back_populates='set')

View File

@ -4,6 +4,7 @@ from typing import Optional
import jwt import jwt
from fastapi import HTTPException from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status
from app.application.users_repository import UsersRepository from app.application.users_repository import UsersRepository
from app.settings import get_auth_data from app.settings import get_auth_data
@ -22,7 +23,7 @@ class AuthService:
"user_id": user.id "user_id": user.id
} }
raise HTTPException(status_code=403, detail="Invalid login or password") raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid login or password")
@staticmethod @staticmethod
def create_access_token(data: dict) -> str: def create_access_token(data: dict) -> str:

View File

@ -2,6 +2,7 @@ from fastapi import Depends, HTTPException, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt import jwt
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status
from app.database.session import get_db from app.database.session import get_db
from app.domain.models.users import User from app.domain.models.users import User
@ -20,23 +21,23 @@ async def get_current_user(
try: try:
payload = jwt.decode(credentials.credentials, auth_data["secret_key"], algorithms=[auth_data["algorithm"]]) payload = jwt.decode(credentials.credentials, auth_data["secret_key"], algorithms=[auth_data["algorithm"]])
except jwt.ExpiredSignatureError: except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token has expired") raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token has expired")
except jwt.InvalidTokenError: except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token") raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
user_id = payload.get("user_id") user_id = payload.get("user_id")
if user_id is None: if user_id is None:
raise HTTPException(status_code=401, detail="Invalid token") raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
user = await UsersRepository(db).get_by_id_with_role(user_id) user = await UsersRepository(db).get_by_id_with_role(user_id)
if user is None: if user is None:
raise HTTPException(status_code=401, detail="User not found") raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
return user return user
def require_admin(user: User = Depends(get_current_user)): def require_admin(user: User = Depends(get_current_user)):
if user.role.title != "Администратор": if user.role.title != "Администратор":
raise HTTPException(status_code=403, detail="Access denied") raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied")
return user return user

View File

@ -0,0 +1,109 @@
from typing import Optional
from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status
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
class LensesService:
def __init__(self, db: AsyncSession):
self.lenses_repository = LensesRepository(db)
self.lens_types_repository = LensTypesRepository(db)
async def get_all_lenses(self) -> list[LensEntity]:
lenses = await self.lenses_repository.get_all()
return [
LensEntity(
id=lens.id,
tor=lens.tor,
trial=lens.trial,
esa=lens.esa,
fvc=lens.fvs,
preset_refraction=lens.preset_refraction,
diameter=lens.diameter,
periphery_toricity=lens.periphery_toricity,
side=lens.side,
issued=lens.issued,
type_id=lens.type_id,
)
for lens in lenses
]
async def create_lens(self, lens: LensEntity) -> LensEntity:
lens_type = self.lens_types_repository.get_by_id(lens.type_id)
if not lens_type:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail='The lens type with this ID was not found'
)
lens_model = Lens(
tor=lens.tor,
trial=lens.trial,
esa=lens.esa,
fvc=lens.fvc,
preset_refraction=lens.preset_refraction,
diameter=lens.diameter,
periphery_toricity=lens.periphery_toricity,
side=lens.side,
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,
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,
)
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:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Lens not found")
return result

View File

@ -2,6 +2,7 @@ from typing import Optional
from fastapi import HTTPException from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status
from app.application.patients_repository import PatientsRepository from app.application.patients_repository import PatientsRepository
from app.domain.entities.patient import PatientEntity from app.domain.entities.patient import PatientEntity
@ -82,12 +83,12 @@ class PatientsService:
correction=patient_model.correction, correction=patient_model.correction,
) )
raise HTTPException(status_code=404, detail="Patient not found") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Patient not found")
async def delete_patient(self, patient_id: int) -> Optional[bool]: async def delete_patient(self, patient_id: int) -> Optional[bool]:
result = await self.patient_repository.delete(patient_id) is not None result = await self.patient_repository.delete(patient_id) is not None
if not result: if not result:
raise HTTPException(status_code=404, detail="Patient not found") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Patient not found")
return result return result