"""Event and gallery API schemas.""" from uuid import UUID from ninja import ModelSchema, Schema from pydantic import field_validator from typing import Literal, Optional, List from datetime import datetime from api.schemas.blog import AuthorSchema from events.models import Event, Registration from gallery.models import Gallery from payments.models import Payment class EventGallerySchema(ModelSchema): """Schema representing gallery items associated with an event.""" uploaded_by: AuthorSchema file_size_mb: float markdown_url: str absolute_image_url: Optional[str] = None class Config: model = Gallery model_fields = ['id', 'title', 'description', 'image', 'alt_text', 'width', 'height', 'is_public', 'created_at'] @staticmethod def resolve_absolute_image_url(obj, context): request = context['request'] if obj.image and hasattr(obj.image, 'url'): return request.build_absolute_uri(obj.image.url) return None class EventSchema(ModelSchema): """Schema providing full event details for API responses.""" gallery_images: List[EventGallerySchema] description_html: str registration_count: int absolute_featured_image_url: Optional[str] = None class Config: model = Event model_fields = [ 'id', 'title', 'slug', 'description', 'featured_image', 'event_type', 'address', 'location', 'online_link', 'start_time', 'end_time', 'registration_start_date', 'registration_end_date', 'registration_success_markdown', 'capacity', 'price', 'status', 'created_at', 'updated_at' ] @staticmethod def resolve_absolute_featured_image_url(obj, context): request = context['request'] if obj.featured_image and hasattr(obj.featured_image, 'url'): return request.build_absolute_uri(obj.featured_image.url) return None @staticmethod def resolve_registration_count(obj): return obj.registrations.filter(status__in=[Registration.StatusChoices.CONFIRMED, Registration.StatusChoices.ATTENDED]).count() @staticmethod def resolve_description_html(obj): return obj.description_html class EventListSchema(Schema): """Condensed event representation for list endpoints.""" id: int title: str slug: str featured_image: Optional[str] = None absolute_featured_image_url: Optional[str] = None event_type: str start_time: datetime end_time: datetime registration_start_date: Optional[datetime] = None registration_end_date: Optional[datetime] = None capacity: Optional[int] = None price: Optional[float] = None status: str registration_count: int created_at: datetime @staticmethod def resolve_absolute_featured_image_url(obj, context): request = context['request'] if obj.featured_image and hasattr(obj.featured_image, 'url'): return request.build_absolute_uri(obj.featured_image.url) return None @staticmethod def resolve_registration_count(obj): return obj.registrations.filter(status__in=[Registration.StatusChoices.CONFIRMED, Registration.StatusChoices.ATTENDED]).count() class EventCreateSchema(Schema): """Payload for creating events via the API.""" title: str description: str event_type: str address: Optional[str] = None location: Optional[str] = None online_link: Optional[str] = None start_time: datetime end_time: datetime registration_start_date: Optional[datetime] = None registration_end_date: Optional[datetime] = None capacity: Optional[int] = None price: Optional[float] = None status: str = "draft" gallery_image_ids: Optional[List[int]] = [] class EventUpdateSchema(Schema): """Payload for updating events via the API.""" title: Optional[str] = None description: Optional[str] = None event_type: Optional[str] = None address: Optional[str] = None location: Optional[str] = None online_link: Optional[str] = None start_time: Optional[datetime] = None end_time: Optional[datetime] = None registration_start_date: Optional[datetime] = None registration_end_date: Optional[datetime] = None capacity: Optional[int] = None price: Optional[float] = None status: Optional[str] = None gallery_image_ids: Optional[List[int]] = None class RegistrationSchema(ModelSchema): """Schema describing a registration entry with event context.""" user: AuthorSchema event: EventListSchema discount_code: str | None = None class Config: model = Registration model_fields = [ 'id', 'status', 'registered_at', 'ticket_id', 'discount_amount', 'final_price', 'created_at', 'updated_at', ] @staticmethod def resolve_discount_code(obj): return obj.discount_code.code if obj.discount_code else None class AdminUserSchema(Schema): id: int username: str first_name: str last_name: str email: str class PaymentAdminSchema(Schema): id: int authority: Optional[str] ref_id: Optional[str] status: int status_label: str base_amount: int discount_amount: int amount: int verified_at: Optional[datetime] created_at: datetime discount_code: Optional[str] user: AdminUserSchema @field_validator("discount_code", mode="before") def normalize_discount_code(cls, value): if value is None: return None if hasattr(value, "code"): return value.code return str(value) class RegistrationAdminSchema(Schema): id: int ticket_id: UUID status: str status_label: str registered_at: datetime final_price: Optional[int] discount_amount: Optional[int] user: AdminUserSchema payments: List[PaymentAdminSchema] class EventAdminDetailSchema(EventSchema): registrations: List[RegistrationAdminSchema] = [] @staticmethod def resolve_registrations(obj): return obj.registrations.select_related("user").prefetch_related( "payments__discount_code" ).order_by("-registered_at") class PaginatedRegistrationSchema(Schema): count: int next: Optional[str] = None previous: Optional[str] = None results: List[RegistrationAdminSchema] class RegistrationStatusUpdateSchema(Schema): status: str class RegisterationDetailSchema(Schema): """Detailed registration information with associated event metadata.""" event_image: Optional[str] event_title: str event_type: str ticket_id: UUID status: str registered_at: datetime success_markdown: Optional[str] class EventBriefSchema(Schema): """Minimal event representation used for nested responses.""" id: int title: str slug: str start_date: datetime end_date: Optional[datetime] = None location: Optional[str] = None price: int absolute_image_url: Optional[str] = None class MyEventRegistrationOut(Schema): """Registration information as returned to authenticated users.""" id: int created_at: datetime status: Literal["pending", "confirmed", "cancelled", "attended"] event: EventBriefSchema class RegistrationStatusOut(Schema): is_registered: bool class RegistrationCreateSchema(Schema): discount_code: Optional[str] = None