Compare commits

...

6 Commits

Author SHA1 Message Date
Dean Wenstrand
f40c223dfc Add getUsageStats operation + hook
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 10:30:13 -07:00
Dean Wenstrand
4758c95149 Add getUsageStats admin query types
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 10:29:07 -07:00
905a1ca6db Merge pull request 'Add RGBColorGQL and identifierToColor to TableStateGQL' (#290) from shot-lab-ball-colors into master 2026-07-10 03:36:33 +00:00
6f63189703 Add RGBColorGQL and identifierToColor to TableStateGQL
All checks were successful
Tests / Tests (pull_request) Successful in 12s
Regenerated from backend: Shot Lab table-state endpoints now expose a
per-ball dominant color (RGB + hex) index-aligned with ball positions.
2026-07-09 20:35:48 -07:00
958057764c Merge pull request 'Add getLiveTableState query and live-stream operations' (#288) from live-table-state into master 2026-07-10 02:12:31 +00:00
a150e1a300 Add getLiveTableState query and live-stream operations
All checks were successful
Tests / Tests (pull_request) Successful in 10s
- schema: getLiveTableState(videoId) returning LiveTableStateGQL
  (videoId, frameIndex, tableState)
- operations: GetLiveTableState in shot_simulation.gql, lightweight
  GetLiveStreams listing in live_streams.gql
- regenerated TS client outputs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 01:15:33 -07:00
5 changed files with 5525 additions and 14051 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
# Lightweight listing of live (in-progress) streams for pickers like the
# Shot Lab "follow a live stream" flow. Selects only what a picker card
# needs; intentionally avoids the heavy VideoCardFields fragment.
query GetLiveStreams($limit: Int! = 25) {
getFeedVideos(
limit: $limit
filters: { isStreamCompleted: false, requireCursorCompletion: false }
includePrivate: MINE
feedInput: { allUsers: true }
) {
videos {
id
name
startTime
createdAt
screenshotUri
elapsedTime
owner {
id
username
profileImageUri
}
stream {
id
isCompleted
lastSegmentUploadedAt
}
}
pageInfo {
hasNextPage
endCursor
}
}
}

View File

@@ -12,6 +12,19 @@ query GetTableState(
} }
} }
query GetLiveTableState($videoId: Int!) {
getLiveTableState(videoId: $videoId) {
videoId
frameIndex
tableState {
identifierToPosition
homography {
...HomographyInfo
}
}
}
}
query SimulateShot($simulationInput: SimulateShotInputGQL!) { query SimulateShot($simulationInput: SimulateShotInputGQL!) {
simulateShot(simulationInput: $simulationInput) { simulateShot(simulationInput: $simulationInput) {
trajectories { trajectories {

View File

@@ -0,0 +1,26 @@
query getUsageStats($days: Int! = 30) {
getUsageStats(days: $days) {
totalUsers
totalVideos
totalShots
totalRuns
windows {
label
newUsers
activeUsers
sessions
shots
}
daily {
day
newUsers
activeUsers
sessions
shots
}
processingLast24h {
status
count
}
}
}

View File

@@ -72,6 +72,7 @@ type Query {
tableSize: Float = 100 tableSize: Float = 100
useHomography: HomographyInputGQL = null useHomography: HomographyInputGQL = null
): TableStateGQL! ): TableStateGQL!
getLiveTableState(videoId: Int!): LiveTableStateGQL!
simulateShot(simulationInput: SimulateShotInputGQL!): ShotProjectionGQL! simulateShot(simulationInput: SimulateShotInputGQL!): ShotProjectionGQL!
computePotAim( computePotAim(
simulationInput: SimulateShotInputGQL! simulationInput: SimulateShotInputGQL!
@@ -99,6 +100,7 @@ type Query {
countRespectsLimit: Boolean! = false countRespectsLimit: Boolean! = false
): [ShotGQL!]! ): [ShotGQL!]!
getShotsByIds(ids: [Int!]!): [ShotGQL!]! getShotsByIds(ids: [Int!]!): [ShotGQL!]!
getUsageStats(days: Int! = 30): UsageStatsGQL!
getUser(userId: Int!): UserGQL getUser(userId: Int!): UserGQL
doesUsernameExist(candidateUsername: String!): Boolean! doesUsernameExist(candidateUsername: String!): Boolean!
getLoggedInUser: UserGQL getLoggedInUser: UserGQL
@@ -1019,6 +1021,14 @@ type PlayerClusterShotGQL {
type TableStateGQL { type TableStateGQL {
identifierToPosition: [[Float!]!]! identifierToPosition: [[Float!]!]!
homography: HomographyInfoGQL homography: HomographyInfoGQL
identifierToColor: [RGBColorGQL]!
}
type RGBColorGQL {
r: Int!
g: Int!
b: Int!
hex: String!
} }
input HomographyInputGQL { input HomographyInputGQL {
@@ -1049,6 +1059,12 @@ input IntPoint2DInput {
y: Int! y: Int!
} }
type LiveTableStateGQL {
videoId: Int!
frameIndex: Int!
tableState: TableStateGQL!
}
type ShotProjectionGQL { type ShotProjectionGQL {
trajectories: [BallTrajectoryGQL!]! trajectories: [BallTrajectoryGQL!]!
events: [SimulationEventGQL!]! events: [SimulationEventGQL!]!
@@ -1153,6 +1169,37 @@ input CreatedAfter @oneOf {
createdAt: DateTime createdAt: DateTime
} }
type UsageStatsGQL {
totalUsers: Int!
totalVideos: Int!
totalShots: Int!
totalRuns: Int!
windows: [UsageStatsWindowGQL!]!
daily: [UsageStatsDailyGQL!]!
processingLast24h: [ProcessingStatusCountGQL!]!
}
type UsageStatsWindowGQL {
label: String!
newUsers: Int!
activeUsers: Int!
sessions: Int!
shots: Int!
}
type UsageStatsDailyGQL {
day: Date!
newUsers: Int!
activeUsers: Int!
sessions: Int!
shots: Int!
}
type ProcessingStatusCountGQL {
status: String!
count: Int!
}
type UserRelationshipsResult { type UserRelationshipsResult {
inquiringUser: UserGQL! inquiringUser: UserGQL!
relationships: [UserRelationship!]! relationships: [UserRelationship!]!