51 lines
3.7 KiB
Python
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]}}
|