init
This commit is contained in:
247
backend/api/schemas/events.py
Normal file
247
backend/api/schemas/events.py
Normal file
@@ -0,0 +1,247 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user