Compare commits

..

18 Commits

Author SHA1 Message Date
e86dd1900f Add suggestPositionShot schema types and operation
Mirrors the backend's position-play search: SuggestPositionShotInputGQL
(pot lock + target-area polygon), PositionSuggestionGQL /
PositionShotCandidateGQL / CueStrikeGQL, the Query field, and a
SuggestPositionShot operation with a shared candidate fragment. Rebased
onto master's gql; index.tsx regenerated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:47:27 -07:00
6d9cb4126e Merge pull request 'Add per-video HomographyBackendGQL selection to VideoMetadataInput' (#298) from colonelpanic/homography-backend-selection into master 2026-07-18 02:12:18 +00:00
feeb5b7038 Add HomographyBackendGQL enum and VideoMetadataInput.homographyBackend
All checks were successful
Tests / Tests (pull_request) Successful in 11s
Regenerated from backend Python: adds the HomographyBackendGQL enum
(CLASSIC/TABLENET) and the optional homographyBackend field on
VideoMetadataInput, for per-video homography backend selection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:26:53 -07:00
3340314166 Merge pull request 'Add per-session Coach GraphQL contract' (#297) from loewy/session-coach-v1 into master
Reviewed-on: #297
2026-07-17 18:33:58 +00:00
6b8fdd4262 Add per-session Coach GraphQL contract
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-07-16 16:45:55 -07:00
bfaed33c89 Merge pull request 'feed: select uploadCompletionCursor on VideoCardFields stream' (#296) from dean/feed-upload-completion-cursor into master
Reviewed-on: #296
2026-07-14 18:24:54 +00:00
Dean Wenstrand
1ce3aaed1b feed: select uploadCompletionCursor on VideoCardFields stream
All checks were successful
Tests / Tests (pull_request) Successful in 24s
Lets the client distinguish a finalized-but-not-fully-uploaded stream
(is_completed set, but received cursor short of last_intended_segment_bound)
from one that is genuinely processing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 11:15:41 -07:00
1bd03f2bbe Merge pull request 'dean/unify-shotlab-cohorts' (#295) from dean/unify-shotlab-cohorts into master
Reviewed-on: #295
2026-07-13 01:08:48 +00:00
Dean Wenstrand
4a1c41aae1 Regenerate schema and hooks from backend master
All checks were successful
Tests / Tests (pull_request) Successful in 12s
Union of the cohort funnel, Shot Lab picker queries, and transient
homography IDs — converges the three divergent submodule pointers
(gql master, railbird master, railbird-mobile master).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 18:03:33 -07:00
Dean Wenstrand
4122284bbd Merge Shot Lab queries with cohort funnel 2026-07-12 17:59:33 -07:00
b30dbd01f5 Fetch shot paths and features for the Shot Lab picker
All checks were successful
Tests / Tests (pull_request) Successful in 11s
The recorded-shot picker now renders per-shot table diagrams, so
GetRecordedStreamShots needs serializedShotPaths plus the pocketing and
cue-object features that PlaneShotDiagram consumes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:13:17 -07:00
6d13dc3eb1 Merge remote-tracking branch 'origin/master' into colonelpanic/allow-transient-homography-ids 2026-07-12 00:19:46 -07:00
e3524cab97 Merge pull request 'Add weekly cohort funnel to getUsageStats' (#293) from dean/usage-stats-cohorts into master
Reviewed-on: #293
2026-07-12 04:07:43 +00:00
20a70fad2a Allow transient homography IDs 2026-07-11 20:55:53 -07:00
Dean Wenstrand
35c7c23ba9 Add disjoint activated segment to cohorts for stacking
All checks were successful
Tests / Tests (pull_request) Successful in 10s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 17:45:01 -07:00
Dean Wenstrand
40c4b82a59 Add weekly cohort funnel to getUsageStats
All checks were successful
Tests / Tests (pull_request) Successful in 13s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 17:40:10 -07:00
9ebe2e61df Merge pull request 'dean/usage-stats' (#292) from dean/usage-stats into master
Reviewed-on: #292
2026-07-11 19:26:13 +00:00
dc02fd8387 Merge pull request 'Select detected ball colors for Shot Lab' (#291) from colonelpanic/shot-lab-image-colors into master
Reviewed-on: #291
2026-07-10 17:20:37 +00:00
6 changed files with 408 additions and 6 deletions

View File

@@ -243,6 +243,15 @@ export type ClusterAssignmentInput = {
userId?: InputMaybe<Scalars['Int']['input']>; userId?: InputMaybe<Scalars['Int']['input']>;
}; };
export type CohortStatsGql = {
__typename?: 'CohortStatsGQL';
activated7d: Scalars['Int']['output'];
activatedNotPaid7d: Scalars['Int']['output'];
paid7d: Scalars['Int']['output'];
signups: Scalars['Int']['output'];
weekStart: Scalars['Date']['output'];
};
export type CommentGql = { export type CommentGql = {
__typename?: 'CommentGQL'; __typename?: 'CommentGQL';
id: Scalars['Int']['output']; id: Scalars['Int']['output'];
@@ -579,6 +588,11 @@ export type Header = {
value: Scalars['String']['output']; value: Scalars['String']['output'];
}; };
export enum HomographyBackendGql {
Classic = 'CLASSIC',
Tablenet = 'TABLENET'
}
export type HomographyInfoGql = { export type HomographyInfoGql = {
__typename?: 'HomographyInfoGQL'; __typename?: 'HomographyInfoGQL';
crop: BoundingBoxGql; crop: BoundingBoxGql;
@@ -1323,6 +1337,7 @@ export type Query = {
getQuotaStatus: QuotaStatusGql; getQuotaStatus: QuotaStatusGql;
getResolvedTier: ResolvedTierGql; getResolvedTier: ResolvedTierGql;
getRuns: GetRunsResult; getRuns: GetRunsResult;
getSessionCoach: SessionCoachGql;
getShotAnnotationTypes: Array<ShotAnnotationTypeGql>; getShotAnnotationTypes: Array<ShotAnnotationTypeGql>;
getShotTableState: ShotTableStateGql; getShotTableState: ShotTableStateGql;
getShots: Array<ShotGql>; getShots: Array<ShotGql>;
@@ -1488,6 +1503,11 @@ export type QueryGetRunsArgs = {
}; };
export type QueryGetSessionCoachArgs = {
videoId: Scalars['Int']['input'];
};
export type QueryGetShotAnnotationTypesArgs = { export type QueryGetShotAnnotationTypesArgs = {
errorTypes?: InputMaybe<Scalars['Boolean']['input']>; errorTypes?: InputMaybe<Scalars['Boolean']['input']>;
}; };
@@ -1831,6 +1851,84 @@ export type SerializedShotPathsGql = {
b64EncodedBuffer?: Maybe<Scalars['String']['output']>; b64EncodedBuffer?: Maybe<Scalars['String']['output']>;
}; };
export enum SessionCoachCandidateFamilyEnum {
Aiming = 'AIMING',
BackCut = 'BACK_CUT',
CueToObjectDistance = 'CUE_TO_OBJECT_DISTANCE',
CutAngle = 'CUT_ANGLE',
ObjectToPocketDistance = 'OBJECT_TO_POCKET_DISTANCE',
Spin = 'SPIN'
}
export type SessionCoachCandidateGql = {
__typename?: 'SessionCoachCandidateGQL';
action: Scalars['String']['output'];
body: Scalars['String']['output'];
drillId?: Maybe<Scalars['String']['output']>;
evidence: SessionCoachEvidenceGql;
family: SessionCoachCandidateFamilyEnum;
id: Scalars['ID']['output'];
kind: SessionCoachCandidateKindEnum;
limitations: Array<Scalars['String']['output']>;
rank: Scalars['Int']['output'];
supportingShotIds: Array<Scalars['Int']['output']>;
title: Scalars['String']['output'];
};
export enum SessionCoachCandidateKindEnum {
DirectionalMiss = 'DIRECTIONAL_MISS',
OutcomePattern = 'OUTCOME_PATTERN',
UsagePattern = 'USAGE_PATTERN'
}
export type SessionCoachEvidenceGql = {
__typename?: 'SessionCoachEvidenceGQL';
attemptCount: Scalars['Int']['output'];
bucketKey: Scalars['String']['output'];
bucketLabel: Scalars['String']['output'];
comparisonAttemptCount: Scalars['Int']['output'];
comparisonMadeCount: Scalars['Int']['output'];
comparisonMakePercentage?: Maybe<Scalars['Float']['output']>;
madeCount: Scalars['Int']['output'];
makePercentage: Scalars['Float']['output'];
missedCount: Scalars['Int']['output'];
patternShotCount: Scalars['Int']['output'];
rankReason: Scalars['String']['output'];
score: Scalars['Float']['output'];
};
export type SessionCoachGql = {
__typename?: 'SessionCoachGQL';
analysisVersion: Scalars['String']['output'];
analyzedShotCount: Scalars['Int']['output'];
candidates: Array<SessionCoachCandidateGql>;
generatedAt: Scalars['DateTime']['output'];
ineligibilityReason?: Maybe<SessionCoachIneligibilityReasonEnum>;
madeCount: Scalars['Int']['output'];
makePercentage?: Maybe<Scalars['Float']['output']>;
missedCount: Scalars['Int']['output'];
primaryCandidate?: Maybe<SessionCoachCandidateGql>;
processingId?: Maybe<Scalars['Int']['output']>;
processingStatus?: Maybe<Scalars['String']['output']>;
shotCount: Scalars['Int']['output'];
state: SessionCoachStateEnum;
summary?: Maybe<Scalars['String']['output']>;
unknownOutcomeCount: Scalars['Int']['output'];
videoId: Scalars['Int']['output'];
};
export enum SessionCoachIneligibilityReasonEnum {
Multiplayer = 'MULTIPLAYER'
}
export enum SessionCoachStateEnum {
Failed = 'FAILED',
Ineligible = 'INELIGIBLE',
NoTrackedShots = 'NO_TRACKED_SHOTS',
Processing = 'PROCESSING',
Ready = 'READY'
}
export type ShotAnnotationGql = { export type ShotAnnotationGql = {
__typename?: 'ShotAnnotationGQL'; __typename?: 'ShotAnnotationGQL';
createdAt?: Maybe<Scalars['DateTime']['output']>; createdAt?: Maybe<Scalars['DateTime']['output']>;
@@ -2252,6 +2350,7 @@ export type UsageStatsDailyGql = {
export type UsageStatsGql = { export type UsageStatsGql = {
__typename?: 'UsageStatsGQL'; __typename?: 'UsageStatsGQL';
cohorts: Array<CohortStatsGql>;
daily: Array<UsageStatsDailyGql>; daily: Array<UsageStatsDailyGql>;
funnel: FunnelStatsGql; funnel: FunnelStatsGql;
processingLast24h: Array<ProcessingStatusCountGql>; processingLast24h: Array<ProcessingStatusCountGql>;
@@ -2424,6 +2523,7 @@ export type VideoMetadataInput = {
framesPerSecond?: InputMaybe<Scalars['Float']['input']>; framesPerSecond?: InputMaybe<Scalars['Float']['input']>;
/** @deprecated `game_type` is deprecated. Use `tags` instead. */ /** @deprecated `game_type` is deprecated. Use `tags` instead. */
gameType?: InputMaybe<Scalars['String']['input']>; gameType?: InputMaybe<Scalars['String']['input']>;
homographyBackend?: InputMaybe<HomographyBackendGql>;
lastIntendedSegmentBound?: InputMaybe<Scalars['Int']['input']>; lastIntendedSegmentBound?: InputMaybe<Scalars['Int']['input']>;
pocketSize?: InputMaybe<Scalars['Float']['input']>; pocketSize?: InputMaybe<Scalars['Float']['input']>;
private?: InputMaybe<Scalars['Boolean']['input']>; private?: InputMaybe<Scalars['Boolean']['input']>;
@@ -2692,11 +2792,11 @@ export type GetFeedQueryVariables = Exact<{
}>; }>;
export type GetFeedQuery = { __typename?: 'Query', getUserVideos: { __typename?: 'VideoHistoryGQL', videos: Array<{ __typename?: 'VideoGQL', id: number, name?: string | null, screenshotUri?: string | null, totalShots: number, makePercentage: number, averageTimeBetweenShots?: number | null, averageDifficulty?: number | null, startTime?: any | null, private: boolean, elapsedTime?: number | null, tableSize: number, pocketSize?: number | null, owner?: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null } | null, stream?: { __typename?: 'UploadStreamGQL', id: string, lastIntendedSegmentBound?: number | null, streamSegmentType: StreamSegmentTypeEnum, isCompleted: boolean, lastSegmentUploadedAt?: any | null, clientUploadStatus?: ClientUploadStatusEnum | null } | null, tags: Array<{ __typename?: 'VideoTag', name: string, tagClasses: Array<{ __typename?: 'VideoTagClass', name: string }> }>, playerSummaries: Array<{ __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } }>, currentProcessing?: { __typename?: 'VideoProcessingGQL', id: number, status: ProcessingStatusEnum } | null, reactions: Array<{ __typename?: 'ReactionGQL', videoId: number, reaction: ReactionEnum, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }>, comments: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null }, replies: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }> }> }>, pageInfo: { __typename?: 'PageInfoGQL', hasNextPage: boolean, endCursor?: string | null } } }; export type GetFeedQuery = { __typename?: 'Query', getUserVideos: { __typename?: 'VideoHistoryGQL', videos: Array<{ __typename?: 'VideoGQL', id: number, name?: string | null, screenshotUri?: string | null, totalShots: number, makePercentage: number, averageTimeBetweenShots?: number | null, averageDifficulty?: number | null, startTime?: any | null, private: boolean, elapsedTime?: number | null, tableSize: number, pocketSize?: number | null, owner?: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null } | null, stream?: { __typename?: 'UploadStreamGQL', id: string, lastIntendedSegmentBound?: number | null, uploadCompletionCursor: number, streamSegmentType: StreamSegmentTypeEnum, isCompleted: boolean, lastSegmentUploadedAt?: any | null, clientUploadStatus?: ClientUploadStatusEnum | null } | null, tags: Array<{ __typename?: 'VideoTag', name: string, tagClasses: Array<{ __typename?: 'VideoTagClass', name: string }> }>, playerSummaries: Array<{ __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } }>, currentProcessing?: { __typename?: 'VideoProcessingGQL', id: number, status: ProcessingStatusEnum } | null, reactions: Array<{ __typename?: 'ReactionGQL', videoId: number, reaction: ReactionEnum, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }>, comments: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null }, replies: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }> }> }>, pageInfo: { __typename?: 'PageInfoGQL', hasNextPage: boolean, endCursor?: string | null } } };
export type UserSocialsFieldsFragment = { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null }; export type UserSocialsFieldsFragment = { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null };
export type VideoCardFieldsFragment = { __typename?: 'VideoGQL', id: number, name?: string | null, screenshotUri?: string | null, totalShots: number, makePercentage: number, averageTimeBetweenShots?: number | null, averageDifficulty?: number | null, startTime?: any | null, private: boolean, elapsedTime?: number | null, tableSize: number, pocketSize?: number | null, owner?: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null } | null, stream?: { __typename?: 'UploadStreamGQL', id: string, lastIntendedSegmentBound?: number | null, streamSegmentType: StreamSegmentTypeEnum, isCompleted: boolean, lastSegmentUploadedAt?: any | null, clientUploadStatus?: ClientUploadStatusEnum | null } | null, tags: Array<{ __typename?: 'VideoTag', name: string, tagClasses: Array<{ __typename?: 'VideoTagClass', name: string }> }>, playerSummaries: Array<{ __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } }>, currentProcessing?: { __typename?: 'VideoProcessingGQL', id: number, status: ProcessingStatusEnum } | null, reactions: Array<{ __typename?: 'ReactionGQL', videoId: number, reaction: ReactionEnum, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }>, comments: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null }, replies: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }> }> }; export type VideoCardFieldsFragment = { __typename?: 'VideoGQL', id: number, name?: string | null, screenshotUri?: string | null, totalShots: number, makePercentage: number, averageTimeBetweenShots?: number | null, averageDifficulty?: number | null, startTime?: any | null, private: boolean, elapsedTime?: number | null, tableSize: number, pocketSize?: number | null, owner?: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null } | null, stream?: { __typename?: 'UploadStreamGQL', id: string, lastIntendedSegmentBound?: number | null, uploadCompletionCursor: number, streamSegmentType: StreamSegmentTypeEnum, isCompleted: boolean, lastSegmentUploadedAt?: any | null, clientUploadStatus?: ClientUploadStatusEnum | null } | null, tags: Array<{ __typename?: 'VideoTag', name: string, tagClasses: Array<{ __typename?: 'VideoTagClass', name: string }> }>, playerSummaries: Array<{ __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } }>, currentProcessing?: { __typename?: 'VideoProcessingGQL', id: number, status: ProcessingStatusEnum } | null, reactions: Array<{ __typename?: 'ReactionGQL', videoId: number, reaction: ReactionEnum, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }>, comments: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null }, replies: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }> }> };
export type GetVideoFeedSessionCountQueryVariables = Exact<{ export type GetVideoFeedSessionCountQueryVariables = Exact<{
limit?: Scalars['Int']['input']; limit?: Scalars['Int']['input'];
@@ -2727,7 +2827,7 @@ export type GetVideoFeedQueryVariables = Exact<{
}>; }>;
export type GetVideoFeedQuery = { __typename?: 'Query', getFeedVideos: { __typename?: 'VideoHistoryGQL', hasFollowing: boolean, followingCount: number, videos: Array<{ __typename?: 'VideoGQL', id: number, name?: string | null, screenshotUri?: string | null, totalShots: number, makePercentage: number, averageTimeBetweenShots?: number | null, averageDifficulty?: number | null, startTime?: any | null, private: boolean, elapsedTime?: number | null, tableSize: number, pocketSize?: number | null, owner?: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null } | null, stream?: { __typename?: 'UploadStreamGQL', id: string, lastIntendedSegmentBound?: number | null, streamSegmentType: StreamSegmentTypeEnum, isCompleted: boolean, lastSegmentUploadedAt?: any | null, clientUploadStatus?: ClientUploadStatusEnum | null } | null, tags: Array<{ __typename?: 'VideoTag', name: string, tagClasses: Array<{ __typename?: 'VideoTagClass', name: string }> }>, playerSummaries: Array<{ __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } }>, currentProcessing?: { __typename?: 'VideoProcessingGQL', id: number, status: ProcessingStatusEnum } | null, reactions: Array<{ __typename?: 'ReactionGQL', videoId: number, reaction: ReactionEnum, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }>, comments: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null }, replies: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }> }> }>, pageInfo: { __typename?: 'PageInfoGQL', hasNextPage: boolean, endCursor?: string | null } } }; export type GetVideoFeedQuery = { __typename?: 'Query', getFeedVideos: { __typename?: 'VideoHistoryGQL', hasFollowing: boolean, followingCount: number, videos: Array<{ __typename?: 'VideoGQL', id: number, name?: string | null, screenshotUri?: string | null, totalShots: number, makePercentage: number, averageTimeBetweenShots?: number | null, averageDifficulty?: number | null, startTime?: any | null, private: boolean, elapsedTime?: number | null, tableSize: number, pocketSize?: number | null, owner?: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null } | null, stream?: { __typename?: 'UploadStreamGQL', id: string, lastIntendedSegmentBound?: number | null, uploadCompletionCursor: number, streamSegmentType: StreamSegmentTypeEnum, isCompleted: boolean, lastSegmentUploadedAt?: any | null, clientUploadStatus?: ClientUploadStatusEnum | null } | null, tags: Array<{ __typename?: 'VideoTag', name: string, tagClasses: Array<{ __typename?: 'VideoTagClass', name: string }> }>, playerSummaries: Array<{ __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } }>, currentProcessing?: { __typename?: 'VideoProcessingGQL', id: number, status: ProcessingStatusEnum } | null, reactions: Array<{ __typename?: 'ReactionGQL', videoId: number, reaction: ReactionEnum, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }>, comments: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null }, replies: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }> }> }>, pageInfo: { __typename?: 'PageInfoGQL', hasNextPage: boolean, endCursor?: string | null } } };
export type GetMakesLeaderboardQueryVariables = Exact<{ export type GetMakesLeaderboardQueryVariables = Exact<{
interval?: InputMaybe<TimeInterval>; interval?: InputMaybe<TimeInterval>;
@@ -3034,7 +3134,7 @@ export type GetRecordedStreamShotsQueryVariables = Exact<{
}>; }>;
export type GetRecordedStreamShotsQuery = { __typename?: 'Query', getOrderedShots: { __typename?: 'GetShotsResult', shots: Array<{ __typename?: 'ShotGQL', id: number, videoId: number, startFrame: number, endFrame: number }> } }; export type GetRecordedStreamShotsQuery = { __typename?: 'Query', getOrderedShots: { __typename?: 'GetShotsResult', shots: Array<{ __typename?: 'ShotGQL', id: number, videoId: number, startFrame: number, endFrame: number, serializedShotPaths?: { __typename?: 'SerializedShotPathsGQL', b64EncodedBuffer?: string | null } | null, cueObjectFeatures?: { __typename?: 'CueObjectFeaturesGQL', cueObjectDistance?: number | null, cueObjectAngle?: number | null, cueBallSpeed?: number | null, shotDirection?: ShotDirectionEnum | null, spinType?: SpinTypeEnum | null } | null, pocketingIntentionFeatures?: { __typename?: 'PocketingIntentionFeaturesGQL', make?: boolean | null, targetPocketDistance?: number | null, targetPocketAngle?: number | null, targetPocketAngleDirection?: ShotDirectionEnum | null, marginOfErrorInDegrees?: number | null, intendedPocketType?: PocketEnum | null, difficulty?: number | null } | null, pocketingIntentionInfo?: { __typename?: 'PocketingIntentionInfoGQL', ballId: number, pocketId: PocketIdentifier, pathMetadataIndex: number } | null }> } };
export type GetRunsForHighlightsQueryVariables = Exact<{ export type GetRunsForHighlightsQueryVariables = Exact<{
filterInput: RunFilterInput; filterInput: RunFilterInput;
@@ -3058,6 +3158,15 @@ export type GetRunsWithTimestampsQueryVariables = Exact<{
export type GetRunsWithTimestampsQuery = { __typename?: 'Query', getRuns: { __typename?: 'GetRunsResult', count?: number | null, runIds: Array<number>, runs: Array<{ __typename?: 'RunGQL', id: number, runLength: number, userId: number, videoId: number, shots: Array<{ __typename?: 'ShotGQL', videoId: number, id: number, createdAt?: any | null, pocketingIntentionFeatures?: { __typename?: 'PocketingIntentionFeaturesGQL', make?: boolean | null, targetPocketDistance?: number | null, targetPocketAngle?: number | null, targetPocketAngleDirection?: ShotDirectionEnum | null, marginOfErrorInDegrees?: number | null, intendedPocketType?: PocketEnum | null, difficulty?: number | null } | null }> }> } }; export type GetRunsWithTimestampsQuery = { __typename?: 'Query', getRuns: { __typename?: 'GetRunsResult', count?: number | null, runIds: Array<number>, runs: Array<{ __typename?: 'RunGQL', id: number, runLength: number, userId: number, videoId: number, shots: Array<{ __typename?: 'ShotGQL', videoId: number, id: number, createdAt?: any | null, pocketingIntentionFeatures?: { __typename?: 'PocketingIntentionFeaturesGQL', make?: boolean | null, targetPocketDistance?: number | null, targetPocketAngle?: number | null, targetPocketAngleDirection?: ShotDirectionEnum | null, marginOfErrorInDegrees?: number | null, intendedPocketType?: PocketEnum | null, difficulty?: number | null } | null }> }> } };
export type GetSessionCoachQueryVariables = Exact<{
videoId: Scalars['Int']['input'];
}>;
export type GetSessionCoachQuery = { __typename?: 'Query', getSessionCoach: { __typename?: 'SessionCoachGQL', videoId: number, processingId?: number | null, analysisVersion: string, state: SessionCoachStateEnum, generatedAt: any, processingStatus?: string | null, ineligibilityReason?: SessionCoachIneligibilityReasonEnum | null, summary?: string | null, shotCount: number, madeCount: number, missedCount: number, unknownOutcomeCount: number, analyzedShotCount: number, makePercentage?: number | null, primaryCandidate?: { __typename?: 'SessionCoachCandidateGQL', id: string, rank: number, family: SessionCoachCandidateFamilyEnum, kind: SessionCoachCandidateKindEnum, title: string, body: string, action: string, supportingShotIds: Array<number>, drillId?: string | null, limitations: Array<string>, evidence: { __typename?: 'SessionCoachEvidenceGQL', bucketKey: string, bucketLabel: string, attemptCount: number, madeCount: number, missedCount: number, makePercentage: number, comparisonAttemptCount: number, comparisonMadeCount: number, comparisonMakePercentage?: number | null, patternShotCount: number, score: number, rankReason: string } } | null, candidates: Array<{ __typename?: 'SessionCoachCandidateGQL', id: string, rank: number, family: SessionCoachCandidateFamilyEnum, kind: SessionCoachCandidateKindEnum, title: string, body: string, action: string, supportingShotIds: Array<number>, drillId?: string | null, limitations: Array<string>, evidence: { __typename?: 'SessionCoachEvidenceGQL', bucketKey: string, bucketLabel: string, attemptCount: number, madeCount: number, missedCount: number, makePercentage: number, comparisonAttemptCount: number, comparisonMadeCount: number, comparisonMakePercentage?: number | null, patternShotCount: number, score: number, rankReason: string } }> } };
export type SessionCoachCandidateFieldsFragment = { __typename?: 'SessionCoachCandidateGQL', id: string, rank: number, family: SessionCoachCandidateFamilyEnum, kind: SessionCoachCandidateKindEnum, title: string, body: string, action: string, supportingShotIds: Array<number>, drillId?: string | null, limitations: Array<string>, evidence: { __typename?: 'SessionCoachEvidenceGQL', bucketKey: string, bucketLabel: string, attemptCount: number, madeCount: number, missedCount: number, makePercentage: number, comparisonAttemptCount: number, comparisonMadeCount: number, comparisonMakePercentage?: number | null, patternShotCount: number, score: number, rankReason: string } };
export type PlayerSummaryFieldsFragment = { __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } }; export type PlayerSummaryFieldsFragment = { __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } };
export type PlayerClusterShotFieldsFragment = { __typename?: 'PlayerClusterShotGQL', shotId: number, bboxX1: number, bboxY1: number, bboxX2: number, bboxY2: number, confidence: number, isConfirmed: boolean, cropUrl?: string | null, fullFrameUrl?: string | null }; export type PlayerClusterShotFieldsFragment = { __typename?: 'PlayerClusterShotGQL', shotId: number, bboxX1: number, bboxY1: number, bboxX2: number, bboxY2: number, confidence: number, isConfirmed: boolean, cropUrl?: string | null, fullFrameUrl?: string | null };
@@ -3234,7 +3343,7 @@ export type GetUsageStatsQueryVariables = Exact<{
}>; }>;
export type GetUsageStatsQuery = { __typename?: 'Query', getUsageStats: { __typename?: 'UsageStatsGQL', totalUsers: number, totalVideos: number, totalShots: number, totalRuns: number, funnel: { __typename?: 'FunnelStatsGQL', freemiumEpoch: any, eraSignups: number, eraPaying: number, activationCohort: number, activationActivated: number, payingUsers: number, newPaid7d: number, newPaid30d: number, retentionPrevActive: number, retentionReturned: number }, windows: Array<{ __typename?: 'UsageStatsWindowGQL', label: string, newUsers: number, activeUsers: number, sessions: number, shots: number }>, daily: Array<{ __typename?: 'UsageStatsDailyGQL', day: any, newUsers: number, activeUsers: number, sessions: number, shots: number }>, processingLast24h: Array<{ __typename?: 'ProcessingStatusCountGQL', status: string, count: number }> } }; export type GetUsageStatsQuery = { __typename?: 'Query', getUsageStats: { __typename?: 'UsageStatsGQL', totalUsers: number, totalVideos: number, totalShots: number, totalRuns: number, funnel: { __typename?: 'FunnelStatsGQL', freemiumEpoch: any, eraSignups: number, eraPaying: number, activationCohort: number, activationActivated: number, payingUsers: number, newPaid7d: number, newPaid30d: number, retentionPrevActive: number, retentionReturned: number }, cohorts: Array<{ __typename?: 'CohortStatsGQL', weekStart: any, signups: number, activated7d: number, paid7d: number, activatedNotPaid7d: number }>, windows: Array<{ __typename?: 'UsageStatsWindowGQL', label: string, newUsers: number, activeUsers: number, sessions: number, shots: number }>, daily: Array<{ __typename?: 'UsageStatsDailyGQL', day: any, newUsers: number, activeUsers: number, sessions: number, shots: number }>, processingLast24h: Array<{ __typename?: 'ProcessingStatusCountGQL', status: string, count: number }> } };
export type GetProfileImageUploadLinkMutationVariables = Exact<{ export type GetProfileImageUploadLinkMutationVariables = Exact<{
fileExt?: InputMaybe<Scalars['String']['input']>; fileExt?: InputMaybe<Scalars['String']['input']>;
@@ -3403,7 +3512,7 @@ export type GetVideoCardQueryVariables = Exact<{
}>; }>;
export type GetVideoCardQuery = { __typename?: 'Query', getVideo: { __typename?: 'VideoGQL', id: number, name?: string | null, screenshotUri?: string | null, totalShots: number, makePercentage: number, averageTimeBetweenShots?: number | null, averageDifficulty?: number | null, startTime?: any | null, private: boolean, elapsedTime?: number | null, tableSize: number, pocketSize?: number | null, owner?: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null } | null, stream?: { __typename?: 'UploadStreamGQL', id: string, lastIntendedSegmentBound?: number | null, streamSegmentType: StreamSegmentTypeEnum, isCompleted: boolean, lastSegmentUploadedAt?: any | null, clientUploadStatus?: ClientUploadStatusEnum | null } | null, tags: Array<{ __typename?: 'VideoTag', name: string, tagClasses: Array<{ __typename?: 'VideoTagClass', name: string }> }>, playerSummaries: Array<{ __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } }>, currentProcessing?: { __typename?: 'VideoProcessingGQL', id: number, status: ProcessingStatusEnum } | null, reactions: Array<{ __typename?: 'ReactionGQL', videoId: number, reaction: ReactionEnum, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }>, comments: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null }, replies: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }> }> } }; export type GetVideoCardQuery = { __typename?: 'Query', getVideo: { __typename?: 'VideoGQL', id: number, name?: string | null, screenshotUri?: string | null, totalShots: number, makePercentage: number, averageTimeBetweenShots?: number | null, averageDifficulty?: number | null, startTime?: any | null, private: boolean, elapsedTime?: number | null, tableSize: number, pocketSize?: number | null, owner?: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null } | null, stream?: { __typename?: 'UploadStreamGQL', id: string, lastIntendedSegmentBound?: number | null, uploadCompletionCursor: number, streamSegmentType: StreamSegmentTypeEnum, isCompleted: boolean, lastSegmentUploadedAt?: any | null, clientUploadStatus?: ClientUploadStatusEnum | null } | null, tags: Array<{ __typename?: 'VideoTag', name: string, tagClasses: Array<{ __typename?: 'VideoTagClass', name: string }> }>, playerSummaries: Array<{ __typename?: 'PlayerSummaryGQL', clusterId: number, userId?: number | null, username?: string | null, profileImageUri?: string | null, representativeFullFrameUrl?: string | null, totalShots: number, totalShotsMade: number, makePercentage: number, score?: number | null, longestRun: number, runLengths: Array<number>, averageDifficulty?: number | null, averageTimeBetweenShots?: number | null, spinTypeBreakdown: { __typename?: 'SpinTypeBreakdownGQL', draw: number, center: number, follow: number } }>, currentProcessing?: { __typename?: 'VideoProcessingGQL', id: number, status: ProcessingStatusEnum } | null, reactions: Array<{ __typename?: 'ReactionGQL', videoId: number, reaction: ReactionEnum, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }>, comments: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null }, replies: Array<{ __typename?: 'CommentGQL', id: number, message: string, user: { __typename?: 'UserGQL', id: number, username: string, profileImageUri?: string | null, isFollowedByCurrentUser?: boolean | null } }> }> } };
export type GetVideosQueryVariables = Exact<{ export type GetVideosQueryVariables = Exact<{
videoIds: Array<Scalars['Int']['input']> | Scalars['Int']['input']; videoIds: Array<Scalars['Int']['input']> | Scalars['Int']['input'];
@@ -3662,6 +3771,7 @@ export const VideoCardFieldsFragmentDoc = gql`
stream { stream {
id id
lastIntendedSegmentBound lastIntendedSegmentBound
uploadCompletionCursor
streamSegmentType streamSegmentType
isCompleted isCompleted
lastSegmentUploadedAt lastSegmentUploadedAt
@@ -3844,6 +3954,34 @@ export const PocketingIntentionFragmentFragmentDoc = gql`
difficulty difficulty
} }
`; `;
export const SessionCoachCandidateFieldsFragmentDoc = gql`
fragment SessionCoachCandidateFields on SessionCoachCandidateGQL {
id
rank
family
kind
title
body
action
supportingShotIds
drillId
limitations
evidence {
bucketKey
bucketLabel
attemptCount
madeCount
missedCount
makePercentage
comparisonAttemptCount
comparisonMadeCount
comparisonMakePercentage
patternShotCount
score
rankReason
}
}
`;
export const PlayerClusterShotFieldsFragmentDoc = gql` export const PlayerClusterShotFieldsFragmentDoc = gql`
fragment PlayerClusterShotFields on PlayerClusterShotGQL { fragment PlayerClusterShotFields on PlayerClusterShotGQL {
shotId shotId
@@ -7232,6 +7370,30 @@ export const GetRecordedStreamShotsDocument = gql`
videoId videoId
startFrame startFrame
endFrame endFrame
serializedShotPaths {
b64EncodedBuffer
}
cueObjectFeatures {
cueObjectDistance
cueObjectAngle
cueBallSpeed
shotDirection
spinType
}
pocketingIntentionFeatures {
make
targetPocketDistance
targetPocketAngle
targetPocketAngleDirection
marginOfErrorInDegrees
intendedPocketType
difficulty
}
pocketingIntentionInfo {
ballId
pocketId
pathMetadataIndex
}
} }
} }
} }
@@ -7388,6 +7550,65 @@ export type GetRunsWithTimestampsQueryHookResult = ReturnType<typeof useGetRunsW
export type GetRunsWithTimestampsLazyQueryHookResult = ReturnType<typeof useGetRunsWithTimestampsLazyQuery>; export type GetRunsWithTimestampsLazyQueryHookResult = ReturnType<typeof useGetRunsWithTimestampsLazyQuery>;
export type GetRunsWithTimestampsSuspenseQueryHookResult = ReturnType<typeof useGetRunsWithTimestampsSuspenseQuery>; export type GetRunsWithTimestampsSuspenseQueryHookResult = ReturnType<typeof useGetRunsWithTimestampsSuspenseQuery>;
export type GetRunsWithTimestampsQueryResult = Apollo.QueryResult<GetRunsWithTimestampsQuery, GetRunsWithTimestampsQueryVariables>; export type GetRunsWithTimestampsQueryResult = Apollo.QueryResult<GetRunsWithTimestampsQuery, GetRunsWithTimestampsQueryVariables>;
export const GetSessionCoachDocument = gql`
query GetSessionCoach($videoId: Int!) {
getSessionCoach(videoId: $videoId) {
videoId
processingId
analysisVersion
state
generatedAt
processingStatus
ineligibilityReason
summary
shotCount
madeCount
missedCount
unknownOutcomeCount
analyzedShotCount
makePercentage
primaryCandidate {
...SessionCoachCandidateFields
}
candidates {
...SessionCoachCandidateFields
}
}
}
${SessionCoachCandidateFieldsFragmentDoc}`;
/**
* __useGetSessionCoachQuery__
*
* To run a query within a React component, call `useGetSessionCoachQuery` and pass it any options that fit your needs.
* When your component renders, `useGetSessionCoachQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetSessionCoachQuery({
* variables: {
* videoId: // value for 'videoId'
* },
* });
*/
export function useGetSessionCoachQuery(baseOptions: Apollo.QueryHookOptions<GetSessionCoachQuery, GetSessionCoachQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<GetSessionCoachQuery, GetSessionCoachQueryVariables>(GetSessionCoachDocument, options);
}
export function useGetSessionCoachLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetSessionCoachQuery, GetSessionCoachQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<GetSessionCoachQuery, GetSessionCoachQueryVariables>(GetSessionCoachDocument, options);
}
export function useGetSessionCoachSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions<GetSessionCoachQuery, GetSessionCoachQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSuspenseQuery<GetSessionCoachQuery, GetSessionCoachQueryVariables>(GetSessionCoachDocument, options);
}
export type GetSessionCoachQueryHookResult = ReturnType<typeof useGetSessionCoachQuery>;
export type GetSessionCoachLazyQueryHookResult = ReturnType<typeof useGetSessionCoachLazyQuery>;
export type GetSessionCoachSuspenseQueryHookResult = ReturnType<typeof useGetSessionCoachSuspenseQuery>;
export type GetSessionCoachQueryResult = Apollo.QueryResult<GetSessionCoachQuery, GetSessionCoachQueryVariables>;
export const VideoPlayerClustersDocument = gql` export const VideoPlayerClustersDocument = gql`
query VideoPlayerClusters($videoId: Int!) { query VideoPlayerClusters($videoId: Int!) {
videoPlayerClusters(videoId: $videoId) { videoPlayerClusters(videoId: $videoId) {
@@ -8315,6 +8536,13 @@ export const GetUsageStatsDocument = gql`
retentionPrevActive retentionPrevActive
retentionReturned retentionReturned
} }
cohorts {
weekStart
signups
activated7d
paid7d
activatedNotPaid7d
}
totalUsers totalUsers
totalVideos totalVideos
totalShots totalShots

View File

@@ -41,6 +41,7 @@ fragment VideoCardFields on VideoGQL {
stream { stream {
id id
lastIntendedSegmentBound lastIntendedSegmentBound
uploadCompletionCursor
streamSegmentType streamSegmentType
isCompleted isCompleted
lastSegmentUploadedAt lastSegmentUploadedAt

View File

@@ -33,6 +33,30 @@ query GetRecordedStreamShots($videoId: Int!, $limit: Int! = 200) {
videoId videoId
startFrame startFrame
endFrame endFrame
serializedShotPaths {
b64EncodedBuffer
}
cueObjectFeatures {
cueObjectDistance
cueObjectAngle
cueBallSpeed
shotDirection
spinType
}
pocketingIntentionFeatures {
make
targetPocketDistance
targetPocketAngle
targetPocketAngleDirection
marginOfErrorInDegrees
intendedPocketType
difficulty
}
pocketingIntentionInfo {
ballId
pocketId
pathMetadataIndex
}
} }
} }
} }

View File

@@ -0,0 +1,51 @@
query GetSessionCoach($videoId: Int!) {
getSessionCoach(videoId: $videoId) {
videoId
processingId
analysisVersion
state
generatedAt
processingStatus
ineligibilityReason
summary
shotCount
madeCount
missedCount
unknownOutcomeCount
analyzedShotCount
makePercentage
primaryCandidate {
...SessionCoachCandidateFields
}
candidates {
...SessionCoachCandidateFields
}
}
}
fragment SessionCoachCandidateFields on SessionCoachCandidateGQL {
id
rank
family
kind
title
body
action
supportingShotIds
drillId
limitations
evidence {
bucketKey
bucketLabel
attemptCount
madeCount
missedCount
makePercentage
comparisonAttemptCount
comparisonMadeCount
comparisonMakePercentage
patternShotCount
score
rankReason
}
}

View File

@@ -12,6 +12,13 @@ query getUsageStats($days: Int! = 30) {
retentionPrevActive retentionPrevActive
retentionReturned retentionReturned
} }
cohorts {
weekStart
signups
activated7d
paid7d
activatedNotPaid7d
}
totalUsers totalUsers
totalVideos totalVideos
totalShots totalShots

View File

@@ -65,6 +65,7 @@ type Query {
limit: Int! = 500 limit: Int! = 500
countRespectsLimit: Boolean! = false countRespectsLimit: Boolean! = false
): GetRunsResult! ): GetRunsResult!
getSessionCoach(videoId: Int!): SessionCoachGQL!
videoPlayerClusters(videoId: Int!): [PlayerClusterGQL!]! videoPlayerClusters(videoId: Int!): [PlayerClusterGQL!]!
getShotAnnotationTypes(errorTypes: Boolean = false): [ShotAnnotationTypeGQL!]! getShotAnnotationTypes(errorTypes: Boolean = false): [ShotAnnotationTypeGQL!]!
getTableState( getTableState(
@@ -998,6 +999,81 @@ input DatetimeOrdering {
startingAt: DateTime = null startingAt: DateTime = null
} }
type SessionCoachGQL {
videoId: Int!
processingId: Int
analysisVersion: String!
state: SessionCoachStateEnum!
generatedAt: DateTime!
processingStatus: String
ineligibilityReason: SessionCoachIneligibilityReasonEnum
summary: String
shotCount: Int!
madeCount: Int!
missedCount: Int!
unknownOutcomeCount: Int!
analyzedShotCount: Int!
makePercentage: Float
primaryCandidate: SessionCoachCandidateGQL
candidates: [SessionCoachCandidateGQL!]!
}
enum SessionCoachStateEnum {
PROCESSING
READY
NO_TRACKED_SHOTS
INELIGIBLE
FAILED
}
enum SessionCoachIneligibilityReasonEnum {
MULTIPLAYER
}
type SessionCoachCandidateGQL {
id: ID!
rank: Int!
family: SessionCoachCandidateFamilyEnum!
kind: SessionCoachCandidateKindEnum!
title: String!
body: String!
action: String!
supportingShotIds: [Int!]!
drillId: String
evidence: SessionCoachEvidenceGQL!
limitations: [String!]!
}
enum SessionCoachCandidateFamilyEnum {
AIMING
CUT_ANGLE
SPIN
OBJECT_TO_POCKET_DISTANCE
CUE_TO_OBJECT_DISTANCE
BACK_CUT
}
enum SessionCoachCandidateKindEnum {
DIRECTIONAL_MISS
OUTCOME_PATTERN
USAGE_PATTERN
}
type SessionCoachEvidenceGQL {
bucketKey: String!
bucketLabel: String!
attemptCount: Int!
madeCount: Int!
missedCount: Int!
makePercentage: Float!
comparisonAttemptCount: Int!
comparisonMadeCount: Int!
comparisonMakePercentage: Float
patternShotCount: Int!
score: Float!
rankReason: String!
}
type PlayerClusterGQL { type PlayerClusterGQL {
videoId: Int! videoId: Int!
clusterId: Int! clusterId: Int!
@@ -1224,6 +1300,7 @@ type UsageStatsGQL {
totalVideos: Int! totalVideos: Int!
totalShots: Int! totalShots: Int!
totalRuns: Int! totalRuns: Int!
cohorts: [CohortStatsGQL!]!
windows: [UsageStatsWindowGQL!]! windows: [UsageStatsWindowGQL!]!
daily: [UsageStatsDailyGQL!]! daily: [UsageStatsDailyGQL!]!
processingLast24h: [ProcessingStatusCountGQL!]! processingLast24h: [ProcessingStatusCountGQL!]!
@@ -1242,6 +1319,14 @@ type FunnelStatsGQL {
retentionReturned: Int! retentionReturned: Int!
} }
type CohortStatsGQL {
weekStart: Date!
signups: Int!
activated7d: Int!
paid7d: Int!
activatedNotPaid7d: Int!
}
type UsageStatsWindowGQL { type UsageStatsWindowGQL {
label: String! label: String!
newUsers: Int! newUsers: Int!
@@ -1857,6 +1942,7 @@ input VideoMetadataInput {
clientUploadStatus: ClientUploadStatusEnum = null clientUploadStatus: ClientUploadStatusEnum = null
resolution: VideoResolution = null resolution: VideoResolution = null
framesPerSecond: Float = null framesPerSecond: Float = null
homographyBackend: HomographyBackendGQL = null
} }
input VideoResolution { input VideoResolution {
@@ -1864,6 +1950,11 @@ input VideoResolution {
height: Int! height: Int!
} }
enum HomographyBackendGQL {
CLASSIC
TABLENET
}
type GetUploadLinkReturn { type GetUploadLinkReturn {
value: UploadLinkGetUploadLinkErrors! value: UploadLinkGetUploadLinkErrors!
stream: UploadStreamGQL stream: UploadStreamGQL