Compare commits

..

14 Commits

Author SHA1 Message Date
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
Dean Wenstrand
6f356f9535 Add freemium epoch + era conversion to funnel
All checks were successful
Tests / Tests (pull_request) Successful in 12s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 11:38:20 -07:00
Dean Wenstrand
5c0ff8c31b Add freemium funnel to getUsageStats
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 10:48:25 -07:00
Dean Wenstrand
582c20170d Add getUsageStats admin query + operation (schema regen on shot-lab tip)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 10:36:47 -07: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
ef4782fc14 Select detected ball colors for Shot Lab
All checks were successful
Tests / Tests (pull_request) Successful in 33s
2026-07-10 00:12:06 -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
4 changed files with 400 additions and 7 deletions

View File

@@ -260,6 +260,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"];
@@ -2299,6 +2308,20 @@ export type FloatRangeFilter = {
lessThanInclusive?: Scalars["Boolean"]["input"]; lessThanInclusive?: Scalars["Boolean"]["input"];
}; };
export type FunnelStatsGql = {
__typename?: "FunnelStatsGQL";
activationActivated: Scalars["Int"]["output"];
activationCohort: Scalars["Int"]["output"];
eraPaying: Scalars["Int"]["output"];
eraSignups: Scalars["Int"]["output"];
freemiumEpoch: Scalars["Date"]["output"];
newPaid7d: Scalars["Int"]["output"];
newPaid30d: Scalars["Int"]["output"];
payingUsers: Scalars["Int"]["output"];
retentionPrevActive: Scalars["Int"]["output"];
retentionReturned: Scalars["Int"]["output"];
};
export type GameTypeTagMetric = { export type GameTypeTagMetric = {
__typename?: "GameTypeTagMetric"; __typename?: "GameTypeTagMetric";
madeShots: Scalars["Int"]["output"]; madeShots: Scalars["Int"]["output"];
@@ -2386,7 +2409,7 @@ export type HomographyInfoGql = {
crop: BoundingBoxGql; crop: BoundingBoxGql;
destPoints: PocketPointsGql; destPoints: PocketPointsGql;
frameIndex: Scalars["Int"]["output"]; frameIndex: Scalars["Int"]["output"];
id: Scalars["Int"]["output"]; id?: Maybe<Scalars["Int"]["output"]>;
pockets: Array<BoundingBoxGql>; pockets: Array<BoundingBoxGql>;
sourcePoints: PocketPointsGql; sourcePoints: PocketPointsGql;
}; };
@@ -3018,6 +3041,12 @@ export type ProcessingFailedErr = {
processing: VideoProcessingGql; processing: VideoProcessingGql;
}; };
export type ProcessingStatusCountGql = {
__typename?: "ProcessingStatusCountGQL";
count: Scalars["Int"]["output"];
status: Scalars["String"]["output"];
};
export enum ProcessingStatusEnum { export enum ProcessingStatusEnum {
Created = "CREATED", Created = "CREATED",
Failed = "FAILED", Failed = "FAILED",
@@ -3060,11 +3089,13 @@ export type Query = {
getResolvedTier: ResolvedTierGql; getResolvedTier: ResolvedTierGql;
getRuns: GetRunsResult; getRuns: GetRunsResult;
getShotAnnotationTypes: Array<ShotAnnotationTypeGql>; getShotAnnotationTypes: Array<ShotAnnotationTypeGql>;
getShotTableState: ShotTableStateGql;
getShots: Array<ShotGql>; getShots: Array<ShotGql>;
getShotsByIds: Array<ShotGql>; getShotsByIds: Array<ShotGql>;
getShotsWithMetadata: GetShotsResult; getShotsWithMetadata: GetShotsResult;
getStorageStatus?: Maybe<StorageStatusGql>; getStorageStatus?: Maybe<StorageStatusGql>;
getTableState: TableStateGql; getTableState: TableStateGql;
getUsageStats: UsageStatsGql;
getUser?: Maybe<UserGql>; getUser?: Maybe<UserGql>;
getUserRelationshipsMatching: UserRelationshipsResult; getUserRelationshipsMatching: UserRelationshipsResult;
getUserSubscriptionStatus: UserSubscriptionStatusGql; getUserSubscriptionStatus: UserSubscriptionStatusGql;
@@ -3204,6 +3235,10 @@ export type QueryGetShotAnnotationTypesArgs = {
errorTypes?: InputMaybe<Scalars["Boolean"]["input"]>; errorTypes?: InputMaybe<Scalars["Boolean"]["input"]>;
}; };
export type QueryGetShotTableStateArgs = {
shotId: Scalars["Int"]["input"];
};
export type QueryGetShotsArgs = { export type QueryGetShotsArgs = {
countRespectsLimit?: Scalars["Boolean"]["input"]; countRespectsLimit?: Scalars["Boolean"]["input"];
filterInput: FilterInput; filterInput: FilterInput;
@@ -3229,6 +3264,10 @@ export type QueryGetTableStateArgs = {
useHomography?: InputMaybe<HomographyInputGql>; useHomography?: InputMaybe<HomographyInputGql>;
}; };
export type QueryGetUsageStatsArgs = {
days?: Scalars["Int"]["input"];
};
export type QueryGetUserArgs = { export type QueryGetUserArgs = {
userId: Scalars["Int"]["input"]; userId: Scalars["Int"]["input"];
}; };
@@ -3338,6 +3377,14 @@ export type QuotaStatusGql = {
tierName: Scalars["String"]["output"]; tierName: Scalars["String"]["output"];
}; };
export type RgbColorGql = {
__typename?: "RGBColorGQL";
b: Scalars["Int"]["output"];
g: Scalars["Int"]["output"];
hex: Scalars["String"]["output"];
r: Scalars["Int"]["output"];
};
export enum ReactionEnum { export enum ReactionEnum {
Bullseye = "BULLSEYE", Bullseye = "BULLSEYE",
Heart = "HEART", Heart = "HEART",
@@ -3571,6 +3618,14 @@ export type ShotProjectionGql = {
trajectories: Array<BallTrajectoryGql>; trajectories: Array<BallTrajectoryGql>;
}; };
export type ShotTableStateGql = {
__typename?: "ShotTableStateGQL";
frameIndex: Scalars["Int"]["output"];
shotId: Scalars["Int"]["output"];
tableState: TableStateGql;
videoId: Scalars["Int"]["output"];
};
export type ShotsOrderingComponent = export type ShotsOrderingComponent =
| { | {
difficulty: FloatOrdering; difficulty: FloatOrdering;
@@ -3803,6 +3858,7 @@ export type SyncAppleSubscriptionResultGql = {
export type TableStateGql = { export type TableStateGql = {
__typename?: "TableStateGQL"; __typename?: "TableStateGQL";
homography?: Maybe<HomographyInfoGql>; homography?: Maybe<HomographyInfoGql>;
identifierToColor: Array<Maybe<RgbColorGql>>;
identifierToPosition: Array<Array<Scalars["Float"]["output"]>>; identifierToPosition: Array<Array<Scalars["Float"]["output"]>>;
}; };
@@ -3935,6 +3991,37 @@ export type UploadStreamGql = {
uploadsCompleted: Scalars["Int"]["output"]; uploadsCompleted: Scalars["Int"]["output"];
}; };
export type UsageStatsDailyGql = {
__typename?: "UsageStatsDailyGQL";
activeUsers: Scalars["Int"]["output"];
day: Scalars["Date"]["output"];
newUsers: Scalars["Int"]["output"];
sessions: Scalars["Int"]["output"];
shots: Scalars["Int"]["output"];
};
export type UsageStatsGql = {
__typename?: "UsageStatsGQL";
cohorts: Array<CohortStatsGql>;
daily: Array<UsageStatsDailyGql>;
funnel: FunnelStatsGql;
processingLast24h: Array<ProcessingStatusCountGql>;
totalRuns: Scalars["Int"]["output"];
totalShots: Scalars["Int"]["output"];
totalUsers: Scalars["Int"]["output"];
totalVideos: Scalars["Int"]["output"];
windows: Array<UsageStatsWindowGql>;
};
export type UsageStatsWindowGql = {
__typename?: "UsageStatsWindowGQL";
activeUsers: Scalars["Int"]["output"];
label: Scalars["String"]["output"];
newUsers: Scalars["Int"]["output"];
sessions: Scalars["Int"]["output"];
shots: Scalars["Int"]["output"];
};
export type UserGql = { export type UserGql = {
__typename?: "UserGQL"; __typename?: "UserGQL";
activeVideoId?: Maybe<Scalars["Int"]["output"]>; activeVideoId?: Maybe<Scalars["Int"]["output"]>;
@@ -6736,6 +6823,10 @@ export type GetTableStateQuery = {
getTableState: { getTableState: {
__typename?: "TableStateGQL"; __typename?: "TableStateGQL";
identifierToPosition: Array<Array<number>>; identifierToPosition: Array<Array<number>>;
identifierToColor: Array<{
__typename?: "RGBColorGQL";
hex: string;
} | null>;
}; };
}; };
@@ -6752,9 +6843,13 @@ export type GetLiveTableStateQuery = {
tableState: { tableState: {
__typename?: "TableStateGQL"; __typename?: "TableStateGQL";
identifierToPosition: Array<Array<number>>; identifierToPosition: Array<Array<number>>;
identifierToColor: Array<{
__typename?: "RGBColorGQL";
hex: string;
} | null>;
homography?: { homography?: {
__typename?: "HomographyInfoGQL"; __typename?: "HomographyInfoGQL";
id: number; id?: number | null;
frameIndex: number; frameIndex: number;
crop: { crop: {
__typename?: "BoundingBoxGQL"; __typename?: "BoundingBoxGQL";
@@ -7378,6 +7473,63 @@ export type DeleteTagsMutation = {
deleteTags: boolean; deleteTags: boolean;
}; };
export type GetUsageStatsQueryVariables = Exact<{
days?: Scalars["Int"]["input"];
}>;
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"]>;
}>; }>;
@@ -7734,7 +7886,7 @@ export type GetStreamMonitoringDetailsQuery = {
elapsedTime?: number | null; elapsedTime?: number | null;
currentHomography?: { currentHomography?: {
__typename?: "HomographyInfoGQL"; __typename?: "HomographyInfoGQL";
id: number; id?: number | null;
frameIndex: number; frameIndex: number;
crop: { crop: {
__typename?: "BoundingBoxGQL"; __typename?: "BoundingBoxGQL";
@@ -8148,7 +8300,7 @@ export type GetVideoQuery = {
} | null; } | null;
homographyHistory: Array<{ homographyHistory: Array<{
__typename?: "HomographyInfoGQL"; __typename?: "HomographyInfoGQL";
id: number; id?: number | null;
frameIndex: number; frameIndex: number;
crop: { crop: {
__typename?: "BoundingBoxGQL"; __typename?: "BoundingBoxGQL";
@@ -8396,7 +8548,7 @@ export type FindPrerecordTableLayoutMutation = {
__typename?: "Mutation"; __typename?: "Mutation";
findPrerecordTableLayout?: { findPrerecordTableLayout?: {
__typename?: "HomographyInfoGQL"; __typename?: "HomographyInfoGQL";
id: number; id?: number | null;
frameIndex: number; frameIndex: number;
crop: { crop: {
__typename?: "BoundingBoxGQL"; __typename?: "BoundingBoxGQL";
@@ -8426,7 +8578,7 @@ export type FindPrerecordTableLayoutMutation = {
export type HomographyInfoFragment = { export type HomographyInfoFragment = {
__typename?: "HomographyInfoGQL"; __typename?: "HomographyInfoGQL";
id: number; id?: number | null;
frameIndex: number; frameIndex: number;
crop: { crop: {
__typename?: "BoundingBoxGQL"; __typename?: "BoundingBoxGQL";
@@ -14417,6 +14569,9 @@ export const GetTableStateDocument = gql`
useHomography: $useHomography useHomography: $useHomography
) { ) {
identifierToPosition identifierToPosition
identifierToColor {
hex
}
} }
} }
`; `;
@@ -14495,6 +14650,9 @@ export const GetLiveTableStateDocument = gql`
frameIndex frameIndex
tableState { tableState {
identifierToPosition identifierToPosition
identifierToColor {
hex
}
homography { homography {
...HomographyInfo ...HomographyInfo
} }
@@ -15646,6 +15804,119 @@ export type DeleteTagsMutationOptions = Apollo.BaseMutationOptions<
DeleteTagsMutation, DeleteTagsMutation,
DeleteTagsMutationVariables DeleteTagsMutationVariables
>; >;
export const GetUsageStatsDocument = gql`
query getUsageStats($days: Int! = 30) {
getUsageStats(days: $days) {
funnel {
freemiumEpoch
eraSignups
eraPaying
activationCohort
activationActivated
payingUsers
newPaid7d
newPaid30d
retentionPrevActive
retentionReturned
}
cohorts {
weekStart
signups
activated7d
paid7d
activatedNotPaid7d
}
totalUsers
totalVideos
totalShots
totalRuns
windows {
label
newUsers
activeUsers
sessions
shots
}
daily {
day
newUsers
activeUsers
sessions
shots
}
processingLast24h {
status
count
}
}
}
`;
/**
* __useGetUsageStatsQuery__
*
* To run a query within a React component, call `useGetUsageStatsQuery` and pass it any options that fit your needs.
* When your component renders, `useGetUsageStatsQuery` 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 } = useGetUsageStatsQuery({
* variables: {
* days: // value for 'days'
* },
* });
*/
export function useGetUsageStatsQuery(
baseOptions?: Apollo.QueryHookOptions<
GetUsageStatsQuery,
GetUsageStatsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<GetUsageStatsQuery, GetUsageStatsQueryVariables>(
GetUsageStatsDocument,
options,
);
}
export function useGetUsageStatsLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetUsageStatsQuery,
GetUsageStatsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<GetUsageStatsQuery, GetUsageStatsQueryVariables>(
GetUsageStatsDocument,
options,
);
}
export function useGetUsageStatsSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetUsageStatsQuery,
GetUsageStatsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetUsageStatsQuery,
GetUsageStatsQueryVariables
>(GetUsageStatsDocument, options);
}
export type GetUsageStatsQueryHookResult = ReturnType<
typeof useGetUsageStatsQuery
>;
export type GetUsageStatsLazyQueryHookResult = ReturnType<
typeof useGetUsageStatsLazyQuery
>;
export type GetUsageStatsSuspenseQueryHookResult = ReturnType<
typeof useGetUsageStatsSuspenseQuery
>;
export type GetUsageStatsQueryResult = Apollo.QueryResult<
GetUsageStatsQuery,
GetUsageStatsQueryVariables
>;
export const GetProfileImageUploadLinkDocument = gql` export const GetProfileImageUploadLinkDocument = gql`
mutation getProfileImageUploadLink($fileExt: String = ".png") { mutation getProfileImageUploadLink($fileExt: String = ".png") {
getProfileImageUploadLink(fileExt: $fileExt) { getProfileImageUploadLink(fileExt: $fileExt) {

View File

@@ -9,6 +9,9 @@ query GetTableState(
useHomography: $useHomography useHomography: $useHomography
) { ) {
identifierToPosition identifierToPosition
identifierToColor {
hex
}
} }
} }
@@ -18,6 +21,9 @@ query GetLiveTableState($videoId: Int!) {
frameIndex frameIndex
tableState { tableState {
identifierToPosition identifierToPosition
identifierToColor {
hex
}
homography { homography {
...HomographyInfo ...HomographyInfo
} }

View File

@@ -0,0 +1,45 @@
query getUsageStats($days: Int! = 30) {
getUsageStats(days: $days) {
funnel {
freemiumEpoch
eraSignups
eraPaying
activationCohort
activationActivated
payingUsers
newPaid7d
newPaid30d
retentionPrevActive
retentionReturned
}
cohorts {
weekStart
signups
activated7d
paid7d
activatedNotPaid7d
}
totalUsers
totalVideos
totalShots
totalRuns
windows {
label
newUsers
activeUsers
sessions
shots
}
daily {
day
newUsers
activeUsers
sessions
shots
}
processingLast24h {
status
count
}
}
}

View File

@@ -73,6 +73,7 @@ type Query {
useHomography: HomographyInputGQL = null useHomography: HomographyInputGQL = null
): TableStateGQL! ): TableStateGQL!
getLiveTableState(videoId: Int!): LiveTableStateGQL! getLiveTableState(videoId: Int!): LiveTableStateGQL!
getShotTableState(shotId: Int!): ShotTableStateGQL!
simulateShot(simulationInput: SimulateShotInputGQL!): ShotProjectionGQL! simulateShot(simulationInput: SimulateShotInputGQL!): ShotProjectionGQL!
computePotAim( computePotAim(
simulationInput: SimulateShotInputGQL! simulationInput: SimulateShotInputGQL!
@@ -100,6 +101,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
@@ -608,7 +610,7 @@ type VideoTagClass {
} }
type HomographyInfoGQL { type HomographyInfoGQL {
id: Int! id: Int
frameIndex: Int! frameIndex: Int!
crop: BoundingBoxGQL! crop: BoundingBoxGQL!
pockets: [BoundingBoxGQL!]! pockets: [BoundingBoxGQL!]!
@@ -1020,6 +1022,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 {
@@ -1056,6 +1066,13 @@ type LiveTableStateGQL {
tableState: TableStateGQL! tableState: TableStateGQL!
} }
type ShotTableStateGQL {
shotId: Int!
videoId: Int!
frameIndex: Int!
tableState: TableStateGQL!
}
type ShotProjectionGQL { type ShotProjectionGQL {
trajectories: [BallTrajectoryGQL!]! trajectories: [BallTrajectoryGQL!]!
events: [SimulationEventGQL!]! events: [SimulationEventGQL!]!
@@ -1160,6 +1177,60 @@ input CreatedAfter @oneOf {
createdAt: DateTime createdAt: DateTime
} }
type UsageStatsGQL {
funnel: FunnelStatsGQL!
totalUsers: Int!
totalVideos: Int!
totalShots: Int!
totalRuns: Int!
cohorts: [CohortStatsGQL!]!
windows: [UsageStatsWindowGQL!]!
daily: [UsageStatsDailyGQL!]!
processingLast24h: [ProcessingStatusCountGQL!]!
}
type FunnelStatsGQL {
freemiumEpoch: Date!
eraSignups: Int!
eraPaying: Int!
activationCohort: Int!
activationActivated: Int!
payingUsers: Int!
newPaid7d: Int!
newPaid30d: Int!
retentionPrevActive: Int!
retentionReturned: Int!
}
type CohortStatsGQL {
weekStart: Date!
signups: Int!
activated7d: Int!
paid7d: Int!
activatedNotPaid7d: Int!
}
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!]!