feat: track five most recent project models (#395)

* feat: track most recent 5 models for project

* refactor: set recent model records count in constants

* style: remove empty line whitespaces

* style: remove eol whitespaces
This commit is contained in:
stew-ro 2020-07-09 11:59:08 -07:00 коммит произвёл GitHub
Родитель abc63767e9
Коммит 05850603d5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 63 добавлений и 26 удалений

Просмотреть файл

@ -32,7 +32,7 @@ export const constants = {
convertedImageFormat: "image/jpeg", convertedImageFormat: "image/jpeg",
convertedImageQuality: 0.7, convertedImageQuality: 0.7,
convertedThumbnailQuality: 0.2, convertedThumbnailQuality: 0.2,
recentModelRecordsCount: 5,
apiModelsPath: `/formrecognizer/${apiVersion}/custom/models`, apiModelsPath: `/formrecognizer/${apiVersion}/custom/models`,
pdfjsWorkerSrc(version: string) { pdfjsWorkerSrc(version: string) {

Просмотреть файл

@ -147,6 +147,7 @@ export default class MockFactory {
apiUriBase: "localhost", apiUriBase: "localhost",
folderPath: "", folderPath: "",
trainRecord: null, trainRecord: null,
recentModelRecords: [],
predictModelId: "", predictModelId: "",
}; };
} }

Просмотреть файл

@ -94,6 +94,7 @@ export interface IProject {
apiKey?: string | ISecureString; apiKey?: string | ISecureString;
folderPath: string; folderPath: string;
trainRecord: ITrainRecordProps; trainRecord: ITrainRecordProps;
recentModelRecords: IRecentModel[];
predictModelId: string; predictModelId: string;
} }
@ -281,6 +282,18 @@ export interface IFieldInfo {
fields: IField[]; fields: IField[];
} }
export interface IRecentModel {
readonly composedTrainResults?: object;
readonly accuracies?: object;
readonly averageAccuracy?: number;
readonly modelInfo: {
readonly isComposed: boolean;
readonly modelId: string;
readonly createdDateTime: string;
readonly modelName: string;
};
}
/** /**
* Enum of supported error codes * Enum of supported error codes
*/ */

Просмотреть файл

