Add PostgreSQL analytics dashboard prototype and realistic sample data
This commit is contained in:
189
PROJECT.md
Normal file
189
PROJECT.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Roblox Game Analytics Dashboard
|
||||
|
||||
## Project overview
|
||||
|
||||
Build a custom analytics data logger and web dashboard for Roblox games. Roblox experiences send event data to the service over HTTP requests; game owners use the web frontend to inspect activity, retention, playtime, and other metrics. The system must also support game-defined custom datasets so developers can record and analyze data specific to their experience.
|
||||
|
||||
The service is intended for the owner's team and shared games, rather than public self-service signups. The first implementation will use Lua with Lapis and run on a self-hosted VPS. The web frontend will use React and TypeScript, with Apache ECharts for graphs rendered directly in the dashboard.
|
||||
|
||||
## Goals
|
||||
|
||||
- Provide a reliable, documented API for Roblox experiences to submit analytics events.
|
||||
- Track player sessions, retention, playtime, and other common game metrics.
|
||||
- Let each game define and send custom datasets/events without requiring a backend code change for every new metric.
|
||||
- Provide a web frontend to explore metrics over time and inspect relevant underlying records.
|
||||
- Store analytics data in PostgreSQL and support multiple games/projects with clear data isolation.
|
||||
- Track engagement and monetization with documented calculations and chart-based views, including individual reported product sales.
|
||||
|
||||
## Core concepts and data
|
||||
|
||||
- **Project/game:** A registered game rooted at its internal database `games.id`. Every dataset references this internal `game_id`; the Roblox experience ID is an optional child property of the game record and is never used as the dataset root.
|
||||
- **Player:** A Roblox user identified by their Roblox user ID within a project. Avoid collecting personal information that is not needed for analytics.
|
||||
- **Player snapshot:** A timestamped copy of the player stats reported by the game, such as cash, level, cumulative playtime, session count, and game-reported Robux spending. Keep the latest values available for sorting and retain snapshot history for trends and progress analysis.
|
||||
- **Session:** A play session with a start time, end time or heartbeat/last-seen time, duration, and optional context such as place or server.
|
||||
- **Activity run:** A user-defined interval inside a game session, such as a shop visit or minigame run, paired from configured start and end events. It can include ordered steps, prompts, outcomes, and captured custom values. Store each run with its player ID for lookup in that player's profile, while the activity analytics page reports aggregates such as average/median duration, completion, prompt, and purchase rates across runs.
|
||||
- **Event:** A timestamped record used to calculate standard metrics or represent a custom event/dataset. Events may include validated properties supplied by the game.
|
||||
- **Metrics:** Aggregations such as active players, session counts, average playtime, retention cohorts, and custom event counts or values.
|
||||
- **Game-level data:** Categorized events and metric time samples that have no player binding, such as visits, CCU, server events, and performance reports. These belong directly to the internal game root.
|
||||
- **Product sale:** A unique receipt under a game, with product ID, actual unit price at sale time, quantity, timestamp, and player/session references when available. Store catalog metadata separately so price changes do not rewrite past sales.
|
||||
|
||||
Keep the event model modular so a variety of event types and game-defined properties can be stored without a schema change for every new event. Build common, game-independent metrics such as retention and playtime on top of this event data, with room to add further broadly useful metrics.
|
||||
|
||||
Use PostgreSQL as the source of truth. Keep project ownership and credentials relationally scoped; design event storage and indexes for time-based, per-project queries. Custom event properties should be flexible enough to evolve while retaining searchable fields and sensible validation/size limits. Define retention and aggregation strategies as data volume grows.
|
||||
|
||||
Use composite foreign keys to keep player, session, event, activity, and product references inside the same game. Keep unavailable source values null or absent; display `N/A` for missing values, retain known zeros, and leave gaps in charts instead of inventing observations.
|
||||
|
||||
## API and Roblox ingestion
|
||||
|
||||
- The Roblox integration is a ModuleScript created and maintained by the project owner. The web service provides the HTTP API and its contract; the ModuleScript is a client of that API, not a deliverable of the web backend.
|
||||
- The ModuleScript accepts a project API key and Lua tables, JSON-encodes the tables, and sends them to the HTTPS API using Roblox HTTP services (for example, `HttpService:RequestAsync`). The API should accept JSON request bodies and return documented JSON responses.
|
||||
- Provide endpoints for session start/update/end and for submitting custom events or dataset records. Prefer a small, versioned JSON API with documented request and response formats so the ModuleScript can send both standard analytics and arbitrary game-defined data.
|
||||
- Authenticate game ingestion with per-project API keys. Store keys securely, support rotation/revocation, and never expose them to dashboard clients. The key must only be used by server-side Roblox scripts; it must not be shipped to clients where players can extract it.
|
||||
- Validate project credentials, timestamps, identifiers, event names, payload size, and allowed property shapes. Apply rate limits and return clear status codes so Roblox scripts can handle failures.
|
||||
- Make ingestion safe to retry: accept an optional event/request identifier and enforce idempotency where appropriate. Do not assume requests arrive exactly once or in order.
|
||||
- Roblox requests may be delayed, duplicated, or unavailable temporarily. Document response codes and retry behavior so the ModuleScript can retry transient failures without creating duplicate sessions/events.
|
||||
- The ModuleScript will send session-start data, session-end data with session statistics, and other events when they occur. Heartbeat-based session tracking is not required by the current plan.
|
||||
- Support player-stat snapshots submitted by the game, associated with the Roblox user ID and capture time. Store a copy of each accepted snapshot so the dashboard can show current values and how those values change over time. The game controls which custom fields it reports; validate field names, types, and payload sizes.
|
||||
- Support configurable activity definitions that pair start/end events and optionally track intermediate events, outcomes, timeouts, and selected property values. Persist individual activity runs against the associated player for profile history; aggregate activity run statistics across players for analysis. The development prototype now persists seeded definitions and runs in PostgreSQL; definition editing and live event ingestion remain future work.
|
||||
- Report each trusted product sale with receipt ID, product ID, price, quantity, timestamp, and player. Deduplicate receipts within each game and derive monetization metrics from stored sale reports.
|
||||
- Whether each HTTP request contains one event or a batch of events is undecided; choose this when defining the API contract, taking Roblox request limits and retry behavior into account.
|
||||
|
||||
## Web frontend
|
||||
|
||||
- Provide a private team dashboard for shared games, with login and cookie-based sessions. Public account creation and user-managed signup are out of scope for the initial plan.
|
||||
- Save dashboard preferences such as selected project or display settings where useful. Project/game access should remain private to the team.
|
||||
- Support project selection, date/time ranges, and useful filters such as place, event name, or custom properties when available.
|
||||
- Show overview metrics, session/playtime views, retention cohorts, and custom dataset/event exploration.
|
||||
- Provide a Players area with a searchable, sortable table of each player's latest reported stats. Initial sortable values: cash, level, game-reported Robux spent, cumulative playtime, session count, and last seen. A player detail view can show snapshot history and session/event activity.
|
||||
- Render charts in the application with Apache ECharts so they share the dashboard's navigation, filters, and visual design.
|
||||
- Have the API return aggregated, time-bucketed results for charts instead of sending large volumes of raw events to the browser for aggregation.
|
||||
- Make metric definitions and timezone/date-range behavior clear. Avoid presenting incomplete or delayed ingestion as exact real-time data.
|
||||
- Keep all project data isolated in both API authorization and database queries.
|
||||
|
||||
## Website pages and navigation
|
||||
|
||||
Use the supplied analytics dashboard screenshots as visual inspiration: a compact dark interface, persistent navigation, clear filters, and charts that emphasize trends. Build the product's own layout and visual identity rather than copying another dashboard.
|
||||
|
||||
### App shell
|
||||
|
||||
- **Desktop:** A fixed, collapsible left sidebar with the product mark, project selector, and primary navigation. The main area has a slim top bar with breadcrumbs/page title, date range and timezone controls where relevant, refresh status, and account/session menu. Keep filters close to the charts they affect.
|
||||
- **Phone:** Use a compact top bar for the current project and page title, a bottom navigation bar for the four main destinations (Overview, Analytics, Events, More), and a drawer or sheet for secondary pages and filters. Stack metric cards and charts vertically; allow wide charts and data tables to scroll horizontally or switch to a compact list. Keep date selection and key filters usable without desktop hover behavior.
|
||||
- **Visual style:** Dark mode by default, with layered charcoal surfaces, restrained borders, readable muted labels, and one bright accent color for primary actions and selected data series. Use accessible text contrast, visible focus states, and distinguish chart series with more than color alone. Consider a light theme later, but do not block the initial dark theme on it.
|
||||
- **Shared controls:** Project selector, date range presets and custom range, timezone indicator, loading/empty/error states, refresh action, and clear active-filter chips. Preserve useful filters when moving between analytics views.
|
||||
|
||||
### Pages and tabs
|
||||
|
||||
1. **Login**
|
||||
- Private team sign-in; no public registration.
|
||||
- Clear invalid-login and expired-session states; provide logout from the account menu.
|
||||
|
||||
2. **Overview** (main navigation)
|
||||
- At-a-glance cards for active players, sessions, average playtime, and returning players/retention for the selected range.
|
||||
- Trend charts with comparison to the previous equivalent period when available.
|
||||
- Recent activity or notable event counts, plus ingestion freshness/status.
|
||||
- Links into Sessions, Retention, and Events with the current project and date range preserved.
|
||||
|
||||
3. **Analytics** (main navigation with tabs)
|
||||
- **Sessions tab:** Session counts, duration distribution, average/median playtime, sessions over time, and a paginated session table with player, start/end, duration, and available context.
|
||||
- **Retention tab:** Cohort retention table/heatmap and retention trend, with clearly labeled cohort dates and return windows (for example day 1 and day 7).
|
||||
- **Players tab:** Aggregate player activity and returning/new player trends. Avoid exposing more player-level detail than needed.
|
||||
- All tabs use shared date, place, and other applicable filters.
|
||||
|
||||
4. **Events** (main navigation)
|
||||
- **Explore tab:** Select an event type/name, time range, and supported property filters; view event volume over time and aggregate numeric properties.
|
||||
- **Records tab:** Paginated raw event records for debugging and inspection, with timestamp, event name, player/session identifiers when provided, and expandable JSON properties.
|
||||
- **Event catalog tab:** Discover event names and observed property keys/types for the selected project; show first/last seen and volume.
|
||||
- Clearly distinguish standard events used by built-in metrics from custom game-defined events.
|
||||
|
||||
5. **Players** (main navigation)
|
||||
- Provide column templates that choose which of the many available player fields appear in the table. Start with Overview, Economy, Progress, Engagement, and All values; the All values table can scroll horizontally.
|
||||
- Populate a sort-by dropdown from all available standard, game-reported, and derived player value types; sort the current player list by the selected value.
|
||||
- Search players and show their latest saved snapshot values, including cash, level, game-reported Robux spent, cumulative playtime, session count, and last seen.
|
||||
- Show snapshot freshness and allow filtering by ranges or activity where supported.
|
||||
- Save the selected column template and sort preference for the dashboard user. A player detail view can show snapshot history, progress over time, sessions, and related events.
|
||||
- Provide a player info view with all available player data, value history over a selected time window, average and total session measures, and that player's individual activity runs and events.
|
||||
- Offer player groups such as whales (by trusted spend values), top playtime, and first-time players. Make group definitions and time windows clear.
|
||||
|
||||
6. **Activity Sessions** (main navigation)
|
||||
- Let the team define custom activity journeys such as shop visits, minigame runs, and quest attempts, including start/end events, timeout, intermediate steps, prompts, outcomes, and captured values.
|
||||
- Show activity-level aggregates across runs, including average/median run time, outcome rates, prompt-to-purchase rate, and captured-value summaries.
|
||||
- Keep individual run details associated with a player and available in that player's info view; do not use the aggregate activity list as a player-by-player session list.
|
||||
- The sample backend reads seeded definitions and stored runs from PostgreSQL. Definition management and live event pairing remain future work.
|
||||
|
||||
7. **Monetization** (main navigation)
|
||||
- Charts for hourly/daily Robux spending, daily paying users, daily payer conversion, daily ARPDAU, and Robux totals by product.
|
||||
- Period cards for total reported Robux, paying users, payer conversion, average Robux per payer, products per payer, and ARPDAU (also called average Robux per daily active user).
|
||||
- List every product with its product ID, current price, reported sale revenue, paid quantity, distinct buyers, and decimal average units bought per paying user. The per-product denominator is all period paying users.
|
||||
- Inspect individual sale reports with the price captured on that receipt; do not estimate historical revenue from current product prices.
|
||||
|
||||
8. **Projects** (More on phone; sidebar on desktop)
|
||||
- List and select shared games/projects; create, rename, or archive a project.
|
||||
- Project overview with its Roblox experience identifier and data/ingestion status.
|
||||
|
||||
9. **Project Settings** (within a selected project)
|
||||
- **General tab:** Project name, Roblox experience/place identifiers, timezone/display preferences.
|
||||
- **API Keys tab:** Create, label, revoke, and rotate ingestion keys; show a new key only at creation and explain that it belongs in server-side Roblox scripts.
|
||||
- **Data tab:** Event and property validation limits, data retention policy/status, and project-specific ingestion settings as implemented.
|
||||
|
||||
10. **Account / Session** (account menu; More on phone)
|
||||
- Current login/session information and logout. Team account administration is not part of the initial scope.
|
||||
|
||||
### Initial navigation priority
|
||||
|
||||
Keep the first release focused on Login, Overview, Analytics (Sessions and Retention tabs), Players (sortable snapshots and player info), Events (Explore and Records), Activity Sessions (aggregate activity analytics), Monetization, and Project Settings (General and API Keys). Add the event catalog and further project/data controls as the underlying metrics and schema support them.
|
||||
|
||||
## Required engagement measurements
|
||||
|
||||
Record or calculate CCU, DAU, new and returning users, session count, total playtime, average playtime per active player, average session duration, new-user and returning-user session duration, and the share of known first sessions reaching five minutes. Calculate rolling 30-day MAU independently for every day and graph these measures wherever useful. Daily new/returning classification uses the player's first-seen UTC date.
|
||||
|
||||
Monetization includes hourly/daily Robux spent, sale-price totals per product, paying users, payer conversion, average Robux per paying user, average paid units per paying user (including a decimal count for each product), and ARPDAU. ARPDAU and average Robux spent per daily active user are the same measurement and should use a consistent definition.
|
||||
|
||||
Precise formulas, time windows, and missing-data behavior are documented in [mockups/METRICS.md](mockups/METRICS.md).
|
||||
|
||||
## Current development data layer
|
||||
|
||||
The HTML prototype reads a local PostgreSQL database through a temporary read-only Python API. Migrations, a repeatable seed generator, and game-scoped endpoints are in `mockups/`. The sample contains three independent games and 90 days of players, sessions, snapshots, categorized events, game metrics, activity runs, and product sales. Setup and run instructions are in [mockups/README.md](mockups/README.md). The planned production stack remains Lua/Lapis and React/TypeScript with ECharts.
|
||||
|
||||
## Initial scope
|
||||
|
||||
1. Private team login, project management, and API credential management.
|
||||
2. Secure ingestion for session lifecycle and custom events.
|
||||
3. PostgreSQL schema and queries for session, playtime, and event data.
|
||||
4. Modular event storage and a configurable analytics overview with date filtering.
|
||||
5. Player snapshots and a searchable/sortable player list, plus game-independent retention and playtime reporting and flexible custom event exploration.
|
||||
6. API documentation, including the JSON table shape, authentication header, response codes, limits, and retry/idempotency behavior needed by the owner-created ModuleScript.
|
||||
|
||||
## Design considerations
|
||||
|
||||
- Keep the ingestion path efficient; move expensive aggregations off the request path as needed.
|
||||
- Use UTC for stored timestamps and make dashboard display timezone explicit.
|
||||
- Plan for pagination, query limits, indexes, and eventual partitioning/rollups as event volume increases.
|
||||
- Treat analytics payloads as untrusted input and enforce per-project access at every read and write boundary.
|
||||
- Backend: Lua with Lapis. Frontend: React with TypeScript. Charts: Apache ECharts. Deployment target: a self-hosted VPS with PostgreSQL. Choose compatible PostgreSQL access and migration tooling as part of implementation.
|
||||
- Dashboard login should use secure cookie-based sessions. Keep team access private; settle the initial login mechanism and secret/session configuration during implementation.
|
||||
- Player snapshots are game-reported analytics data. Label Robux-spent values as game-reported; only record purchases from trusted, server-validated Roblox purchase/receipt flows, never values asserted solely by a client. Whether to retain every snapshot indefinitely or apply a configurable history window remains an implementation decision.
|
||||
|
||||
## Player metrics to consider
|
||||
|
||||
In addition to raw snapshot fields, useful sortable or trendable measures include:
|
||||
|
||||
- **Progress:** Levels gained, experience gained, quests completed, achievements unlocked, and time to reach a level or milestone.
|
||||
- **Engagement:** Days active in a selected period, days since last seen, sessions per active day, average/median session duration, and return frequency.
|
||||
- **Economy:** Current cash/balance, currency earned and spent over a period, net balance change, purchases made, and game-reported Robux spending. Keep premium purchase reporting separate from in-game currency unless the game explicitly maps them.
|
||||
- **Progress efficiency:** Levels gained per hour, currency earned per hour, or time-to-next milestone, when the game reports enough consistent data to calculate these fairly.
|
||||
- **Social and gameplay:** Friends/party participation, rounds played/won, win rate, matchmaking or mode preference, and other game-defined counters.
|
||||
|
||||
Prefer clear time windows and definitions for derived measures; for example, show whether playtime is lifetime or selected-period, and calculate rates only when the denominator is nonzero and the underlying snapshot history is available.
|
||||
## Analytics UI refinements
|
||||
|
||||
- Player value history compares up to two numeric values with independent left/right scales and hover inspection. Include custom numeric snapshot fields, cash, level, recorded Robux spending, purchases, completed playtime, session count, average/median session duration, active days, and Robux per playtime hour. Derived history is calculated as of each snapshot, never from future records.
|
||||
- Player session tables replace Place with a Join session placeholder. Only unended sessions started within six hours have an enabled button; ended or stale sessions are disabled. Real Roblox joining remains unconnected.
|
||||
- The Events per day chart has searchable event checkboxes and All/None controls. Remember selection per game; preserve event colors when filtering. The summary remains game-wide.
|
||||
|
||||
The sample database uses benchmark-informed synthetic histories documented in `mockups/SAMPLE_DATA.md`: one-session churn, declining exact-day retention, skewed session durations, rare heavy spenders, and coherent sales, snapshots, activity stages, visits, and CCU. Public reference figures guide plausible ranges; no real players' private data is imported. The bundled samples can be replaced transactionally with `manage_database.py reset-sample`, preserving internal game IDs and unrelated projects.
|
||||
|
||||
- Monetization has Revenue and Gamepasses tabs. Each gamepass shows ownership as a percentage of all tracked players and of lifetime paying players, plus reporting coverage. Ownership is stored explicitly per game/player/pass; missing checks remain unknown and affected percentages show N/A.
|
||||
- Sessions aggregates and graphs can be filtered by player category: all players, whales, top playtime, or first-time players.
|
||||
- Events shows daily counts with one graph line per event name and a summary row for each event.
|
||||
- Activity funnels show the percentage reaching each ordered stage, conversion from the previous stage, and drop-off from that stage. Missing stage observations show N/A.
|
||||
- Remove global recent-session, recent-event, and recent-sale panels, the unattributed-sales card, and game-root metric category tables from the UI. Keep the underlying records, game-level storage, and individual player history tabs.
|
||||
Reference in New Issue
Block a user