Files
MathApp/content/management/commands/mark_abandoned_node_progress.py

57 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
تشخیصِ «نصفه‌رها» (abandoned) با سیگنال قابل انجام نیست چون به گذر زمانِ
بدون هیچ رویدادی نیاز داره، نه یک event مشخص؛ برای همین management
commandه، نه receiver. باید periodic اجرا بشه (cron / celery beat)،
مثلاً یک‌بار در روز:
python manage.py mark_abandoned_node_progress
آستانه از settings.NODE_PROGRESS_ABANDON_DAYS خونده می‌شه (پیش‌فرض ۱۴ روز).
"""
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils import timezone
from content.models import NodeProgress
from statuslog.signals import status_changed
class Command(BaseCommand):
help = 'رکوردهای NodeProgress در حال انجام که مدت‌ها فعالیتی نداشته‌اند را نصفه‌رها (abandoned) علامت می‌زند.'
def handle(self, *args, **options):
abandon_days = getattr(settings, 'NODE_PROGRESS_ABANDON_DAYS', 14)
threshold = timezone.now() - timezone.timedelta(days=abandon_days)
stale_qs = NodeProgress.objects.filter(
status='in_progress', last_activity_at__lt=threshold,
)
count = 0
# هر رکورد جدا اتمیک است (نه کل حلقه) تا خطای یک رکورد بقیه را rollback نکند.
# id ها جدا گرفته می‌شوند و هر ردیف زیر select_for_update دوباره خوانده و
# شرط abandoned بعد از قفل دوباره چک می‌شود، تا فعالیت هم‌زمان کاربر overwrite نشود.
stale_ids = list(stale_qs.values_list('id', flat=True))
for progress_id in stale_ids:
with transaction.atomic():
progress = NodeProgress.objects.select_for_update().get(pk=progress_id)
if progress.status != 'in_progress' or progress.last_activity_at is None \
or progress.last_activity_at >= threshold:
continue
old_status = progress.status
progress.status = 'abandoned'
progress.save(update_fields=['status'])
status_changed.send(
sender='content.NodeProgress', object_id=progress.id,
old_status=old_status, new_status='abandoned',
event='inactivity_timeout', user_id=progress.user_id,
)
count += 1
self.stdout.write(self.style.SUCCESS(
f'{count} رکورد NodeProgress به abandoned تغییر کرد (آستانه: {abandon_days} روز).'
))