"""Efficiency profiles and tool efficiency endpoints.""" from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.models.feedback import EfficiencyProfile, EfficiencyRate, ToolEfficiency, ToolEfficiencyRate router = APIRouter() # Preset profiles seeded on first access PRESET_PROFILES = { "Conservative": { "Account Management": 5, "Delivery": 10, "Strategy": 5, "Creative": 15, "Editorial": 20, "Production": 25, "Web Design": 20, "Data": 10, "UX": 10, "Tech and Web Dev": 15, "QA": 20, "Social & Community Management": 15, "Third Party Fees": 0, }, "Moderate": { "Account Management": 10, "Delivery": 20, "Strategy": 15, "Creative": 35, "Editorial": 40, "Production": 50, "Web Design": 40, "Data": 25, "UX": 25, "Tech and Web Dev": 35, "QA": 45, "Social & Community Management": 30, "Third Party Fees": 0, }, "Aggressive": { "Account Management": 15, "Delivery": 30, "Strategy": 25, "Creative": 60, "Editorial": 70, "Production": 80, "Web Design": 65, "Data": 40, "UX": 45, "Tech and Web Dev": 60, "QA": 75, "Social & Community Management": 50, "Third Party Fees": 0, }, } PRESET_TOOLS = { "Pencil": { "description": "AI-powered ad creative generation and concepting", "rates": {"Creative": 15, "Production": 10, "Delivery": 5}, }, "OMG": { "description": "Media optimization and campaign management platform", "rates": {"Delivery": 10, "Data": 15, "Strategy": 5}, }, "Creative X": { "description": "Scalable asset generation and adaptation platform", "rates": {"Creative": 20, "Production": 15, "Editorial": 10}, }, "Semblance": { "description": "Audience analysis and model insights", "rates": {"Data": 15, "Strategy": 10}, }, "Share of Model": { "description": "Media modeling and optimization engine", "rates": {"Data": 15, "Strategy": 10, "Delivery": 5}, }, } async def _ensure_presets(db: AsyncSession): """Seed preset profiles and tools if they don't exist.""" existing = await db.execute(select(EfficiencyProfile)) if existing.scalars().first(): return for name, rates in PRESET_PROFILES.items(): profile = EfficiencyProfile(name=name, is_default=(name == "Moderate")) db.add(profile) await db.flush() for discipline, pct in rates.items(): db.add(EfficiencyRate(profile_id=profile.id, discipline=discipline, efficiency_pct=pct)) for tool_name, tool_data in PRESET_TOOLS.items(): tool = ToolEfficiency(tool_name=tool_name, tool_description=tool_data["description"]) db.add(tool) await db.flush() for discipline, pct in tool_data["rates"].items(): db.add(ToolEfficiencyRate(tool_id=tool.id, discipline=discipline, additional_efficiency_pct=pct)) await db.commit() @router.get("/profiles") async def list_profiles(db: AsyncSession = Depends(get_db)): await _ensure_presets(db) result = await db.execute(select(EfficiencyProfile).order_by(EfficiencyProfile.id)) profiles = result.scalars().all() out = [] for p in profiles: rates_result = await db.execute( select(EfficiencyRate).where(EfficiencyRate.profile_id == p.id) ) rates = {r.discipline: float(r.efficiency_pct) for r in rates_result.scalars().all()} out.append({ "id": p.id, "name": p.name, "is_default": p.is_default, "rates": rates, }) return out @router.get("/profiles/{profile_id}") async def get_profile(profile_id: int, db: AsyncSession = Depends(get_db)): result = await db.execute(select(EfficiencyProfile).where(EfficiencyProfile.id == profile_id)) profile = result.scalar_one_or_none() if not profile: raise HTTPException(status_code=404, detail="Profile not found") rates_result = await db.execute( select(EfficiencyRate).where(EfficiencyRate.profile_id == profile.id) ) rates = {r.discipline: float(r.efficiency_pct) for r in rates_result.scalars().all()} return {"id": profile.id, "name": profile.name, "is_default": profile.is_default, "rates": rates} @router.put("/profiles/{profile_id}") async def update_profile(profile_id: int, data: dict, db: AsyncSession = Depends(get_db)): """Update rates for a profile. Body: {"rates": {"Creative": 40, "Production": 55, ...}}""" result = await db.execute(select(EfficiencyProfile).where(EfficiencyProfile.id == profile_id)) profile = result.scalar_one_or_none() if not profile: raise HTTPException(status_code=404, detail="Profile not found") if "rates" in data: # Delete existing rates and re-create existing = await db.execute(select(EfficiencyRate).where(EfficiencyRate.profile_id == profile.id)) for r in existing.scalars().all(): await db.delete(r) for discipline, pct in data["rates"].items(): db.add(EfficiencyRate(profile_id=profile.id, discipline=discipline, efficiency_pct=pct)) if "name" in data: profile.name = data["name"] await db.commit() return await get_profile(profile_id, db) @router.post("/profiles") async def create_profile(data: dict, db: AsyncSession = Depends(get_db)): """Create a custom profile. Body: {"name": "My Profile", "rates": {...}}""" profile = EfficiencyProfile(name=data["name"]) db.add(profile) await db.flush() for discipline, pct in data.get("rates", {}).items(): db.add(EfficiencyRate(profile_id=profile.id, discipline=discipline, efficiency_pct=pct)) await db.commit() return await get_profile(profile.id, db) @router.get("/tools") async def list_tools(db: AsyncSession = Depends(get_db)): await _ensure_presets(db) result = await db.execute(select(ToolEfficiency).order_by(ToolEfficiency.tool_name)) tools = result.scalars().all() out = [] for t in tools: rates_result = await db.execute( select(ToolEfficiencyRate).where(ToolEfficiencyRate.tool_id == t.id) ) rates = {r.discipline: float(r.additional_efficiency_pct) for r in rates_result.scalars().all()} out.append({ "id": t.id, "tool_name": t.tool_name, "tool_description": t.tool_description, "rates": rates, }) return out