import asyncio import logging import uuid from typing import TYPE_CHECKING from src import domain, dto if TYPE_CHECKING: from .. import Usecase log = logging.getLogger(__name__) async def update_creative( self: 'Usecase', creative_id: uuid.UUID, input: dto.UpdateCreativeInput, user_id: uuid.UUID, workspace_id: uuid.UUID, ) -> dto.CreativeOutput: context = await self.ensure_workspace_permission(workspace_id, user_id, domain.PermissionKey.PROJECTS_WRITE) creative = await self.database.get_creative(workspace_id, creative_id) if not creative: log.warning('User %s attempted to update unavailable creative %s', user_id, creative_id) raise domain.CreativeNotFound(creative_id) context.ensure_project_permission(domain.PermissionKey.PROJECTS_WRITE, creative.project_id) if input.name: creative.name = input.name if input.text: creative.text = domain.replace_invite_link_with_tag(input.text) if input.media_data is not None: domain.validate_media_size(input.media_data) # Удаляем старое медиа из S3 в фоне old_media_key = creative.media_s3_key if old_media_key: async def delete_old_media() -> None: try: await self.s3.delete(old_media_key) log.info('Deleted old creative media from S3: %s', old_media_key) except Exception as e: log.warning('Failed to delete old creative media from S3: %s', e) asyncio.create_task(delete_old_media()) # Загружаем новое медиа в S3 new_media_data = input.media_data if new_media_data: file_id = uuid.uuid4() new_media_key = f'creatives/{workspace_id}/{file_id}' content_type = _get_content_type(input.media_type) await self.s3.upload(new_media_key, new_media_data, content_type) log.info('Uploaded new creative media to S3: %s', new_media_key) creative.media_s3_key = new_media_key else: creative.media_s3_key = None if input.media_type is not None: creative.media_type = input.media_type if input.media_file_id is not None: creative.media_file_id = input.media_file_id if input.buttons is not None: creative.buttons = [button.model_dump() for button in input.buttons] if input.status: creative.status = input.status await self.database.update_creative(creative) placements_count = await self.database.count_publications_by_creative(creative.id) return dto.CreativeOutput( id=creative.id, name=creative.name, text=creative.text, media_type=creative.media_type, media_file_id=creative.media_file_id, buttons=creative.buttons, project_id=creative.project_id, project_channel_title=creative.project.channel.title, created_at=creative.created_at, status=creative.status, placements_count=placements_count, ) def _get_content_type(media_type: str | None) -> str: if not media_type: return 'application/octet-stream' mapping = { 'photo': 'image/jpeg', 'video': 'video/mp4', 'animation': 'image/gif', } return mapping.get(media_type, 'application/octet-stream')