diff --git a/vocodes/src/common/ApiConfig.ts b/vocodes/src/common/ApiConfig.ts index 2e1fef83b0..1cacd7924c 100644 --- a/vocodes/src/common/ApiConfig.ts +++ b/vocodes/src/common/ApiConfig.ts @@ -258,6 +258,14 @@ class ApiConfig { return `${this.getScheme()}://${this.getNewApiHost()}/moderation/user/list`; } + getTtsInferenceStats() : string { + return `${this.getScheme()}://${this.getNewApiHost()}/moderation/jobs/tts_inference_queue_stats`; + } + + getW2lInferenceStats() : string { + return `${this.getScheme()}://${this.getNewApiHost()}/moderation/jobs/w2l_inference_queue_stats`; + } + private getScheme() : string { return this.isLocalDev ? "http" : "https"; } diff --git a/vocodes/src/v2/api/moderation/GetTtsInferenceStats.ts b/vocodes/src/v2/api/moderation/GetTtsInferenceStats.ts new file mode 100644 index 0000000000..0bff4d4859 --- /dev/null +++ b/vocodes/src/v2/api/moderation/GetTtsInferenceStats.ts @@ -0,0 +1,63 @@ +import { ApiConfig } from "../../../common/ApiConfig"; + +export interface TtsInferenceStats { + success: boolean, + pending_count: number, +} + +export enum TtsInferenceStatsError { + NotAuthorized, + ServerError, + FrontendError, +} + +export type GetTtsInferenceStatsResponse = TtsInferenceStats | TtsInferenceStatsError; + +export function GetTtsInferenceStatsIsOk(response: GetTtsInferenceStatsResponse): response is TtsInferenceStats { + return response.hasOwnProperty('pending_count'); +} + +export function GetTtsInferenceStatsIsErr(response: GetTtsInferenceStatsResponse): response is TtsInferenceStatsError { + return !response.hasOwnProperty('pending_count'); +} + +interface TtsInferenceStatsResponsePayload { + success: boolean, + error_reason?: string, + pending_count?: number, +} + + +export async function GetTtsInferenceStats() : Promise +{ + const endpoint = new ApiConfig().getTtsInferenceStats(); + + return await fetch(endpoint, { + method: 'GET', + headers: { + 'Accept': 'application/json', + }, + credentials: 'include', + }) + .then(res => res.json()) + .then(res => { + const response : TtsInferenceStatsResponsePayload = res; + + if (response?.success) { + return res as TtsInferenceStats; // TODO: This is not the way. + } + + if (response?.success === false) { + if (response.error_reason?.includes("authorized")) { + return TtsInferenceStatsError.NotAuthorized; + } else { + return TtsInferenceStatsError.ServerError; + } + } + + return TtsInferenceStatsError.FrontendError; + }) + .catch(e => { + return TtsInferenceStatsError.FrontendError; + }); +} diff --git a/vocodes/src/v2/api/moderation/GetUserList.ts b/vocodes/src/v2/api/moderation/GetUserList.ts index c426455689..c127e5570a 100644 --- a/vocodes/src/v2/api/moderation/GetUserList.ts +++ b/vocodes/src/v2/api/moderation/GetUserList.ts @@ -1,8 +1,5 @@ import { ApiConfig } from "../../../common/ApiConfig"; -//export interface GetUserListRequest { -//} - export interface GetUserListSuccessResponse { success: boolean, users: Array @@ -53,7 +50,7 @@ export async function GetUserList() : Promise .then(res => res.json()) .then(res => { if (!res) { - return { success : false }; + return { success : false }; // TODO: This loses error semantics and is deprecated } if (res && 'success' in res) { diff --git a/vocodes/src/v2/api/moderation/GetW2lInferenceStats.ts b/vocodes/src/v2/api/moderation/GetW2lInferenceStats.ts new file mode 100644 index 0000000000..059f13e8ce --- /dev/null +++ b/vocodes/src/v2/api/moderation/GetW2lInferenceStats.ts @@ -0,0 +1,64 @@ +import { ApiConfig } from "../../../common/ApiConfig"; + +export interface W2lInferenceStats { + success: boolean, + pending_count: number, +} + +export enum W2lInferenceStatsError { + NotAuthorized, + ServerError, + FrontendError, +} + +export type GetW2lInferenceStatsResponse = W2lInferenceStats | W2lInferenceStatsError; + +export function GetW2lInferenceStatsIsOk(response: GetW2lInferenceStatsResponse): response is W2lInferenceStats { + return response.hasOwnProperty('pending_count'); +} + +export function GetW2lInferenceStatsIsErr(response: GetW2lInferenceStatsResponse): response is W2lInferenceStatsError { + return !response.hasOwnProperty('pending_count'); +} + +interface W2lInferenceStatsResponsePayload { + success: boolean, + error_reason?: string, + pending_count?: number, +} + + +export async function GetW2lInferenceStats() : Promise +{ + const endpoint = new ApiConfig().getW2lInferenceStats(); + + return await fetch(endpoint, { + method: 'GET', + headers: { + 'Accept': 'application/json', + }, + credentials: 'include', + }) + .then(res => res.json()) + .then(res => { + const response : W2lInferenceStatsResponsePayload = res; + + if (response?.success) { + return res as W2lInferenceStats; // TODO: This is not the way. + } + + if (response?.success === false) { + if (response.error_reason?.includes("authorized")) { + return W2lInferenceStatsError.NotAuthorized; + } else { + return W2lInferenceStatsError.ServerError; + } + } + + return W2lInferenceStatsError.FrontendError; + }) + .catch(e => { + return W2lInferenceStatsError.FrontendError; + }); +} + diff --git a/vocodes/src/v2/view/NewVocodesContainer.tsx b/vocodes/src/v2/view/NewVocodesContainer.tsx index 8aa49ef418..6cb6478dfa 100644 --- a/vocodes/src/v2/view/NewVocodesContainer.tsx +++ b/vocodes/src/v2/view/NewVocodesContainer.tsx @@ -39,6 +39,7 @@ import { TtsModelListItem } from '../api/tts/ListTtsModels'; import { ProfileBanFc } from './profile/profile_ban/ProfileBanFc'; import { ModerationUserListFc } from './moderation/moderation_user_list/ModerationUserList'; import { LeaderboardFc } from './leaderboard/LeaderboardFc'; +import { ModerationJobStatsFc } from './moderation/moderation_job_stats/ModerationJobStatsFc'; interface Props { sessionWrapper: SessionWrapper, @@ -278,6 +279,12 @@ class NewVocodesContainer extends React.Component { /> + + + + , } -/* -event_token "EV:YR8HA4VCVYX9JMH5AMDFHTNRGSYGD" -event_type "w2l_template_upload_completed" -maybe_target_user_token "U:WNZCXKV1AN7XD" -maybe_target_username "brandon" -maybe_target_display_name "brandon" -maybe_target_entity_token "W2L_TPL:MJZ2SMCXX9DRNZ249VK70TWJ" -created_at "2021-05-30T06:42:39Z" -updated_at "2021-05-30T06:42:39Z" -*/ interface FirehoseEvent { event_token: string, event_type: string, diff --git a/vocodes/src/v2/view/moderation/moderation_job_stats/ModerationJobStatsFc.tsx b/vocodes/src/v2/view/moderation/moderation_job_stats/ModerationJobStatsFc.tsx new file mode 100644 index 0000000000..74c433be92 --- /dev/null +++ b/vocodes/src/v2/view/moderation/moderation_job_stats/ModerationJobStatsFc.tsx @@ -0,0 +1,74 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { SessionWrapper } from '../../../../session/SessionWrapper'; +import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig'; +import { BackLink } from '../../_common/BackLink'; +import { GetTtsInferenceStats, GetTtsInferenceStatsIsOk } from '../../../api/moderation/GetTtsInferenceStats'; +import { GetW2lInferenceStats, GetW2lInferenceStatsIsOk } from '../../../api/moderation/GetW2lInferenceStats'; + +interface Props { + sessionWrapper: SessionWrapper, +} + +function ModerationJobStatsFc(props: Props) { + const [ttsPendingJobCount, setTtsPendingJobCount] = useState(-1); + const [w2lPendingJobCount, setW2lPendingJobCount] = useState(-1); + + const getTtsStats = useCallback(async () => { + const response = await GetTtsInferenceStats(); + if (GetTtsInferenceStatsIsOk(response)) { + setTtsPendingJobCount(response.pending_count); + } + }, []); + + const getW2lStats = useCallback(async () => { + const response = await GetW2lInferenceStats(); + if (GetW2lInferenceStatsIsOk(response)) { + setW2lPendingJobCount(response.pending_count); + } + }, []); + + + useEffect(() => { + getTtsStats(); + getW2lStats(); + }, [getTtsStats, getW2lStats]); + + + if (ttsPendingJobCount === -1 && w2lPendingJobCount === -1) { + return
+ } + + if (!props.sessionWrapper.canBanUsers()) { + return

Unauthorized

; + } + + return ( +
+

Job Stats

+ + + +
+
+ + + + + + + + + + + + +
+ Pending TTS Inference Jobs + {ttsPendingJobCount}
+ Pending W2L Inference Jobs + {w2lPendingJobCount}
+
+ ) +} + +export { ModerationJobStatsFc }; diff --git a/vocodes/src/v2/view/moderation/moderation_main/ModerationFc.tsx b/vocodes/src/v2/view/moderation/moderation_main/ModerationFc.tsx index 485e85bc9a..a67f43a1c6 100644 --- a/vocodes/src/v2/view/moderation/moderation_main/ModerationFc.tsx +++ b/vocodes/src/v2/view/moderation/moderation_main/ModerationFc.tsx @@ -18,18 +18,25 @@ function ModerationFc(props: Props) { User List
IP Ban List
+ Job Stats + +
+

More mod controls will be added in the future: user roles, activity tracking, timed bans, account bans, etc.