mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-17 23:09:35 +00:00
- Add public_launch ACA package (gate, pilot_tracker, pdpl, brand_moat) - Register GET/POST /api/v1/public-launch/* router in api/main - Add 18 unit tests, MASTER_STRATEGIC_PLAN, PUBLIC_LAUNCH_READINESS - Refresh POST_MERGE_VERIFICATION snapshot; vendor patch for git apply Verified: 797 passed (6 skipped), ROUTE_CHECK_OK, SMOKE_INPROCESS_OK
177 lines
6.2 KiB
Python
177 lines
6.2 KiB
Python
"""Pilot Tracker — track 5–10 active pilots toward Public Launch gate.
|
||
|
||
Tracks each Pilot's lifecycle:
|
||
intake → diagnostic_24h → pilot_48h → proof_pack_7d → upgrade_decision
|
||
|
||
Used by the Public Launch Gate to count `pilots_completed` and
|
||
`paid_customers`. Deterministic — no I/O, no LLM.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass, asdict, field
|
||
from datetime import datetime, timezone
|
||
from typing import Any, Mapping, Sequence
|
||
|
||
|
||
PILOT_STAGES = (
|
||
"intake", # T+0
|
||
"diagnostic_sent", # T+24h
|
||
"pilot_delivered", # T+48h
|
||
"proof_pack_sent", # T+7d
|
||
"upgrade_decided", # T+14d
|
||
"completed", # marked done
|
||
"stalled", # SLA breach
|
||
"lost", # no upgrade, no case study
|
||
)
|
||
|
||
UPGRADE_OUTCOMES = (
|
||
"growth_os_monthly", # 2,999/mo subscription
|
||
"partnership_growth", # 3,000–7,500 SAR
|
||
"case_study", # free in exchange for case study
|
||
"second_pilot", # paid 499 again, different angle
|
||
"no_upgrade", # explicit decline
|
||
"ghost", # no response
|
||
)
|
||
|
||
|
||
@dataclass
|
||
class PilotRecord:
|
||
"""A single Pilot's data."""
|
||
pilot_id: str
|
||
company: str
|
||
sector: str
|
||
city: str
|
||
started_at: str # ISO date
|
||
stage: str
|
||
paid: bool # True if 499 SAR received OR signed Growth OS
|
||
pilot_price_sar: int
|
||
proof_pack_sent: bool
|
||
proof_pack_sent_at: str | None
|
||
upgrade_outcome: str | None
|
||
upgrade_value_sar: int = 0
|
||
notes: str = ""
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
return asdict(self)
|
||
|
||
|
||
@dataclass
|
||
class PilotSummary:
|
||
"""Aggregate view across all pilots."""
|
||
total_pilots: int
|
||
completed_pilots: int # reached upgrade_decided or completed
|
||
proof_packs_delivered: int
|
||
paid_pilots: int
|
||
paid_revenue_sar: int # sum of pilot prices that were paid
|
||
upgrade_revenue_sar: int # sum of upgrade_value_sar
|
||
case_studies: int # upgrade_outcome == "case_study"
|
||
growth_os_subscribers: int # upgrade_outcome == "growth_os_monthly"
|
||
stalled_pilots: int
|
||
lost_pilots: int
|
||
completion_rate: float # completed / total
|
||
paid_conversion_rate: float # paid / total
|
||
upgrade_conversion_rate: float # any upgrade / completed
|
||
by_sector: dict[str, int]
|
||
by_city: dict[str, int]
|
||
average_proof_pack_days: float
|
||
summary_ar: str
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
return asdict(self)
|
||
|
||
|
||
def _parse_iso(s: str) -> datetime | None:
|
||
if not s:
|
||
return None
|
||
try:
|
||
return datetime.fromisoformat(s.replace("Z", "+00:00"))
|
||
except ValueError:
|
||
return None
|
||
|
||
|
||
def pilot_tracker_summary(pilots: Sequence[PilotRecord | Mapping[str, Any]]) -> PilotSummary:
|
||
"""Compute aggregate summary across all pilots.
|
||
|
||
Accepts either PilotRecord instances or plain dicts (for API ingestion).
|
||
"""
|
||
records: list[PilotRecord] = []
|
||
for p in pilots:
|
||
if isinstance(p, PilotRecord):
|
||
records.append(p)
|
||
else:
|
||
records.append(PilotRecord(
|
||
pilot_id=str(p.get("pilot_id", "")),
|
||
company=str(p.get("company", "")),
|
||
sector=str(p.get("sector", "unknown")),
|
||
city=str(p.get("city", "unknown")),
|
||
started_at=str(p.get("started_at", "")),
|
||
stage=str(p.get("stage", "intake")),
|
||
paid=bool(p.get("paid", False)),
|
||
pilot_price_sar=int(p.get("pilot_price_sar", 499)),
|
||
proof_pack_sent=bool(p.get("proof_pack_sent", False)),
|
||
proof_pack_sent_at=p.get("proof_pack_sent_at"),
|
||
upgrade_outcome=p.get("upgrade_outcome"),
|
||
upgrade_value_sar=int(p.get("upgrade_value_sar", 0)),
|
||
notes=str(p.get("notes", "")),
|
||
))
|
||
|
||
total = len(records)
|
||
completed_stages = {"upgrade_decided", "completed"}
|
||
completed = sum(1 for r in records if r.stage in completed_stages)
|
||
proof_count = sum(1 for r in records if r.proof_pack_sent)
|
||
paid_count = sum(1 for r in records if r.paid)
|
||
paid_revenue = sum(r.pilot_price_sar for r in records if r.paid)
|
||
upgrade_revenue = sum(r.upgrade_value_sar for r in records)
|
||
case_studies = sum(1 for r in records if r.upgrade_outcome == "case_study")
|
||
gros_subs = sum(1 for r in records if r.upgrade_outcome == "growth_os_monthly")
|
||
stalled = sum(1 for r in records if r.stage == "stalled")
|
||
lost = sum(1 for r in records if r.stage == "lost")
|
||
|
||
by_sector: dict[str, int] = {}
|
||
by_city: dict[str, int] = {}
|
||
for r in records:
|
||
by_sector[r.sector] = by_sector.get(r.sector, 0) + 1
|
||
by_city[r.city] = by_city.get(r.city, 0) + 1
|
||
|
||
proof_durations: list[float] = []
|
||
for r in records:
|
||
if r.proof_pack_sent and r.proof_pack_sent_at and r.started_at:
|
||
start = _parse_iso(r.started_at)
|
||
done = _parse_iso(r.proof_pack_sent_at)
|
||
if start and done:
|
||
proof_durations.append((done - start).total_seconds() / 86400)
|
||
avg_proof_days = (
|
||
round(sum(proof_durations) / len(proof_durations), 2)
|
||
if proof_durations else 0.0
|
||
)
|
||
|
||
summary_ar = (
|
||
f"{total} Pilots إجمالاً، {completed} مكتمل، "
|
||
f"{paid_count} مدفوع، {proof_count} Proof Packs، "
|
||
f"{gros_subs} Growth OS، {case_studies} case studies."
|
||
)
|
||
|
||
return PilotSummary(
|
||
total_pilots=total,
|
||
completed_pilots=completed,
|
||
proof_packs_delivered=proof_count,
|
||
paid_pilots=paid_count,
|
||
paid_revenue_sar=paid_revenue,
|
||
upgrade_revenue_sar=upgrade_revenue,
|
||
case_studies=case_studies,
|
||
growth_os_subscribers=gros_subs,
|
||
stalled_pilots=stalled,
|
||
lost_pilots=lost,
|
||
completion_rate=round(completed / total, 3) if total else 0.0,
|
||
paid_conversion_rate=round(paid_count / total, 3) if total else 0.0,
|
||
upgrade_conversion_rate=(
|
||
round((gros_subs + case_studies) / completed, 3)
|
||
if completed else 0.0
|
||
),
|
||
by_sector=by_sector,
|
||
by_city=by_city,
|
||
average_proof_pack_days=avg_proof_days,
|
||
summary_ar=summary_ar,
|
||
)
|