Edit TTS models

This commit is contained in:
Brandon Thomas committed 2021-06-30 17:51:08 -04:00
1 parent 07c3e0be61
commit 139d1f1c17
6 files changed
+238 -162

No files matched your search

+6 -2
View File
@@ -64,8 +64,12 @@ class ApiConfig {
return `${this.getScheme()}://${this.getNewApiHost()}/tts/model/${modelToken}/delete`;
}
getTtsModelUseCount(modelSlug: string) : string {
return `${this.getScheme()}://${this.getNewApiHost()}/tts/model/${modelSlug}/count`;
editTtsModel(modelToken: string) : string {
return `${this.getScheme()}://${this.getNewApiHost()}/tts/model/${modelToken}/edit`;
}
getTtsModelUseCount(modelToken: string) : string {
return `${this.getScheme()}://${this.getNewApiHost()}/tts/model/${modelToken}/count`;
}
viewTtsInferenceResult(token: string) : string {
+8
View File
@@ -51,6 +51,10 @@ export class SessionWrapper {
return this.canEditOtherUsersProfiles();
}
public canEditTtsModelByUserToken(userToken: MaybeString) : boolean {
return this.canEditOtherUsersTtsModels() || this.verifyUserTokenMatch(userToken);
}
public canDeleteTtsModelByUserToken(userToken: MaybeString) : boolean {
return this.canDeleteOtherUsersTtsModels() || this.verifyUserTokenMatch(userToken);
}
@@ -95,6 +99,10 @@ export class SessionWrapper {
return this.sessionStateResponse?.user?.can_edit_other_users_profiles || false;
}
public canEditOtherUsersTtsModels() : boolean {
return this.sessionStateResponse?.user?.can_edit_other_users_tts_models || false;
}
public canDeleteOtherUsersTtsModels() : boolean {
return this.sessionStateResponse?.user?.can_delete_other_users_tts_models || false;
}
+9
View File
@@ -27,6 +27,7 @@ import { W2lTemplateViewFc } from './w2l/w2l_template_view/W2lTemplateViewFc';
import { ModerationViewIpBanFc } from './moderation/moderation_view_ip_ban/ModerationViewIpBanFc';
import { AboutFc } from './about/about_page/AboutFc';
import { TermsFc } from './about/terms_page/TermsFc';
import { TtsModelEditFc } from './tts/tts_model_edit/TtsModelEditFc';
interface Props {
sessionWrapper: SessionWrapper,
@@ -114,6 +115,14 @@ class NewVocodesContainer extends React.Component<Props, State> {
/>
</Route>
<Route path="/tts/:token/edit">
<TtsModelEditFc
sessionWrapper={this.props.sessionWrapper}
enqueueTtsJob={this.props.enqueueTtsJob}
ttsInferenceJobs={this.props.ttsInferenceJobs}
/>
</Route>
<Route path="/tts/:token">
<TtsModelViewFc
sessionWrapper={this.props.sessionWrapper}
@@ -0,0 +1,198 @@
import React, { useState, useEffect, useCallback } from 'react';
//import axios from 'axios';
import { ApiConfig } from '../../../common/ApiConfig';
import { SessionWrapper } from '../../../session/SessionWrapper';
import { TtsInferenceJob } from '../../../App';
import { useParams, Link, useHistory } from 'react-router-dom';
interface TtsModelViewResponsePayload {
success: boolean,
model: TtsModel,
}
interface TtsModel {
model_token: string,
title: string,
tts_model_type: string,
text_preprocessing_algorithm: string,
creator_user_token: string,
creator_username: string,
creator_display_name: string,
description_markdown: string,
description_rendered_html: string,
updatable_slug: string,
created_at: string,
updated_at: string,
maybe_moderator_fields: TtsModelModeratorFields | null | undefined,
}
interface TtsModelModeratorFields {
creator_ip_address_creation: string,
creator_ip_address_last_update: string,
mod_deleted_at: string | undefined | null,
user_deleted_at: string | undefined | null,
}
interface Props {
sessionWrapper: SessionWrapper,
enqueueTtsJob: (jobToken: string) => void,
ttsInferenceJobs: Array<TtsInferenceJob>,
}
function TtsModelEditFc(props: Props) {
let { token } = useParams() as { token : string };
const history = useHistory();
const [ttsModel, setTtsModel] = useState<TtsModel|undefined>(undefined);
const [title, setTitle] = useState<string>("");
const [descriptionMarkdown, setDescriptionMarkdown] = useState<string>("");
const getModel = useCallback((token) => {
const api = new ApiConfig();
const endpointUrl = api.viewTtsModel(token);
fetch(endpointUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include',
})
.then(res => res.json())
.then(res => {
const modelsResponse : TtsModelViewResponsePayload = res;
if (!modelsResponse.success) {
return;
}
setTitle(modelsResponse.model.title || "")
setDescriptionMarkdown(modelsResponse.model.description_markdown || "")
setTtsModel(modelsResponse.model);
})
.catch(e => {});
}, []);
useEffect(() => {
getModel(token);
}, [token, getModel]);
const handleTitleChange = (ev: React.FormEvent<HTMLInputElement>) => {
ev.preventDefault();
const textValue = (ev.target as HTMLInputElement).value;
setTitle(textValue);
return false;
};
const handleDescriptionMarkdownChange = (ev: React.FormEvent<HTMLTextAreaElement>) => {
ev.preventDefault();
const textValue = (ev.target as HTMLTextAreaElement).value;
setDescriptionMarkdown(textValue);
return false;
};
const modelLink = `/tts/${token}`;
const handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) => {
ev.preventDefault();
if (ttsModel === undefined) {
return false;
}
if (title.trim() === "") {
return false;
}
const modelToken = ttsModel!.model_token;
const api = new ApiConfig();
const endpointUrl = api.editTtsModel(modelToken);
const request = {
title: title,
description_markdown: descriptionMarkdown,
}
fetch(endpointUrl, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify(request),
})
.then(res => res.json())
.then(res => {
if (res === undefined ||
!res.success) {
return; // Endpoint error?
}
history.push(modelLink);
})
.catch(e => {
});
return false;
};
let isDisabled = ttsModel === undefined;
return (
<div>
<h1 className="title is-1"> Edit Model </h1>
<Link to={modelLink}>Back to model</Link>
<form onSubmit={handleFormSubmit}>
<fieldset disabled={isDisabled}>
<div className="field">
<label className="label">Model Title</label>
<div className="control has-icons-left has-icons-right">
<input
onChange={handleTitleChange}
className="input"
type="text"
placeholder="Model Title"
value={title}
/>
<span className="icon is-small is-left">
<i className="fas fa-envelope"></i>
</span>
<span className="icon is-small is-right">
<i className="fas fa-exclamation-triangle"></i>
</span>
</div>
{/*<p className="help">{invalidReason}</p>*/}
</div>
<div className="field">
<label className="label">Description (supports Markdown)</label>
<div className="control">
<textarea
onChange={handleDescriptionMarkdownChange}
className="textarea is-large"
placeholder="Model description (ie. source of data, training duration, etc)"
value={descriptionMarkdown}
/>
</div>
</div>
<br />
<button className="button is-link is-large is-fullwidth">Update</button>
</fieldset>
</form>
</div>
)
}
export { TtsModelEditFc };
@@ -1,160 +0,0 @@
import React from 'react';
import { ApiConfig } from '../../../common/ApiConfig';
import { Mode } from '../../../AppMode';
import { SessionWrapper } from '../../../session/SessionWrapper';
enum FieldTriState {
EMPTY_FALSE,
FALSE,
TRUE,
}
interface Props {
sessionWrapper: SessionWrapper,
querySessionCallback : () => void,
switchModeCallback?: (mode: Mode) => void,
}
interface State {
usernameOrEmail: string,
usernameOrEmailValid: FieldTriState,
usernameOrEmailInvalidReason: string,
password: string,
passwordValid: FieldTriState,
passwordInvalidReason: string,
}
class TtsModelViewComponent extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
usernameOrEmail: "",
usernameOrEmailValid: FieldTriState.EMPTY_FALSE,
usernameOrEmailInvalidReason: "",
password: "",
passwordValid: FieldTriState.EMPTY_FALSE,
passwordInvalidReason: "",
};
}
handleUsernameOrEmailChange = (ev: React.FormEvent<HTMLInputElement>) => {
ev.preventDefault();
const usernameOrEmail = (ev.target as HTMLInputElement).value;
this.setState({
usernameOrEmail: usernameOrEmail,
})
return false;
}
handlePasswordChange = (ev: React.FormEvent<HTMLInputElement>) => {
ev.preventDefault();
const password = (ev.target as HTMLInputElement).value;
this.setState({
password: password,
})
return false;
}
handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) : boolean => {
ev.preventDefault();
const api = new ApiConfig();
const endpointUrl = api.login();
const request = {
username_or_email: this.state.usernameOrEmail,
password: this.state.password,
}
fetch(endpointUrl, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify(request),
})
.then(res => res.json())
.then(res => {
console.log('login response', res)
if (res.success) {
console.log('querying new session');
this.props.querySessionCallback();
if (this.props.switchModeCallback !== undefined) {
this.props.switchModeCallback(Mode.SPEAK_MODE);
}
return;
}
})
.catch(e => {
//this.props.onSpeakErrorCallback();
});
return false;
}
public render() {
if (this.props.sessionWrapper.isLoggedIn()) {
return <div>Invalid view for logged in users.</div>;
}
return (
<div>
<h1 className="title is-1"> Login </h1>
<p></p>
<form onSubmit={this.handleFormSubmit}>
<div className="field">
<label className="label">Username or Email</label>
<div className="control has-icons-left has-icons-right">
<input className="input" type="text" placeholder="Username or Email" value={this.state.usernameOrEmail} onChange={this.handleUsernameOrEmailChange} />
<span className="icon is-small is-left">
<i className="fas fa-user"></i>
</span>
<span className="icon is-small is-right">
<i className="fas fa-check"></i>
</span>
</div>
{/*<p className="help"></p>*/}
</div>
<div className="field">
<label className="label">Password</label>
<div className="control has-icons-left has-icons-right">
<input className="input" type="password" placeholder="Password" value={this.state.password} onChange={this.handlePasswordChange} />
<span className="icon is-small is-left">
<i className="fas fa-envelope"></i>
</span>
<span className="icon is-small is-right">
<i className="fas fa-exclamation-triangle"></i>
</span>
</div>
{/*<p className="help"></p>*/}
</div>
<br />
<div className="field is-grouped">
<div className="control">
<button className="button is-link is-large">Login</button>
</div>
</div>
</form>
</div>
)
}
}
export { TtsModelViewComponent };
@@ -27,6 +27,8 @@ interface TtsModel {
creator_user_token: string,
creator_username: string,
creator_display_name: string,
description_markdown: string,
description_rendered_html: string,
updatable_slug: string,
created_at: string,
updated_at: string,
@@ -198,9 +200,24 @@ function TtsModelViewFc(props: Props) {
);
}
let editModelButton = <span />
if (!!ttsModel?.creator_user_token) {
if (props.sessionWrapper.canEditTtsModelByUserToken(ttsModel.creator_user_token)) {
let editLinkUrl = `/tts/${token}/edit`;
editModelButton = (
<Link
className={"button is-medium is-info"}
to={editLinkUrl}>Edit</Link>
);
}
}
return (
<div>
<h1 className="title is-1"> {title} </h1>
{editModelButton}
<table className="table">
<thead>