first commit

This commit is contained in:
Anurag Jangid 2026-10-08 02:52:58 +05:30
commit 34ea15a54d
246 changed files with 18660 additions and 0 deletions

View file

View file

@ -0,0 +1,126 @@
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.core.errors import AppError
from app.models.catalog import Event, TicketType, Venue
from app.models.enums import TicketTypeStatus
from app.schemas.admin import (
EventCreate,
EventUpdate,
TicketTypeCreate,
TicketTypeUpdate,
VenueCreate,
VenueUpdate,
)
def list_venues_admin(db: Session) -> list[Venue]:
return list(db.scalars(select(Venue).order_by(Venue.name.asc())).all())
def create_venue(db: Session, body: VenueCreate) -> Venue:
if db.scalar(select(Venue.id).where(Venue.slug == body.slug)):
raise AppError("Venue slug already exists", status_code=409)
venue = Venue(**body.model_dump())
db.add(venue)
db.commit()
db.refresh(venue)
return venue
def update_venue(db: Session, venue_id: int, body: VenueUpdate) -> Venue:
venue = db.get(Venue, venue_id)
if not venue:
raise AppError("Venue not found", status_code=404)
data = body.model_dump(exclude_unset=True)
if "slug" in data and data["slug"] != venue.slug:
if db.scalar(select(Venue.id).where(Venue.slug == data["slug"])):
raise AppError("Venue slug already exists", status_code=409)
for key, value in data.items():
setattr(venue, key, value)
db.commit()
db.refresh(venue)
return venue
def list_events_admin(db: Session) -> list[Event]:
return list(db.scalars(select(Event).order_by(Event.starts_at.desc())).all())
def create_event(db: Session, body: EventCreate) -> Event:
if not db.get(Venue, body.venue_id):
raise AppError("Venue not found", status_code=400)
if db.scalar(select(Event.id).where(Event.slug == body.slug)):
raise AppError("Event slug already exists", status_code=409)
event = Event(**body.model_dump())
db.add(event)
db.commit()
db.refresh(event)
return event
def update_event(db: Session, event_id: int, body: EventUpdate) -> Event:
event = db.get(Event, event_id)
if not event:
raise AppError("Event not found", status_code=404)
data = body.model_dump(exclude_unset=True)
if "venue_id" in data and not db.get(Venue, data["venue_id"]):
raise AppError("Venue not found", status_code=400)
if "slug" in data and data["slug"] != event.slug:
if db.scalar(select(Event.id).where(Event.slug == data["slug"])):
raise AppError("Event slug already exists", status_code=409)
for key, value in data.items():
setattr(event, key, value)
db.commit()
db.refresh(event)
return event
def ticket_type_to_admin(tier: TicketType) -> dict:
return {
"id": tier.id,
"event_id": tier.event_id,
"name": tier.name,
"description": tier.description,
"price_paise": tier.price_paise,
"quantity_total": tier.quantity_total,
"quantity_sold": tier.quantity_sold,
"remaining": max(tier.quantity_total - tier.quantity_sold, 0),
"status": tier.status,
"sort_order": tier.sort_order,
}
def list_ticket_types(db: Session, event_id: int | None = None) -> list[dict]:
query = select(TicketType).order_by(TicketType.event_id.asc(), TicketType.sort_order.asc())
if event_id is not None:
query = query.where(TicketType.event_id == event_id)
return [ticket_type_to_admin(row) for row in db.scalars(query).all()]
def create_ticket_type(db: Session, body: TicketTypeCreate) -> TicketType:
if not db.get(Event, body.event_id):
raise AppError("Event not found", status_code=400)
tier = TicketType(**body.model_dump())
db.add(tier)
db.commit()
db.refresh(tier)
return tier
def update_ticket_type(db: Session, tier_id: int, body: TicketTypeUpdate) -> TicketType:
tier = db.get(TicketType, tier_id)
if not tier:
raise AppError("Ticket type not found", status_code=404)
data = body.model_dump(exclude_unset=True)
if "quantity_total" in data and data["quantity_total"] < tier.quantity_sold:
raise AppError("Total quantity cannot be less than sold count", status_code=400)
for key, value in data.items():
setattr(tier, key, value)
if tier.quantity_sold >= tier.quantity_total:
tier.status = TicketTypeStatus.sold_out
elif tier.status == TicketTypeStatus.sold_out and tier.quantity_sold < tier.quantity_total:
tier.status = TicketTypeStatus.active
db.commit()
db.refresh(tier)
return tier

