Files
Stat-Dashboard/mockups/player_records.py

51 lines
3.7 KiB
Python

"""Game-scoped player records with stable newest-first pages of twenty."""
import math
def record_page(conn,gid,pid,kind,params):
from sample_api import ApiError, integer
if not conn.execute('SELECT 1 FROM players WHERE game_id=%s AND id=%s',(gid,pid)).fetchone():
raise ApiError(404,'Player not found in this game.')
specs={
'snapshots':('player_snapshots r','r.id,r.captured_at,r.data','captured_at'),
'sessions':('sessions r JOIN players p ON p.game_id=r.game_id AND p.id=r.player_id',
'''r.*,p.display_name,p.roblox_user_id,extract(epoch FROM r.ended_at-r.started_at)::double precision duration,
(r.ended_at IS NULL AND r.started_at>now()-interval '6 hours' AND r.started_at<=now()) AS "joinAvailable"''','started_at'),
'activities':('activity_runs r JOIN activity_definitions d ON d.game_id=r.game_id AND d.id=r.definition_id',
'r.*,d.name,extract(epoch FROM r.ended_at-r.started_at)::double precision duration','started_at'),
'events':('events r LEFT JOIN players p ON p.game_id=r.game_id AND p.id=r.player_id','r.*,p.roblox_user_id','occurred_at'),
'purchases':('product_sales r JOIN products c ON c.game_id=r.game_id AND c.id=r.product_id JOIN players p ON p.game_id=r.game_id AND p.id=r.player_id',
'r.*,p.roblox_user_id,c.name,c.roblox_product_id,(r.unit_price_robux*r.quantity)::double precision robux','purchased_at')}
if kind not in specs: raise ApiError(404,'Unknown player record dataset.')
source,columns,time=specs[kind]
where='r.game_id=%s AND r.player_id=%s'
args=[gid,pid]
if kind=='sessions': where+=" AND (r.ended_at IS NOT NULL OR r.started_at>now()-interval '6 hours')"
if kind=='activities': where+=' AND r.ended_at IS NOT NULL AND r.outcome IS NOT NULL'
if kind=='events' and params.get('event'):
where+=' AND r.name=ANY(%s)';args.append(params['event'])
if kind=='purchases' and params.get('product'):
try:
ids=[int(v) for v in params['product']]
if any(v<=0 for v in ids): raise ValueError
except (ValueError,TypeError): raise ApiError(400,'Invalid product filter.')
where+=' AND r.product_id=ANY(%s)';args.append(ids)
total=conn.execute(f'SELECT count(*) total FROM {source} WHERE {where}',args).fetchone()['total']
pages=max(1,math.ceil(total/20))
page=min(integer(params,'page',1),pages)
records=conn.execute(f'SELECT {columns} FROM {source} WHERE {where} ORDER BY r.{time} DESC,r.id DESC LIMIT 20 OFFSET %s',args+[(page-1)*20]).fetchall()
return {'records':records,'pagination':{'page':page,'pages':pages,'pageSize':20,'total':total}}
def player_inventory(conn,gid,pid):
passes=conn.execute('''SELECT gp.id,gp.name,gp.roblox_gamepass_id,o.purchased_at FROM gamepass_ownership o
JOIN gamepasses gp ON gp.game_id=o.game_id AND gp.id=o.gamepass_id
WHERE o.game_id=%s AND o.player_id=%s AND o.is_owned ORDER BY o.purchased_at ASC NULLS LAST,gp.id''',(gid,pid)).fetchall()
products=conn.execute('''SELECT p.id,p.name,p.roblox_product_id,sum(s.quantity)::int quantity,
sum(s.quantity*s.unit_price_robux)::double precision robux,min(s.purchased_at) first_purchase,max(s.purchased_at) last_purchase
FROM product_sales s JOIN products p ON p.game_id=s.game_id AND p.id=s.product_id
WHERE s.game_id=%s AND s.player_id=%s GROUP BY p.id ORDER BY first_purchase,p.id''',(gid,pid)).fetchall()
event_names=conn.execute('SELECT DISTINCT name FROM events WHERE game_id=%s AND player_id=%s ORDER BY name',(gid,pid)).fetchall()
return {'ownedGamepasses':passes,'purchasedProducts':products,
'recordFilters':{'events':[r['name'] for r in event_names],'purchases':[{'id':r['id'],'name':r['name']} for r in products]}}