статусы
This commit is contained in:
141
src/usecase/placement/update_post_status_cycle.py
Normal file
141
src/usecase/placement/update_post_status_cycle.py
Normal file
@@ -0,0 +1,141 @@
|
||||
import datetime
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from tortoise import timezone
|
||||
|
||||
from src import domain
|
||||
from src.domain.placement_post import PlacementPostStatus
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Usecase
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Статусы, которые не должны обновляться автоматически (финальные или ручные)
|
||||
FINAL_STATUSES = {
|
||||
PlacementPostStatus.COMPLETED_DELETED,
|
||||
PlacementPostStatus.COMPLETED_NOT_DELETED,
|
||||
PlacementPostStatus.CHECK_COMPLETED,
|
||||
}
|
||||
|
||||
# Статусы, при которых нужно проверять условия для автоматического обновления
|
||||
AUTO_UPDATE_STATUSES = {
|
||||
PlacementPostStatus.NO_STATUS,
|
||||
PlacementPostStatus.SEND_POST,
|
||||
PlacementPostStatus.POST_APPROVAL,
|
||||
PlacementPostStatus.WAITING_SCHEDULE,
|
||||
PlacementPostStatus.SCHEDULED,
|
||||
PlacementPostStatus.POST_PUBLISHED,
|
||||
PlacementPostStatus.CHECK_DELETED_EARLY,
|
||||
PlacementPostStatus.CHECK_NOT_PUBLISHED,
|
||||
}
|
||||
|
||||
|
||||
async def update_post_status_cycle(self: 'Usecase', interval_seconds: int) -> None:
|
||||
"""Автоматически обновляет статусы PlacementPost на основе состояния постов"""
|
||||
log.debug('Starting update_post_status_cycle')
|
||||
|
||||
# Получаем все PlacementPost со статусами, которые могут быть обновлены
|
||||
placement_posts = (
|
||||
await domain.PlacementPost.filter(
|
||||
status__in=list(AUTO_UPDATE_STATUSES),
|
||||
)
|
||||
.prefetch_related('placement', 'post', 'post__channel')
|
||||
.all()
|
||||
)
|
||||
|
||||
if not placement_posts:
|
||||
log.debug('No placement_posts to update')
|
||||
return
|
||||
|
||||
updated_count = 0
|
||||
now = timezone.now()
|
||||
|
||||
for placement_post in placement_posts:
|
||||
placement = placement_post.placement
|
||||
post = placement_post.post
|
||||
|
||||
if not placement:
|
||||
log.warning('PlacementPost %s missing placement', placement_post.id)
|
||||
continue
|
||||
|
||||
new_status = _determine_status(placement, placement_post, post, now)
|
||||
|
||||
if new_status and new_status != placement_post.status:
|
||||
old_status = placement_post.status
|
||||
placement_post.status = new_status
|
||||
await placement_post.save()
|
||||
updated_count += 1
|
||||
log.info(
|
||||
'Updated PlacementPost %s status: %s -> %s',
|
||||
placement_post.id,
|
||||
old_status,
|
||||
new_status,
|
||||
)
|
||||
|
||||
log.debug('Update post status cycle completed. Updated %s placement_posts', updated_count)
|
||||
|
||||
|
||||
def _determine_status(
|
||||
placement: domain.Placement,
|
||||
placement_post: domain.PlacementPost,
|
||||
post: domain.Post | None,
|
||||
now: datetime.datetime,
|
||||
) -> PlacementPostStatus | None:
|
||||
"""Определяет новый статус PlacementPost на основе текущего состояния"""
|
||||
|
||||
# Если поста нет и прошло время размещения - "Пост не вышел"
|
||||
if post is None:
|
||||
if placement.placement_at and placement.placement_at < now:
|
||||
return PlacementPostStatus.CHECK_NOT_PUBLISHED
|
||||
return None
|
||||
|
||||
# Если пост есть, но не опубликован - оставляем как есть
|
||||
if not post.published_at:
|
||||
return None
|
||||
|
||||
# Пост опубликован - проверяем условия для перехода статусов
|
||||
published_at = post.published_at
|
||||
deleted_at = post.deleted_from_channel_at
|
||||
|
||||
# Вычисляем время в топе
|
||||
if deleted_at:
|
||||
time_on_top = int((deleted_at - published_at).total_seconds())
|
||||
else:
|
||||
time_on_top = int((now - published_at).total_seconds())
|
||||
|
||||
# Получаем требуемую длительность из формата
|
||||
required_duration = domain.parse_format_duration(placement.format)
|
||||
|
||||
# Если формат "без удаления" - размещение отработало, если прошло 24 часа
|
||||
if required_duration is None:
|
||||
# По умолчанию считаем что "без удаления" = 24 часа минимум
|
||||
required_duration = 24 * 3600
|
||||
|
||||
# Проверяем условия для автоматических статусов
|
||||
|
||||
# Пост удалён раньше срока
|
||||
if deleted_at and time_on_top < required_duration:
|
||||
return PlacementPostStatus.CHECK_DELETED_EARLY
|
||||
|
||||
# Размещение отработало - пост удалён
|
||||
if deleted_at and time_on_top >= required_duration:
|
||||
return PlacementPostStatus.COMPLETED_DELETED
|
||||
|
||||
# Размещение отработало - пост не удалён
|
||||
if not deleted_at and time_on_top >= required_duration:
|
||||
return PlacementPostStatus.COMPLETED_NOT_DELETED
|
||||
|
||||
# Пост вышел, но ещё не отработал
|
||||
if placement_post.status in {
|
||||
PlacementPostStatus.NO_STATUS,
|
||||
PlacementPostStatus.SEND_POST,
|
||||
PlacementPostStatus.POST_APPROVAL,
|
||||
PlacementPostStatus.WAITING_SCHEDULE,
|
||||
PlacementPostStatus.SCHEDULED,
|
||||
}:
|
||||
return PlacementPostStatus.POST_PUBLISHED
|
||||
|
||||
return None
|
||||
Reference in New Issue
Block a user