@ -5,7 +5,7 @@ import React from "react";
import {connect} from "react-redux"; import {connect} from "react-redux";
import url from "url"; import url from "url";
import { RouteComponentProps } from "react-router-dom"; import { RouteComponentProps } from "react-router-dom";
import { IProject, IConnection, IAppSettings, IApplicationState, AppError, ErrorCode } from "../../../../models/applicationState"; import { IProject, IConnection, IAppSettings, IApplicationState, AppError, ErrorCode, IRecentModel } from "../../../../models/applicationState";
import { constants } from "../../../../common/constants"; import { constants } from "../../../../common/constants";
import ServiceHelper from "../../../../services/serviceHelper"; import ServiceHelper from "../../../../services/serviceHelper";
import { IColumn, import { IColumn,
@ -341,10 +341,8 @@ export default class ModelComposePage extends React.Component<IModelComposePageP
const composedModels = this.reloadComposedModel(this.state.composedModelList); const composedModels = this.reloadComposedModel(this.state.composedModelList);
let composedModelIds = []; let composedModelIds = [];
let predictModelFlag = false;
if (this.state.composeModelId.length !== 0) { if (this.state.composeModelId.length !== 0) {
composedModelIds = this.getComposedIds(); composedModelIds = this.getComposedIds();
predictModelFlag = true;
if (composedModelIds.indexOf(this.state.composeModelId[0]) === -1) { if (composedModelIds.indexOf(this.state.composeModelId[0]) === -1) {
const idURL = constants.apiModelsPath + "/" + this.state.composeModelId[0]; const idURL = constants.apiModelsPath + "/" + this.state.composeModelId[0];
composedModels.push(await this.getComposeModelByURl(idURL)); composedModels.push(await this.getComposeModelByURl(idURL));
@ -359,10 +357,6 @@ export default class ModelComposePage extends React.Component<IModelComposePageP
models = models.filter((m) => composedModelIds.indexOf(m.modelId) === -1); models = models.filter((m) => composedModelIds.indexOf(m.modelId) === -1);
this.allModels = composedModels.concat(models); this.allModels = composedModels.concat(models);
if (predictModelFlag) {
const updatedProject = this.buildUpdatedProject(this.state.composeModelId[0]);
await this.props.actions.saveProject(updatedProject, false, false);
}
this.setState({ this.setState({
modelList: this.allModels, modelList: this.allModels,
nextLink: link, nextLink: link,
@ -377,10 +371,27 @@ export default class ModelComposePage extends React.Component<IModelComposePageP
} }
} }
private buildUpdatedProject = (modelId: string): IProject => { private buildUpdatedProject = (composedModel: object): IProject => {
const newTrainRecord = {
modelInfo: {
createdDateTime: composedModel["modelInfo"]["createdDateTime"],
modelId: composedModel["modelInfo"]["modelId"],
modelName: composedModel["modelInfo"]["modelName"],
isComposed: true,
},
composedTrainResults: composedModel["composedTrainResults"]
} as IRecentModel;
const recentModelRecords: IRecentModel[] = this.props.project.recentModelRecords ?
[...this.props.project.recentModelRecords] : [];
recentModelRecords.unshift(newTrainRecord);
if (recentModelRecords.length > constants.recentModelRecordsCount) {
recentModelRecords.pop();
}
return { return {
...this.props.project, ...this.props.project,
predictModelId: modelId, recentModelRecords,
predictModelId: newTrainRecord.modelInfo.modelId,
}; };
} }
@ -399,7 +410,7 @@ export default class ModelComposePage extends React.Component<IModelComposePageP
private getComposeModelByURl = async (idURL) => { private getComposeModelByURl = async (idURL) => {
const composedRes = await this.getResponse(idURL); const composedRes = await this.getResponse(idURL);
const composedModel: IModel = composedRes.data.modelInfo; const composedModel: IModel = composedRes.data.modelInfo;
composedModel.iconName = "combine"; composedModel.iconName = "Combine";
composedModel.key = composedModel.modelId; composedModel.key = composedModel.modelId;
return composedModel; return composedModel;
} }
@ -632,18 +643,23 @@ export default class ModelComposePage extends React.Component<IModelComposePageP
modelIds: idList, modelIds: idList,
modelName: name, modelName: name,
}; };
const link = constants.apiModelsPath + "/compose"; const link = constants.apiModelsPath + "/compose";
const composeRes = await this.post(link, payload); const composeRes = await this.post(link, payload);
await this.waitUntilModelIsReady(composeRes["headers"]["location"]); const composedModel = await this.waitUntilModelIsReady(composeRes["headers"]["location"]);
const composedModelId = this.getComposeModelId(composeRes);
const updatedProject = this.buildUpdatedProject(composedModel);
await this.props.actions.saveProject(updatedProject, false, false);
const newCols = this.state.columns; const newCols = this.state.columns;
newCols.forEach((ncol) => { newCols.forEach((ncol) => {
ncol.isSorted = false; ncol.isSorted = false;
ncol.isSortedDescending = true; ncol.isSortedDescending = true;
}); });
this.setState({ this.setState({
isComposing: false, isComposing: false,
composeModelId: [composedModelId], composeModelId: [composedModel["modelInfo"]["modelId"]],
columns: newCols, columns: newCols,
}); });
} catch (error) { } catch (error) {
@ -655,13 +671,6 @@ export default class ModelComposePage extends React.Component<IModelComposePageP
}, 5000); }, 5000);
} }
/** get the model Id of new composed model */
private getComposeModelId = (composeRes: any): string => {
const location = composeRes["headers"]["location"];
const splitGroup = location.split("/");
return splitGroup[splitGroup.length - 1];
}
private async post(link, payload): Promise<any> { private async post(link, payload): Promise<any> {
const baseURL = url.resolve( const baseURL = url.resolve(
this.props.project.apiUriBase, this.props.project.apiUriBase,

Просмотреть файл

@ -10,7 +10,7 @@ import IProjectActions, * as projectActions from "../../../../redux/actions/proj
import IApplicationActions, * as applicationActions from "../../../../redux/actions/applicationActions"; import IApplicationActions, * as applicationActions from "../../../../redux/actions/applicationActions";
import IAppTitleActions, * as appTitleActions from "../../../../redux/actions/appTitleActions"; import IAppTitleActions, * as appTitleActions from "../../../../redux/actions/appTitleActions";
import { import {
IApplicationState, IConnection, IProject, IAppSettings, FieldType, IApplicationState, IConnection, IProject, IAppSettings, FieldType, IRecentModel,
} from "../../../../models/applicationState"; } from "../../../../models/applicationState";
import TrainChart from "./trainChart"; import TrainChart from "./trainChart";
import TrainPanel from "./trainPanel"; import TrainPanel from "./trainPanel";
@ -327,8 +327,16 @@ export default class TrainPage extends React.Component<ITrainPageProps, ITrainPa
} }
private buildUpdatedProject = (newTrainRecord: ITrainRecordProps): IProject => { private buildUpdatedProject = (newTrainRecord: ITrainRecordProps): IProject => {
const recentModelRecords: IRecentModel[] = this.props.project.recentModelRecords ?
[...this.props.project.recentModelRecords] : [];
recentModelRecords.unshift({...newTrainRecord, isComposed: false} as IRecentModel);
if (recentModelRecords.length > constants.recentModelRecordsCount) {
recentModelRecords.pop();
}
return { return {
...this.props.project, ...this.props.project,
recentModelRecords,
trainRecord: newTrainRecord, trainRecord: newTrainRecord,
predictModelId: newTrainRecord.modelInfo.modelId, predictModelId: newTrainRecord.modelInfo.modelId,
}; };
@ -356,6 +364,7 @@ export default class TrainPage extends React.Component<ITrainPageProps, ITrainPa
modelId: response["modelInfo"]["modelId"], modelId: response["modelInfo"]["modelId"],
createdDateTime: response["modelInfo"]["createdDateTime"], createdDateTime: response["modelInfo"]["createdDateTime"],
modelName: response["modelInfo"]["modelName"], modelName: response["modelInfo"]["modelName"],
isComposed: false,
}, },
averageAccuracy: response["trainResult"]["averageModelAccuracy"], averageAccuracy: response["trainResult"]["averageModelAccuracy"],
accuracies: this.buildAccuracies(response["trainResult"]["fields"]), accuracies: this.buildAccuracies(response["trainResult"]["fields"]),

Просмотреть файл

@ -8,6 +8,7 @@ export interface ITrainRecordProps {
accuracies?: object; accuracies?: object;
averageAccuracy: number; averageAccuracy: number;
modelInfo: { modelInfo: {
isComposed?: boolean;
modelId: string; modelId: string;
createdDateTime: string; createdDateTime: string;
modelName: string; modelName: string;
@ -26,10 +27,14 @@ export default class TrainRecord extends React.Component<ITrainRecordProps, ITra
<p> <p>
{this.props.modelInfo.modelId} {this.props.modelInfo.modelId}
</p> </p>
<h6> Model Name: </h6> {this.props.modelInfo.modelName &&
<p> [
{this.props.modelInfo.modelName} <h6> Model Name: </h6>,
</p> <p>
{this.props.modelInfo.modelName}
</p>
]
}
<h6> Created date and time: </h6> <h6> Created date and time: </h6>
<p> <p>
{new Date(this.props.modelInfo.createdDateTime).toLocaleString()} {new Date(this.props.modelInfo.createdDateTime).toLocaleString()}