Files
guilan-ace-backend/apps/content/api/views.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

201 lines
8.7 KiB
Python

from django.http import HttpResponse
from django.utils import timezone
from ninja import Router
from ninja.errors import HttpError
from apps.events.models import Event
from apps.content.current import AboutContent, HomeContent, ListingContent, SiteContent
from apps.content.api.schemas import AboutPageOut, HomePageOut, ListingPageOut, PageContentOut, SiteContentOut
content_router = Router(tags=["Public content"])
def _media(request, field, alt):
if not field or not field.name:
return None
return {"url": request.build_absolute_uri(field.url), "alt": alt or ""}
def _seo(request, obj):
return {
"title": obj.seo_title,
"description": obj.seo_description,
"image": _media(request, obj.seo_image, obj.seo_image_alt),
}
def _about_member_out(request, member, year_label=None):
return {
"name": member.name,
"role": member.role,
"entry_year": year_label or member.entry_year,
"bio": member.bio,
"photo": _media(request, member.photo, member.photo_alt),
"position": member.position,
}
def _about_event_out(request, event):
return {
"id": event.pk,
"title": event.title,
"slug": event.slug,
"start_time": event.start_time,
"featured_image": _media(request, event.featured_image, f"پوستر {event.title}"),
}
@content_router.get("/site", response=SiteContentOut)
def get_site_content(request, response: HttpResponse):
site = SiteContent.objects.filter(pk=1).first()
if site is None:
raise HttpError(404, "Site content not found")
response["Cache-Control"] = "no-store"
return {
"official_name": site.official_name,
"short_name": site.short_name,
"faculty_subline": site.faculty_subline,
"description": site.description,
"logo": _media(request, site.logo, site.logo_alt),
"navigation": {
"home": site.nav_home,
"about": site.nav_about,
"events": site.nav_events,
"blog": site.nav_blog,
},
"contacts": {
"email": site.email,
"phone": site.phone,
"telegram": site.telegram_url,
"instagram": site.instagram_url,
"website": site.website_url,
},
"address": site.address,
"footer_copy": site.footer_copy,
"enamad_image": _media(request, site.enamad_image, site.enamad_image_alt),
"enamad_url": site.enamad_url,
"seo": _seo(request, site),
}
@content_router.get("/pages/{slug}", response=PageContentOut)
def get_page_content(request, response: HttpResponse, slug: str):
response["Cache-Control"] = "no-store"
if slug == "home":
page = HomeContent.objects.filter(pk=1).first()
if page is None:
raise HttpError(404, "Page content not found")
return {
"slug": "home", "title": page.title, "intro": page.intro, "seo": _seo(request, page),
"hero_heading": page.hero_heading, "hero_body": page.hero_body,
"hero_image": _media(request, page.hero_image, page.hero_image_alt),
"hero_action_label": page.hero_action_label,
"hero_action_url": page.hero_action_url,
"hero_secondary_action_label": page.hero_secondary_action_label,
"hero_secondary_action_url": page.hero_secondary_action_url,
"featured_events_heading": page.featured_events_heading,
"featured_events_intro": page.featured_events_intro,
"featured_events_link_label": page.featured_events_link_label,
"featured_events_url": page.featured_events_url,
"featured_event_action_label": page.featured_event_action_label,
"featured_posts_heading": page.featured_posts_heading,
"featured_posts_intro": page.featured_posts_intro,
"featured_posts_link_label": page.featured_posts_link_label,
"featured_posts_url": page.featured_posts_url,
"featured_post_action_label": page.featured_post_action_label,
"participation_heading": page.participation_heading,
"participation_body": page.participation_body,
"participation_image": _media(request, page.participation_image, page.participation_image_alt),
"participation_action_label": page.participation_action_label,
"participation_action_url": page.participation_action_url,
"participation_secondary_action_label": page.participation_secondary_action_label,
"participation_secondary_action_url": page.participation_secondary_action_url,
}
if slug == "about":
page = AboutContent.objects.filter(pk=1).first()
if page is None:
raise HttpError(404, "Page content not found")
years_by_label = {}
for year in page.years.prefetch_related("members", "events").all():
label = year.year_label.strip() or "سال نامشخص"
events = year.events.filter(
is_deleted=False,
is_published=True,
start_time__lte=timezone.now(),
).order_by("-start_time", "-pk")
years_by_label[label] = {
"year_label": label,
"position": year.position,
"_pk": year.pk,
"members": [
_about_member_out(request, member, label)
for member in year.members.all()
],
"selected_events": [_about_event_out(request, event) for event in events],
}
legacy_members = page.members.filter(year_group__isnull=True).order_by("entry_year", "position", "pk")
for member in legacy_members:
label = member.entry_year.strip() or "سال نامشخص"
year_data = years_by_label.setdefault(
label,
{
"year_label": label,
"position": 0,
"_pk": 0,
"members": [],
"selected_events": [],
},
)
year_data["members"].append(_about_member_out(request, member, label))
years = sorted(
years_by_label.values(),
key=lambda year: (year["position"], year["_pk"], year["year_label"]),
)
all_members = [member for year in years for member in year["members"]]
return {
"slug": "about", "title": page.title, "intro": page.intro, "seo": _seo(request, page),
"identity_heading": page.identity_heading, "identity_body": page.identity_body,
"mission_heading": page.mission_heading, "mission_body": page.mission_body,
"vision_heading": page.vision_heading, "vision_body": page.vision_body,
"activities_heading": page.activities_heading, "activities_body": page.activities_body,
"values_heading": page.values_heading, "values_body": page.values_body,
"faq_heading": page.faq_heading, "contact_heading": page.contact_heading,
"members_heading": page.members_heading, "members_intro": page.members_intro,
"gallery_heading": page.gallery_heading, "gallery_intro": page.gallery_intro,
"past_events_heading": page.past_events_heading,
"history_heading": page.history_heading,
"audience_heading": page.audience_heading,
"year_events_heading": page.year_events_heading,
"cta_heading": page.cta_heading,
"cta_body": page.cta_body,
"cta_action_label": page.cta_action_label,
"cta_action_url": page.cta_action_url,
"items": list(page.items.exclude(kind__in=("committees", "affiliations")).values("kind", "title", "text", "position")),
"members": all_members,
"gallery_images": [
{
"image": _media(request, item.image, item.image_alt),
"caption": item.caption, "position": item.position,
}
for item in page.gallery_images.all()
],
"audiences": list(page.audiences.values("title", "text", "icon", "position")),
"milestones": list(page.milestones.values("year_label", "title", "description", "position")),
"years": [
{key: value for key, value in year.items() if not key.startswith("_")}
for year in years
],
}
if slug in ("events", "blog"):
page = ListingContent.objects.filter(slug=slug).first()
if page is None:
raise HttpError(404, "Page content not found")
return {"slug": page.slug, "title": page.title, "intro": page.intro, "seo": _seo(request, page)}
raise HttpError(404, "Page content not found")