Files
guilan-ace-backend/apps/events/event_status.py
Amirhossein Khalili 6c5fadef2f feat: derive event lifecycle status from dates and flags
Replace persisted event statuses with publication and cancellation flags, and compute public lifecycle labels from event timing and registration capacity. Keep filters, registration checks, and profile responses aligned with the derived status.
2026-10-03 12:46:16 +03:30

100 lines
3.5 KiB
Python

from django.db.models import Count, F, Q
EVENT_STATUS_LABELS = {
"draft": "پیش‌نویس",
"cancelled": "لغو شده",
"completed": "برگزار شده",
"in_progress": "در حال برگزاری",
"registration_closed": "پایان ثبت‌نام",
"full": "تکمیل ظرفیت",
"registration_open": "در حال ثبت‌نام",
"upcoming": "به‌زودی",
}
PUBLIC_EVENT_STATUSES = tuple(key for key in EVENT_STATUS_LABELS if key != "draft")
def active_registration_count_annotation(*, now, relation="registrations"):
return Count(
relation,
filter=Q(**{f"{relation}__is_deleted": False})
& (
Q(**{f"{relation}__status__in": ["confirmed", "attended"]})
| Q(
**{
f"{relation}__status": "pending",
f"{relation}__checkout_expires_at__gt": now,
}
)
),
distinct=True,
)
def event_status_filter(status, *, now, prefix="", count_field="browse_registration_count"):
"""Return a queryset condition for a derived event status.
The caller should restrict the queryset to published events and annotate the
active registration count using ``count_field`` before applying this filter.
"""
def field(name):
return f"{prefix}{name}"
count = F(count_field)
before_event_start = Q(**{f"{field('start_time')}__gt": now})
deadline_open = Q(**{f"{field('registration_end_date')}__isnull": True}) | Q(
**{f"{field('registration_end_date')}__gt": now}
)
if status == "cancelled":
return Q(**{field("is_cancelled"): True})
if status == "completed":
return Q(**{f"{field('end_time')}__lte": now}) & Q(**{field("is_cancelled"): False})
if status == "in_progress":
return (
Q(**{f"{field('start_time')}__lte": now})
& Q(**{f"{field('end_time')}__gt": now})
& Q(**{field("is_cancelled"): False})
)
if status == "registration_closed":
return (
before_event_start
& Q(**{f"{field('registration_end_date')}__lte": now})
& Q(**{field("is_cancelled"): False})
)
if status == "full":
return (
before_event_start
& deadline_open
& Q(**{f"{field('capacity')}__isnull": False})
& Q(**{f"{field('capacity')}__lte": count})
& Q(**{field("is_cancelled"): False})
)
if status == "registration_open":
registration_started = Q(**{f"{field('registration_start_date')}__isnull": True}) | Q(
**{f"{field('registration_start_date')}__lte": now}
)
capacity_available = Q(**{f"{field('capacity')}__isnull": True}) | Q(
**{f"{field('capacity')}__gt": count}
)
return (
before_event_start
& registration_started
& deadline_open
& capacity_available
& Q(**{field("is_cancelled"): False})
)
if status == "upcoming":
registration_not_started = Q(**{f"{field('registration_start_date')}__gt": now})
capacity_available = Q(**{f"{field('capacity')}__isnull": True}) | Q(
**{f"{field('capacity')}__gt": count}
)
return (
before_event_start
& registration_not_started
& deadline_open
& capacity_available
& Q(**{field("is_cancelled"): False})
)
raise ValueError(f"Unsupported event status: {status}")