View file

@ -0,0 +1,82 @@
from datetime import datetime, timezone
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.models.catalog import Event, TicketType
from app.models.enums import EventStatus, OrderStatus, TicketStatus
from app.models.orders import Order, Ticket
from app.schemas.admin import DashboardMetrics, EventStatsRow
def get_dashboard_metrics(db: Session) -> DashboardMetrics:
now = datetime.now(timezone.utc)
revenue = db.scalar(
select(func.coalesce(func.sum(Order.total_paise), 0)).where(Order.status == OrderStatus.paid)
) or 0
tickets_sold = db.scalar(select(func.coalesce(func.sum(TicketType.quantity_sold), 0))) or 0
tickets_total = db.scalar(select(func.coalesce(func.sum(TicketType.quantity_total), 0))) or 0
tickets_remaining = max(int(tickets_total) - int(tickets_sold), 0)
orders_total = db.scalar(select(func.count(Order.id))) or 0
orders_paid = db.scalar(select(func.count(Order.id)).where(Order.status == OrderStatus.paid)) or 0
orders_pending = db.scalar(select(func.count(Order.id)).where(Order.status == OrderStatus.pending)) or 0
attendance = db.scalar(select(func.count(Ticket.id)).where(Ticket.status == TicketStatus.used)) or 0
upcoming_events = (
db.scalar(
select(func.count(Event.id)).where(
Event.status == EventStatus.published,
Event.starts_at >= now,
)
)
or 0
)
return DashboardMetrics(
total_revenue_paise=int(revenue),
tickets_sold=int(tickets_sold),
tickets_remaining=tickets_remaining,
orders_total=int(orders_total),
orders_paid=int(orders_paid),
orders_pending=int(orders_pending),
attendance=int(attendance),
upcoming_events=int(upcoming_events),
)
def get_event_statistics(db: Session) -> list[EventStatsRow]:
events = db.scalars(select(Event).order_by(Event.starts_at.desc())).all()
rows: list[EventStatsRow] = []
for event in events:
tiers = db.scalars(select(TicketType).where(TicketType.event_id == event.id)).all()
sold = sum(t.quantity_sold for t in tiers)
total = sum(t.quantity_total for t in tiers)
revenue = db.scalar(
select(func.coalesce(func.sum(Order.total_paise), 0)).where(
Order.event_id == event.id,
Order.status == OrderStatus.paid,
)
) or 0
attendance = db.scalar(
select(func.count(Ticket.id)).where(
Ticket.event_id == event.id,
Ticket.status == TicketStatus.used,
)
) or 0
rows.append(
EventStatsRow(
event_id=event.id,
event_title=event.title,
starts_at=event.starts_at,
tickets_sold=sold,
tickets_remaining=max(total - sold, 0),
revenue_paise=int(revenue),
attendance=int(attendance),
)
)
return rows

View file

@ -0,0 +1,40 @@
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.core.errors import AppError
from app.models.enums import OrderStatus
from app.models.orders import Order
def list_orders_admin(db: Session, status: OrderStatus | None = None) -> list[Order]:
query = select(Order).order_by(Order.created_at.desc())
if status is not None:
query = query.where(Order.status == status)
return list(db.scalars(query).all())
def get_order_admin(db: Session, order_id: int) -> Order:
order = db.get(Order, order_id)
if not order:
raise AppError("Order not found", status_code=404)
return order
def list_payments_admin(db: Session) -> list[dict]:
orders = db.scalars(
select(Order)
.where(Order.status.in_([OrderStatus.paid, OrderStatus.pending, OrderStatus.failed]))
.order_by(Order.created_at.desc())
).all()
return [
{
"order_id": order.id,
"event_id": order.event_id,
"status": order.status,
"total_paise": order.total_paise,
"provider_payment_id": order.provider_payment_id,
"payment_reference": order.payment_reference,
"paid_at": order.paid_at,
}
for order in orders
]