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") event_statuses = (Event.StatusChoices.PUBLISHED, Event.StatusChoices.COMPLETED) 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, status__in=event_statuses, 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")