Expand dashboard analytics and document production implementation
This commit is contained in:
70
mockups/prompt_metrics.py
Normal file
70
mockups/prompt_metrics.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""Lightweight product-prompt measurements, separate from activity sessions."""
|
||||
def prompt_analytics(conn,gid,days,source='all'):
|
||||
if source not in ('all','game','player'):
|
||||
from sample_api import ApiError
|
||||
raise ApiError(400,'Unknown prompt source.')
|
||||
products=conn.execute('''SELECT p.id,p.name,p.roblox_product_id,count(i.id)::int prompts,
|
||||
count(i.id) FILTER(WHERE i.outcome='bought')::int bought,
|
||||
count(i.id) FILTER(WHERE i.outcome='closed')::int closed,
|
||||
count(i.id) FILTER(WHERE i.outcome IS NULL)::int unresolved,
|
||||
avg(extract(epoch FROM i.ended_at-i.started_at)) FILTER(WHERE i.outcome='bought')::double precision buy_time,
|
||||
avg(extract(epoch FROM i.ended_at-i.started_at)) FILTER(WHERE i.outcome='closed')::double precision close_time
|
||||
FROM products p LEFT JOIN interaction_events i ON i.game_id=p.game_id AND i.product_id=p.id AND i.kind='product_prompt'
|
||||
AND i.outcome IN ('bought','closed') AND i.ended_at IS NOT NULL AND i.trigger_source IN ('game','player')
|
||||
AND i.started_at>=current_date-(%s-1) AND (%s='all' OR i.trigger_source=%s)
|
||||
WHERE p.game_id=%s GROUP BY p.id ORDER BY p.id''',(days,source,source,gid)).fetchall()
|
||||
for row in products: row['conversion']=100*row['bought']/row['prompts'] if row['prompts'] else None
|
||||
daily=conn.execute('''SELECT (started_at AT TIME ZONE 'UTC')::date AS day,product_id,count(*)::int prompts,
|
||||
count(*) FILTER(WHERE outcome='bought')::int bought,
|
||||
avg(extract(epoch FROM ended_at-started_at)) FILTER(WHERE outcome='bought')::double precision buy_time,
|
||||
avg(extract(epoch FROM ended_at-started_at)) FILTER(WHERE outcome='closed')::double precision close_time
|
||||
FROM interaction_events WHERE game_id=%s AND kind='product_prompt'
|
||||
AND outcome IN ('bought','closed') AND ended_at IS NOT NULL AND trigger_source IN ('game','player') AND started_at>=current_date-(%s-1)
|
||||
AND (%s='all' OR trigger_source=%s) GROUP BY day,product_id ORDER BY day''',(gid,days,source,source)).fetchall()
|
||||
totals=conn.execute('''SELECT count(*)::int prompts,count(*) FILTER(WHERE outcome='bought')::int bought,
|
||||
avg(extract(epoch FROM ended_at-started_at)) FILTER(WHERE outcome='bought')::double precision buy_time,
|
||||
avg(extract(epoch FROM ended_at-started_at)) FILTER(WHERE outcome='closed')::double precision close_time
|
||||
FROM interaction_events WHERE game_id=%s AND kind='product_prompt'
|
||||
AND outcome IN ('bought','closed') AND ended_at IS NOT NULL AND trigger_source IN ('game','player') AND started_at>=current_date-(%s-1)
|
||||
AND (%s='all' OR trigger_source=%s)''',(gid,days,source,source)).fetchone()
|
||||
totals['conversion']=100*totals['bought']/totals['prompts'] if totals['prompts'] else None
|
||||
from datetime import datetime,timedelta,timezone
|
||||
dates=[datetime.now(timezone.utc).date()-timedelta(days=i) for i in reversed(range(days))]
|
||||
lookup={(r['product_id'],r['day']):r for r in daily}
|
||||
series=[]
|
||||
for p in products:
|
||||
values=[]
|
||||
for day in dates:
|
||||
row=lookup.get((p['id'],day))
|
||||
values.append(100*row['bought']/row['prompts'] if row and row['prompts'] else None)
|
||||
series.append({'name':p['name'],'format':'percent','values':values})
|
||||
grouped=conn.execute('''SELECT trigger_source source,count(*)::int prompts,
|
||||
count(*) FILTER(WHERE outcome='bought')::int bought,count(*) FILTER(WHERE outcome='closed')::int closed,
|
||||
count(*) FILTER(WHERE outcome IS NULL)::int unresolved,
|
||||
avg(extract(epoch FROM ended_at-started_at)) FILTER(WHERE outcome='bought')::double precision buy_time,
|
||||
avg(extract(epoch FROM ended_at-started_at)) FILTER(WHERE outcome='closed')::double precision close_time
|
||||
FROM interaction_events WHERE game_id=%s AND kind='product_prompt'
|
||||
AND outcome IN ('bought','closed') AND ended_at IS NOT NULL AND trigger_source IN ('game','player') AND started_at>=current_date-(%s-1)
|
||||
GROUP BY trigger_source''',(gid,days)).fetchall()
|
||||
labels={'game':'Game-induced','player':'Player-induced'}
|
||||
by_source={r['source']:r for r in grouped}
|
||||
comparison=[]
|
||||
for key,name in labels.items():
|
||||
row=by_source.get(key,{'source':key,'prompts':0,'bought':0,'closed':0,'unresolved':0,'buy_time':None,'close_time':None})
|
||||
row['name']=name;row['conversion']=100*row['bought']/row['prompts'] if row['prompts'] else None
|
||||
comparison.append(row)
|
||||
source_days=conn.execute('''SELECT (started_at AT TIME ZONE 'UTC')::date AS day,trigger_source source,
|
||||
count(*)::int prompts,count(*) FILTER(WHERE outcome='bought')::int bought
|
||||
FROM interaction_events WHERE game_id=%s AND kind='product_prompt'
|
||||
AND outcome IN ('bought','closed') AND ended_at IS NOT NULL AND trigger_source IN ('game','player') AND started_at>=current_date-(%s-1)
|
||||
GROUP BY day,trigger_source''',(gid,days)).fetchall()
|
||||
source_lookup={(r['source'],r['day']):r for r in source_days}
|
||||
source_series=[]
|
||||
for key,name in labels.items():
|
||||
values=[]
|
||||
for day in dates:
|
||||
row=source_lookup.get((key,day))
|
||||
values.append(100*row['bought']/row['prompts'] if row and row['prompts'] else None)
|
||||
source_series.append({'name':name,'format':'percent','values':values})
|
||||
return {'metrics':totals,'products':products,'source':source,'sourceComparison':comparison,
|
||||
'sourceSeries':source_series,'series':series,'labels':[d.strftime('%d %b') for d in dates]}
|
||||
Reference in New Issue
Block a user