99 lines
3.3 KiB
Python
99 lines
3.3 KiB
Python
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_placements_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')
|