HumeraR's picture
Refactor VideoTemplateEditor component: remove AI editing features and related state management, streamline template loading, and update styles for improved layout. Enhance CSS for responsive design and visual consistency across various components, including dropdowns, sync stats, and quiz elements. Remove deprecated premium badge styles and adjust layout for chat components.
66de9c8
Raw History Blame Contribute Delete
3.74 kB
"""Monthly usage against plan limits, from usage_events."""
from datetime import UTC, datetime
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.models import Artifact, Document, UsageEvent, Workspace
from app.services.plans import Plan, effective_plan, plan_limit_error
from app.services.video_quota import video_usage
def month_start(now: datetime | None = None) -> datetime:
now = now or datetime.now(UTC)
return now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
def month_usage(db: Session, workspace_id, lifetime: bool = False) -> dict:
"""What this month has used. With `lifetime` (the Free plan) Launch Kits, infographics and audio overviews are
counted over all time instead, not counting the ones that failed (a failed one never uses up an allowance)."""
since = month_start()
def total(kind: str, unit: str) -> float:
return float(db.scalar(
select(func.coalesce(func.sum(UsageEvent.quantity), 0)).where(
UsageEvent.workspace_id == workspace_id, UsageEvent.kind == kind,
UsageEvent.unit == unit, UsageEvent.created_at >= since,
)
) or 0)
def made(type_: str, *, always_month: bool = False) -> int:
q = select(func.count()).select_from(Artifact).where(Artifact.workspace_id == workspace_id, Artifact.type == type_)
if lifetime and not always_month:
q = q.where(Artifact.status != "failed")
else:
q = q.where(Artifact.created_at >= since)
return db.scalar(q) or 0
return {
"audio_minutes": round(total("tts", "seconds") / 60, 1),
"audio_overviews": made("audio_overview"),
"launch_kits": made("launch_kit"),
"reports": made("report", always_month=True), # reports stay monthly on every plan
"infographics": made("infographic"),
"llm_tokens": int(total("llm", "tokens")),
"since": since.isoformat(),
}
def usage_report(db: Session, workspace: Workspace) -> dict:
plan = effective_plan(db, workspace)
used = month_usage(db, workspace.id, plan.lifetime)
videos = video_usage(db, workspace)
used["videos"] = videos["used"]
used["indexed_posts"] = db.scalar(
select(func.count()).select_from(Document).where(Document.workspace_id == workspace.id, Document.path.is_not(None))
) or 0
return {
"plan": plan.id,
"used": used,
"videos_resets_at": videos["resets_at"],
"limits": {
"audio_minutes": plan.audio_minutes,
"audio_overviews": plan.audio_overviews,
"videos": videos["limit"],
"launch_kits": plan.launch_kits,
"reports": plan.reports,
"infographics": plan.infographics,
"sources": plan.sources,
"indexed_posts": plan.indexed_posts,
},
}
def check_limit(db: Session, workspace: Workspace, kind: str, amount: float = 1) -> Plan:
"""Raise 402 plan_limit when this request would go over the allowance (monthly, or in total on Free)."""
plan = effective_plan(db, workspace)
used = month_usage(db, workspace.id, plan.lifetime)
limit = {"audio_minutes": plan.audio_minutes, "audio_overviews": plan.audio_overviews,
"launch_kits": plan.launch_kits, "reports": plan.reports, "infographics": plan.infographics}[kind]
if limit >= 0 and used[kind] + amount > limit:
label = kind.replace("_", " ")
total = plan.lifetime and kind != "reports" # Free's allowances are totals, its reports are monthly
raise plan_limit_error(plan, kind, f"This would go over your {limit} {label}{' in total' if total else ' this month'}.")
return plan