| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- """Pydantic v2 request/response models for the 拼团 (group-buy) funnel query API.
- Field names match the API contract in docs/02 §5 (v3) exactly:
- request: period, snapshot_dt (optional)
- response: period, snapshot_dt,
- results[].step_index, results[].name, results[].event_key,
- results[].uv, results[].conversion_rate, results[].dropoff_rate,
- data_status
- """
- from __future__ import annotations
- from datetime import date
- from enum import Enum
- from pydantic import BaseModel, Field
- class Period(str, Enum):
- """Supported periods (docs/02 §5 v3).
- Routing:
- day -> table ads_trd_group_funnel_daily (single day, full history).
- last_7d -> table ads_trd_group_funnel_rolling, columns uv_*_7d.
- last_30d -> table ads_trd_group_funnel_rolling, columns uv_*_30d.
- Any other value is rejected with HTTP 422.
- """
- day = "day"
- last_7d = "last_7d"
- last_30d = "last_30d"
- class FunnelQueryRequest(BaseModel):
- """Funnel query request body.
- ``snapshot_dt`` (ISO ``YYYY-MM-DD``) is optional and only meaningful for
- ``period=day``: omitted -> latest daily row; given -> that historical day
- (must be <= yesterday). For ``last_7d`` / ``last_30d`` it is ignored.
- """
- period: Period
- snapshot_dt: date | None = Field(
- None,
- description=(
- "Optional ISO date (YYYY-MM-DD). Only meaningful for period=day; "
- "ignored for last_7d/last_30d. Must be <= yesterday."
- ),
- )
- class DataStatus(str, Enum):
- """Data completeness status for a query result (docs/02 §5 v3).
- ready - the target row exists and the period's columns are non-null.
- missing - no target row, or the period columns are NULL. Never zero-filled.
- """
- ready = "ready"
- missing = "missing"
- class FunnelStepResult(BaseModel):
- """Computed UV and conversion metrics for one fixed step."""
- step_index: int = Field(..., ge=1, description="1-based step index")
- name: str = Field(..., description="Chinese display name of the step")
- event_key: str = Field(..., description="Stable step key (start/show/...)")
- uv: int = Field(..., ge=0)
- conversion_rate: float | None = Field(
- None, description="uv[i] / uv[i-1]; null for step 1 or when uv[i-1] == 0"
- )
- dropoff_rate: float | None = Field(
- None, description="1 - conversion_rate; null when conversion_rate is null"
- )
- class FunnelQueryResponse(BaseModel):
- """Funnel query response body."""
- period: Period
- snapshot_dt: str | None = Field(
- None,
- description=(
- "dt (yyyyMMdd) of the row actually used; for day = that day, for "
- "7d/30d = the rolling row's as-of dt. null when missing."
- ),
- )
- results: list[FunnelStepResult]
- data_status: DataStatus
- # --------------------------------------------------------------------------- #
- # Trend (按日折线) — daily time series over ads_trd_group_funnel_daily #
- # --------------------------------------------------------------------------- #
- class FunnelTrendRequest(BaseModel):
- """Trend query request body.
- Both bounds optional (ISO ``YYYY-MM-DD``). Omitted -> the 30 most recent
- available days ending at the latest daily ``dt``. ``end_dt`` must be <=
- yesterday (T+1); ``start_dt`` must be <= ``end_dt``.
- """
- start_dt: date | None = Field(
- None, description="Inclusive start (YYYY-MM-DD). Default: end - 29 days."
- )
- end_dt: date | None = Field(
- None,
- description="Inclusive end (YYYY-MM-DD). Default: latest available day. <= yesterday.",
- )
- class FunnelTrendPoint(BaseModel):
- """One day in the trend series: its ``dt`` + the full per-step funnel.
- Reuses :class:`FunnelStepResult` so uv + conversion_rate match the funnel
- query口径 exactly (computed by the same server code).
- """
- dt: str = Field(..., description="Day as yyyyMMdd")
- results: list[FunnelStepResult]
- class FunnelTrendResponse(BaseModel):
- """Trend query response body.
- ``points`` is ascending by ``dt``, with leading all-zero (pre-launch) days
- trimmed. ``start_dt`` / ``end_dt`` are the bounds of the returned series
- (yyyyMMdd) — i.e. the first kept day and the last available day — null when
- ``missing``. Calendar gaps inside the range are simply absent points (the
- client breaks the line; no zero-fill).
- """
- start_dt: str | None = Field(None, description="First kept day (yyyyMMdd); null when missing.")
- end_dt: str | None = Field(None, description="Last day (yyyyMMdd); null when missing.")
- data_status: DataStatus
- points: list[FunnelTrendPoint]
|