202 lines
8.8 KiB
Python
202 lines
8.8 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")
|
|
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")
|