статусы

This commit is contained in:
Artem Tsyrulnikov
2026-01-28 17:15:28 +03:00
parent e87958fb60
commit 2627cafa91
11 changed files with 333 additions and 3 deletions

View 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