feat: Добавлено лого команды и filename

This commit is contained in:
Андрей Дувакин 2025-06-03 14:11:02 +05:00
parent 562166086e
commit 21bc00ee33
6 changed files with 141 additions and 2 deletions

View File

@ -1,10 +1,12 @@
from typing import Optional
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, UploadFile, File
from sqlalchemy.ext.asyncio import AsyncSession
from starlette.responses import FileResponse
from app.database.session import get_db
from app.domain.entities.team import TeamEntity
from app.domain.entities.team_logo import TeamLogoEntity
from app.infrastructure.dependencies import get_current_user, require_admin
from app.infrastructure.teams_service import TeamsService
@ -97,3 +99,33 @@ async def delete_team(
):
teams_service = TeamsService(db)
return await teams_service.delete_team(team_id)
@router.get(
"/{team_id}/file/",
response_class=FileResponse,
summary="Download logo file by team ID",
description="Returns the image file for the given team ID",
)
async def download_photo_file(
team_id: int,
db: AsyncSession = Depends(get_db),
):
teams_service = TeamsService(db)
return await teams_service.get_photo_file_by_team_id(team_id)
@router.post(
"/{team_id}/upload/",
response_model=TeamLogoEntity,
summary="Upload a new photo for a profile",
description="Uploads a new photo file and associates it with the given profile ID",
)
async def upload_photo(
team_id: int,
file: UploadFile = File(...),
db: AsyncSession = Depends(get_db),
user=Depends(get_current_user),
):
teams_service = TeamsService(db)
return await teams_service.upload_photo(team_id, file)

View File

@ -0,0 +1,32 @@
"""0007_добавил filename для лого команды
Revision ID: b6c6c906cd2b
Revises: 61271acdd22f
Create Date: 2025-06-03 14:01:32.752389
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'b6c6c906cd2b'
down_revision: Union[str, None] = '61271acdd22f'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('teams', sa.Column('logo_filename', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('teams', 'logo_filename')
# ### end Alembic commands ###

View File

@ -10,6 +10,7 @@ class TeamEntity(BaseModel):
title: str
description: Optional[str] = None
logo: Optional[str] = None
logo_filename: Optional[str] = None
git_url: Optional[str] = None
is_active: bool

View File

@ -0,0 +1,6 @@
from pydantic import BaseModel
class TeamLogoEntity(BaseModel):
filename: str
file_path: str

View File

@ -10,6 +10,7 @@ class Team(AdvancedBaseModel):
title = Column(VARCHAR(150), nullable=False)
description = Column(VARCHAR(150))
logo = Column(String)
logo_filename = Column(String)
git_url = Column(String)
is_active = Column(Boolean, default=False, nullable=False, server_default='false')

View File

@ -1,10 +1,17 @@
import os
import uuid
from typing import Optional, Any, Coroutine
from fastapi import HTTPException, status
import aiofiles
from fastapi import HTTPException, status, UploadFile
from magic import magic
from sqlalchemy.ext.asyncio import AsyncSession
from starlette.responses import FileResponse
from werkzeug.utils import secure_filename
from app.application.teams_repository import TeamsRepository
from app.domain.entities.team import TeamEntity
from app.domain.entities.team_logo import TeamLogoEntity
from app.domain.models import Team
@ -75,6 +82,36 @@ class TeamsService:
return self.model_to_entity(result)
async def get_photo_file_by_team_id(self, team_id: int) -> FileResponse:
team = await self.teams_repository.get_by_id(team_id)
if not team:
raise HTTPException(404, "Команда с таким ID не найдена")
if not os.path.exists(team.logo):
raise HTTPException(404, "Файл логотипа не найден")
return FileResponse(
team.logo,
media_type=self.get_media_type(team.logo),
filename=team.logo_filename,
)
async def upload_photo(self, team_id: int, file: UploadFile):
team = await self.teams_repository.get_by_id(team_id)
self.validate_file_type(file)
team.logo = await self.save_file(file)
team.logo_filename = self.generate_filename(file)
team = await self.teams_repository.update(team)
return TeamLogoEntity(
filename=team.logo_filename,
file_path=team.logo,
)
@staticmethod
def model_to_entity(team_model: Team) -> TeamEntity:
return TeamEntity(
@ -82,6 +119,7 @@ class TeamsService:
title=team_model.title,
description=team_model.description,
logo=team_model.logo,
logo_filename=team_model.logo_filename,
git_url=team_model.git_url,
is_active=team_model.is_active,
)
@ -92,6 +130,7 @@ class TeamsService:
title=team_entity.title,
description=team_entity.description,
logo=team_entity.logo,
logo_filename=team_entity.logo_filename,
git_url=team_entity.git_url,
is_active=team_entity.is_active,
)
@ -100,3 +139,31 @@ class TeamsService:
team_model.id = team_entity.id
return team_model
async def save_file(self, file: UploadFile, upload_dir: str = "uploads/team_logos") -> str:
os.makedirs(upload_dir, exist_ok=True)
filename = self.generate_filename(file)
file_path = os.path.join(upload_dir, filename)
async with aiofiles.open(file_path, 'wb') as out_file:
content = await file.read()
await out_file.write(content)
return file_path
@staticmethod
def validate_file_type(file: UploadFile):
mime = magic.Magic(mime=True)
file_type = mime.from_buffer(file.file.read(1024))
file.file.seek(0)
if file_type not in ["image/jpeg", "image/png"]:
raise HTTPException(400, "Invalid file type")
@staticmethod
def generate_filename(file: UploadFile):
return secure_filename(f"{uuid.uuid4()}_{file.filename}")
@staticmethod
def get_media_type(filename: str) -> str:
extension = filename.split('.')[-1].lower()
return f"image/{extension}" if extension in ['jpeg', 'jpg', 'png'] else "application/octet-stream"