feat: passport images for user

This commit is contained in:
2024-12-04 20:20:03 +04:00
parent 9a6fc3fdd7
commit d56e292276
4 changed files with 88 additions and 2 deletions

View File

@@ -107,6 +107,13 @@ class User(BaseModel):
uselist=True, uselist=True,
) )
passport_images = relationship(
'PassportImage',
back_populates='user',
lazy='selectin',
cascade="all, delete-orphan"
)
class Position(BaseModel): class Position(BaseModel):
__tablename__ = 'positions' __tablename__ = 'positions'
@@ -119,3 +126,12 @@ class Position(BaseModel):
uselist=False, uselist=False,
back_populates='position' back_populates='position'
) )
class PassportImage(BaseModel):
__tablename__ = 'passport_images'
id: Mapped[int] = mapped_column(primary_key=True)
user_id = mapped_column(ForeignKey('users.id'), nullable=False)
user: Mapped["User"] = relationship(back_populates='passport_images')
image_url: Mapped[str] = mapped_column(nullable=False)

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends, UploadFile
from backend.dependecies import SessionDependency from backend.dependecies import SessionDependency
from schemas.user import * from schemas.user import *
@@ -56,3 +56,17 @@ async def get_managers(
session: SessionDependency, session: SessionDependency,
): ):
return await UserService(session).get_managers() return await UserService(session).get_managers()
@user_router.post(
'/passport-images/upload/{user_id}',
response_model=UploadPassportImageResponse,
operation_id='upload_passport_image'
)
async def upload_passport_image(
user_id: int,
upload_file: UploadFile,
session: SessionDependency,
):
file_bytes = upload_file.file.read()
return await UserService(session).upload_passport_image(user_id, file_bytes)

View File

@@ -1,5 +1,7 @@
from typing import List, Optional from typing import List, Optional
from pydantic import model_validator
from schemas.base import BaseSchema, OkMessageSchema from schemas.base import BaseSchema, OkMessageSchema
from schemas.payrate import PayRateSchema from schemas.payrate import PayRateSchema
from schemas.position import PositionSchema from schemas.position import PositionSchema
@@ -8,6 +10,12 @@ from schemas.role import RoleSchema
# region Entities # region Entities
class PassportImageSchema(BaseSchema):
id: int
user_id: int
image_url: str
class BasicUser(BaseSchema): class BasicUser(BaseSchema):
telegram_id: int telegram_id: int
phone_number: str | None = None phone_number: str | None = None
@@ -23,6 +31,18 @@ class BasicUser(BaseSchema):
role_key: str role_key: str
pay_rate: Optional[PayRateSchema] = None pay_rate: Optional[PayRateSchema] = None
passport_image_url: str | None = None
passport_images: list[PassportImageSchema] | None = []
@model_validator(mode="after")
def image_url_to_list(cls, values):
passport_images = values.passport_images
if not passport_images:
return values
latest_image = passport_images[-1]
values.passport_image_url = latest_image.image_url
return values
class BaseUser(BasicUser): class BaseUser(BasicUser):
id: int id: int
@@ -70,4 +90,8 @@ class CreateUserResponse(OkMessageSchema):
class GetManagersResponse(BaseSchema): class GetManagersResponse(BaseSchema):
managers: List[UserSchema] managers: List[UserSchema]
class UploadPassportImageResponse(OkMessageSchema):
image_url: str | None = None
# endregion # endregion

View File

@@ -1,6 +1,8 @@
from sqlalchemy import select, update, delete, insert, and_ from sqlalchemy import select, update, delete, insert, and_
from models import User, user_position, user_pay_rate from backend import config
from external.s3_uploader.uploader import S3Uploader
from models import User, user_position, user_pay_rate, PassportImage
from services.base import BaseService from services.base import BaseService
from schemas.user import * from schemas.user import *
@@ -34,6 +36,7 @@ class UserService(BaseService):
try: try:
base_fields = request.data.model_dump_parent() base_fields = request.data.model_dump_parent()
del base_fields['pay_rate'] del base_fields['pay_rate']
del base_fields['passport_image_url']
user = User(**base_fields) user = User(**base_fields)
self.session.add(user) self.session.add(user)
await self.session.flush() await self.session.flush()
@@ -62,6 +65,8 @@ class UserService(BaseService):
return UpdateUserResponse(ok=False, message='Указанный пользователь не найден') return UpdateUserResponse(ok=False, message='Указанный пользователь не найден')
base_fields = request.data.model_dump_parent() base_fields = request.data.model_dump_parent()
del base_fields['pay_rate'] del base_fields['pay_rate']
del base_fields['passport_image_url']
del base_fields['passport_images']
stmt = update(User).values(**base_fields).where(User.id == request.data.id) stmt = update(User).values(**base_fields).where(User.id == request.data.id)
await self.session.execute(stmt) await self.session.execute(stmt)
await self.session.flush() await self.session.flush()
@@ -93,3 +98,30 @@ class UserService(BaseService):
return UpdateUserResponse(ok=True, message='Пользователь успешно обновлен') return UpdateUserResponse(ok=True, message='Пользователь успешно обновлен')
except Exception as e: except Exception as e:
return UpdateUserResponse(ok=False, message=str(e)) return UpdateUserResponse(ok=False, message=str(e))
async def upload_passport_image(self, user_id: int, file_bytes: bytes) -> UploadPassportImageResponse:
try:
user: Optional[User] = await self.session.get(User, user_id)
if not user:
raise Exception("Не удалось пользователя с указанным ID")
# removing previous images
for image in user.passport_images:
await self.session.delete(image)
s3_uploader = S3Uploader(config.S3_API_KEY)
response = await s3_uploader.upload(file_bytes)
response_url = response.get('link')
if not response_url:
raise Exception("Не удалось загрузить изображение")
passport_image = PassportImage(
user_id=user_id,
image_url=response_url,
)
self.session.add(passport_image)
await self.session.commit()
return UploadPassportImageResponse(
ok=True,
message='Изображение успешно загружено',
image_url=response_url
)
except Exception as e:
return UploadPassportImageResponse(ok=False, message=str(e))