progress
This commit is contained in:
parent
c17515d571
commit
0c03906518
25 changed files with 475 additions and 201 deletions
31
.vscode/settings.json
vendored
31
.vscode/settings.json
vendored
|
@ -1,32 +1,3 @@
|
|||
{
|
||||
"files.exclude": {
|
||||
"**/.git": false,
|
||||
"**/.svn": false,
|
||||
"**/.hg": false,
|
||||
"**/CVS": false,
|
||||
"**/__pycache__": false,
|
||||
"*.DS_Store": false,
|
||||
"*.DS_Store.*": false,
|
||||
"*.git": false,
|
||||
"*.git.*": false,
|
||||
"*.vscode": false,
|
||||
"*.vscode.*": false,
|
||||
"*server.*": false,
|
||||
"*ui": false,
|
||||
"*ui.*": false
|
||||
},
|
||||
"cSpell.words": [
|
||||
"запущен",
|
||||
"процесс",
|
||||
"antd",
|
||||
"Collada",
|
||||
"Contolls",
|
||||
"fileupload",
|
||||
"lerp",
|
||||
"metadatas",
|
||||
"Skils",
|
||||
"undici",
|
||||
"uuidv"
|
||||
],
|
||||
"editor.rulers": [100]
|
||||
"cSpell.words": ["Ведите"]
|
||||
}
|
||||
|
|
2
p.ts
2
p.ts
|
@ -1,2 +0,0 @@
|
|||
console.log([1, 2, 3, 4, 5, 6].reduce((element, acc) => (acc += element)));
|
||||
console.log(["1", "2", "3", "4", "5", "6"].reduce((element, acc) => (acc += element)));
|
|
@ -13,7 +13,6 @@ export class ReadingJsonFileAndConvertingToInstanceClassScenario<T> {
|
|||
}
|
||||
call = async (path: string): Promise<Result<string, T>> => {
|
||||
try {
|
||||
console.log(path);
|
||||
const result = await new ReadFileAndParseJsonUseCase().call(path);
|
||||
if (result.isFailure()) {
|
||||
return result.forward();
|
||||
|
|
|
@ -8,7 +8,6 @@ export class CreateFolderUseCase {
|
|||
}
|
||||
call = async (path: string): Promise<Result<Error, string>> => {
|
||||
try {
|
||||
console.log(path);
|
||||
if (await this.fileSystemRepository.dirIsExists(path)) {
|
||||
return Result.ok("ok");
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ export class ExecProcessUseCase {
|
|||
try {
|
||||
executorProgramService.execPath = path;
|
||||
executorProgramService.on((event) => {
|
||||
console.log(event);
|
||||
if (watcher) watcher.emit(event);
|
||||
});
|
||||
executorProgramService.call(EXEC_TYPE.EXEC, command);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { CrudController } from "../../core/controllers/crud_controller";
|
||||
import { IsHaveActiveProcessUseCase, KillLastProcessUseCase } from "../../core/usecases/exec_process_usecase";
|
||||
import { CreateDataSetScenario } from "./domain/create_dataset_scanario";
|
||||
import { CreateDataSetScenario } from "./domain/create_dataset_scenario";
|
||||
import { ExecDatasetProcessScenario } from "./domain/exec_process_scenario";
|
||||
import { GetDatasetActiveProjectScenario } from "./domain/get_dataset_active_project_scenario";
|
||||
import { DatasetDBModel } from "./models/dataset_database_model";
|
||||
|
|
|
@ -4,11 +4,10 @@ import { Result } from "../../../core/helpers/result";
|
|||
import { TypedEvent } from "../../../core/helpers/typed_event";
|
||||
import { EXEC_EVENT, ExecError, SpawnError } from "../../../core/models/exec_error_model";
|
||||
import { ExecutorResult } from "../../../core/models/executor_result";
|
||||
import { ExecProcessUseCase } from "../../../core/usecases/exec_process_usecase";
|
||||
import { SearchDataBaseModelUseCase } from "../../../core/usecases/search_database_model_usecase";
|
||||
import { IProjectModel, ProjectDBModel } from "../../_projects/models/project_database_model";
|
||||
import { DatasetDBModel } from "../models/dataset_database_model";
|
||||
import { DatasetValidationModel } from "../models/dataset_validation_model";
|
||||
import { DatasetValidationModel, ProcessStatus } from "../models/dataset_validation_model";
|
||||
|
||||
export class ProcessWatcherAndDatabaseUpdateService extends TypedEvent<Result<ExecError | SpawnError, ExecutorResult>> {
|
||||
databaseId: ObjectId;
|
||||
|
@ -25,7 +24,7 @@ export class ProcessWatcherAndDatabaseUpdateService extends TypedEvent<Result<Ex
|
|||
const dbModel = await DatasetDBModel.findById(this.databaseId);
|
||||
if (dbModel !== null) {
|
||||
dbModel.local_path;
|
||||
dbModel.processStatus = "end";
|
||||
dbModel.processStatus = ProcessStatus.END;
|
||||
dbModel.processLogs = success.data;
|
||||
await dbModel.save();
|
||||
}
|
||||
|
@ -34,7 +33,7 @@ export class ProcessWatcherAndDatabaseUpdateService extends TypedEvent<Result<Ex
|
|||
async (error) => {
|
||||
const dbModel = await DatasetDBModel.findById(this.databaseId);
|
||||
if (dbModel !== null) {
|
||||
dbModel.processStatus = "error";
|
||||
dbModel.processStatus = ProcessStatus.ERROR;
|
||||
dbModel.processLogs = error.message;
|
||||
await dbModel.save();
|
||||
}
|
||||
|
@ -48,7 +47,7 @@ export class CreateDataSetScenario extends CallbackStrategyWithValidationModel<D
|
|||
return (
|
||||
await new SearchDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects")
|
||||
).map(async (project) => {
|
||||
model.processStatus = "new";
|
||||
model.processStatus = ProcessStatus.NEW;
|
||||
model.local_path = project.rootDir;
|
||||
model.projectId = project._id;
|
||||
const d = new DatasetDBModel();
|
|
@ -5,7 +5,7 @@ import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_
|
|||
import { MongoIdValidation } from "../../../core/validations/mongo_id_validation";
|
||||
import { DatasetDBModel } from "../models/dataset_database_model";
|
||||
import { IDatasetModel } from "../models/dataset_validation_model";
|
||||
import { ProcessWatcherAndDatabaseUpdateService } from "./create_dataset_scanario";
|
||||
import { ProcessWatcherAndDatabaseUpdateService } from "./create_dataset_scenario";
|
||||
|
||||
export class ExecDatasetProcessScenario extends CallbackStrategyWithIdQuery {
|
||||
idValidationExpression = new MongoIdValidation();
|
||||
|
|
|
@ -10,14 +10,18 @@ export class FormBuilderValidationModel {
|
|||
@IsArray()
|
||||
public form: [];
|
||||
}
|
||||
|
||||
export enum ProcessStatus {
|
||||
END = "END",
|
||||
ERROR = "ERROR",
|
||||
NEW = "NEW",
|
||||
}
|
||||
export interface IDatasetModel {
|
||||
name: string;
|
||||
local_path: string;
|
||||
dataSetObjects: string[];
|
||||
formBuilder: FormBuilderValidationModel;
|
||||
processLogs: string;
|
||||
processStatus: string;
|
||||
processStatus: ProcessStatus;
|
||||
project?: IProjectModel;
|
||||
}
|
||||
|
||||
|
@ -30,7 +34,7 @@ export class DatasetValidationModel implements IDatasetModel {
|
|||
@Type(() => FormBuilderValidationModel)
|
||||
public formBuilder: FormBuilderValidationModel;
|
||||
public local_path: string;
|
||||
public processStatus: string;
|
||||
public processStatus: ProcessStatus;
|
||||
public projectId: string;
|
||||
public processLogs: string;
|
||||
}
|
||||
|
|
|
@ -20,5 +20,10 @@
|
|||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap')
|
||||
</style>
|
||||
<style>
|
||||
[contenteditable]:focus {
|
||||
outline: 0px solid transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
|
@ -51,4 +51,10 @@ export const ArrayExtensions = () => {
|
|||
return this.indexOf(element) !== -1;
|
||||
};
|
||||
}
|
||||
if ([].repeat === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.repeat = function (quantity) {
|
||||
return Array(quantity).fill(this[0]);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ declare global {
|
|||
isEmpty(): boolean;
|
||||
isNotEmpty(): boolean;
|
||||
hasIncludeElement(element: T): boolean;
|
||||
repeat(quantity: number): Array<T>;
|
||||
}
|
||||
interface Number {
|
||||
fromArray(): number[];
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import * as React from "react";
|
||||
import { Input, Select, Button } from "antd";
|
||||
import { InputBuilderViewModel, InputType } from "./form_view_model";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { FormBuilderStore } from "./form_builder_store";
|
||||
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model";
|
||||
import { SelectCore } from "../select/select";
|
||||
import { CoreInput } from "../input/input";
|
||||
import { Icon } from "../icons/icons";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
export interface IFormBuilder {
|
||||
context: string;
|
||||
|
@ -28,107 +31,107 @@ export const FormBuilder = observer((props: IFormBuilder) => {
|
|||
{store.formViewModel?.inputs.map((element) => {
|
||||
if (element.type.isEqual(InputType.ENUM)) {
|
||||
const values = element.values as string[];
|
||||
console.log(element.totalValue);
|
||||
return (
|
||||
<>
|
||||
<div>{element.name}</div>
|
||||
|
||||
<Select
|
||||
defaultValue={element.defaultValue}
|
||||
style={{ width: 120 }}
|
||||
onChange={(e) => {
|
||||
store.changeTotalValue(element.id, e);
|
||||
}}
|
||||
options={values?.map((el) => {
|
||||
return { value: el };
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
<SelectCore
|
||||
items={values}
|
||||
value={element.totalValue ?? element.defaultValue}
|
||||
onChange={(value) => store.changeTotalValue(element.id, value)}
|
||||
label={element.name}
|
||||
style={{ margin: 10 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (element.type.isEqual(InputType.ARRAY)) {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ fontSize: "large" }}>{element.name}</div>
|
||||
<div style={{ border: "1px black solid", margin: 10 }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
margin: 10,
|
||||
alignItems: "center",
|
||||
paddingRight: 10,
|
||||
}}
|
||||
onClick={() => {
|
||||
store.open(element.id);
|
||||
}}
|
||||
>
|
||||
<CoreText text={element.name} type={CoreTextType.large} />
|
||||
<Icon type="PlusCircle" style={{ width: 22 }} />
|
||||
</div>
|
||||
|
||||
{element.isOpen ? (
|
||||
<div>
|
||||
<div style={{ margin: 10 }}>
|
||||
{element.totalValue instanceof Array
|
||||
? element.totalValue?.map((subArray, index) => {
|
||||
return (
|
||||
<>
|
||||
<div style={{ paddingLeft: "10px" }}>
|
||||
index: {index}
|
||||
<Button onClick={() => store.deleteTotalValueSubItem(element.id, index)}>
|
||||
Delete item
|
||||
</Button>
|
||||
<div style={{ margin: 10 }}>
|
||||
<div style={{ display: "flex" }}>
|
||||
<CoreText text={(element.subType ?? "") + ` ${index}`} type={CoreTextType.medium} />
|
||||
<Icon
|
||||
style={{ paddingLeft: 10 }}
|
||||
type="DeleteCircle"
|
||||
onClick={() => store.deleteTotalValueSubItem(element.id, index)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{subArray.map((subSubArrayItem: InputBuilderViewModel, subIndex: number) => {
|
||||
if (subSubArrayItem.type.isEqual(InputType.ENUM)) {
|
||||
return (
|
||||
<>
|
||||
<div>{subSubArrayItem.name}</div>
|
||||
|
||||
<Select
|
||||
defaultValue={subSubArrayItem.defaultValue}
|
||||
style={{ width: 120 }}
|
||||
onChange={(e) => {
|
||||
store.changeTotalSubValue(element.id, subIndex, e);
|
||||
}}
|
||||
options={subSubArrayItem.values?.map((el) => {
|
||||
return { value: el };
|
||||
})}
|
||||
<SelectCore
|
||||
items={subSubArrayItem.values?.map((el) => String(el)) ?? []}
|
||||
value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue}
|
||||
onChange={(value) => store.changeTotalSubValue(element.id, subIndex, value)}
|
||||
label={element.name}
|
||||
style={{ margin: 5 }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (subSubArrayItem.type.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||
return (
|
||||
<div style={{ width: "200px" }}>
|
||||
<div>{subSubArrayItem.name}</div>
|
||||
<Input
|
||||
<div>
|
||||
<CoreInput
|
||||
style={{ margin: 5 }}
|
||||
onChange={(e) => {
|
||||
store.changeTotalSubValue(element.id, subIndex, e.target.value);
|
||||
store.changeTotalSubValue(element.id, subIndex, e);
|
||||
}}
|
||||
defaultValue={subSubArrayItem.defaultValue}
|
||||
value={subSubArrayItem.defaultValue}
|
||||
label={subSubArrayItem.name}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <>Error</>;
|
||||
})}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
onClick={() => {
|
||||
store.open(element.id);
|
||||
}}
|
||||
>
|
||||
+
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (element.type.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||
return (
|
||||
<div style={{ width: "200px" }}>
|
||||
<div>{element.name}</div>
|
||||
<Input
|
||||
<div>
|
||||
<CoreInput
|
||||
onChange={(e) => {
|
||||
store.changeTotalValue(element.id, e.target.value);
|
||||
store.changeTotalValue(element.id, e);
|
||||
}}
|
||||
defaultValue={element.defaultValue}
|
||||
value={element.defaultValue}
|
||||
label={element.name}
|
||||
style={{ margin: 10 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <>Error</>;
|
||||
})}
|
||||
<Button onClick={() => store.saveForm()}>SAVE FORM</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -4,16 +4,31 @@ import { Result } from "../../helper/result";
|
|||
export interface IIconsProps {
|
||||
type: string;
|
||||
style?: React.CSSProperties;
|
||||
onClick?: Function;
|
||||
}
|
||||
|
||||
export function Icon(props: IIconsProps) {
|
||||
const icon = getIconSvg(props.type);
|
||||
return icon.fold(
|
||||
(node) => {
|
||||
return <div style={props.style}>{node}</div>;
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.onClick) props.onClick();
|
||||
}}
|
||||
style={props.style}
|
||||
>
|
||||
{node}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<div style={props.style}>
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.onClick) props.onClick();
|
||||
}}
|
||||
style={props.style}
|
||||
>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
|
@ -28,6 +43,23 @@ export function Icon(props: IIconsProps) {
|
|||
}
|
||||
const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
|
||||
switch (type) {
|
||||
case "DeleteCircle":
|
||||
return Result.ok(
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM10 8.59L13.59 5L15 6.41L11.41 10L15 13.59L13.59 15L10 11.41L6.41 15L5 13.59L8.59 10L5 6.41L6.41 5L10 8.59Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Check":
|
||||
return Result.ok(
|
||||
<svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 9.4L0 5.4L1.4 4L4 6.6L10.6 0L12 1.4L4 9.4Z" fill="white" />
|
||||
</svg>
|
||||
);
|
||||
case "PlusCircle":
|
||||
return Result.ok(
|
||||
<svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
|
38
ui/src/core/ui/input/input.tsx
Normal file
38
ui/src/core/ui/input/input.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
interface IInputProps {
|
||||
label: string;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export const CoreInput = (props: IInputProps) => {
|
||||
return (
|
||||
<div
|
||||
style={Object.assign(
|
||||
{
|
||||
backgroundColor: "rgba(230, 224, 233, 1)",
|
||||
height: 58,
|
||||
borderRadius: " 4px 4px 0px 0px",
|
||||
borderBottom: "solid 1px black",
|
||||
padding: "10px 10px 10px 10px",
|
||||
},
|
||||
props.style
|
||||
)}
|
||||
>
|
||||
<CoreText type={CoreTextType.small} text={props.label} />
|
||||
|
||||
<div
|
||||
onChange={(event) => {
|
||||
console.log(event);
|
||||
}}
|
||||
onInput={(e) => {
|
||||
if (props.onChange && e.currentTarget.textContent) props.onChange(e.currentTarget.textContent);
|
||||
}}
|
||||
style={{ fontSize: 16, fontFamily: "Roboto", color: "#1D1B20", height: 24 }}
|
||||
contentEditable="true"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
67
ui/src/core/ui/select/select.tsx
Normal file
67
ui/src/core/ui/select/select.tsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import React from "react";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
interface ISelectCoreProps {
|
||||
items: string[];
|
||||
value: string;
|
||||
label: string;
|
||||
onChange: (value: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
export const SelectCore = (props: ISelectCoreProps) => {
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const [cursorIsCorses, setCursorIsCorses] = React.useState(false);
|
||||
const [value, setValue] = React.useState(props.value);
|
||||
React.useEffect(() => {
|
||||
ref.current?.addEventListener("mousemove", () => {
|
||||
setCursorIsCorses(true);
|
||||
});
|
||||
ref.current?.addEventListener("mouseleave", () => {
|
||||
setCursorIsCorses(false);
|
||||
});
|
||||
}, [ref, setCursorIsCorses]);
|
||||
|
||||
return (
|
||||
<div ref={ref} style={props.style}>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(230, 224, 233, 1)",
|
||||
height: 58,
|
||||
borderRadius: "4px 4px 0px 0px",
|
||||
borderBottom: "solid 1px black",
|
||||
padding: "10px 10px 10px 10px",
|
||||
}}
|
||||
>
|
||||
<CoreText type={CoreTextType.small} text={props.label} />
|
||||
<div style={{ fontSize: 16, fontFamily: "Roboto", color: "#1D1B20", height: 24 }}>{value}</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(243, 237, 247, 1)",
|
||||
boxShadow: "0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px 2px rgba(0, 0, 0, 0.15)",
|
||||
borderRadius: 4,
|
||||
}}
|
||||
>
|
||||
{cursorIsCorses
|
||||
? props.items.map((el) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
setValue(el);
|
||||
props.onChange(el);
|
||||
}}
|
||||
style={{
|
||||
height: 48,
|
||||
textAlign: "center",
|
||||
alignContent: "center",
|
||||
cursor: "pointer",
|
||||
borderBottom: "1px solid",
|
||||
}}
|
||||
>
|
||||
{el}
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
19
ui/src/core/ui/switch/switch.tsx
Normal file
19
ui/src/core/ui/switch/switch.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Icon } from "../icons/icons";
|
||||
|
||||
interface ISwitchProps {
|
||||
isSelected: boolean;
|
||||
id: string;
|
||||
onChange: (status: boolean, id: string) => void;
|
||||
}
|
||||
export const CoreSwitch = (props: ISwitchProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{ height: 40, width: 40, borderRadius: 2, alignContent: "center", cursor: "pointer" }}
|
||||
onClick={() => props.onChange(props.isSelected, props.id)}
|
||||
>
|
||||
<div style={{ backgroundColor: "rgba(104, 80, 164, 1)", width: 20, height: 20, textAlign: "center" }}>
|
||||
{props.isSelected ? <Icon type={"Check"} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -4,6 +4,7 @@ export enum CoreTextType {
|
|||
header,
|
||||
medium,
|
||||
large,
|
||||
small,
|
||||
}
|
||||
|
||||
export interface ITextProps {
|
||||
|
@ -13,6 +14,22 @@ export interface ITextProps {
|
|||
}
|
||||
|
||||
export function CoreText(props: ITextProps) {
|
||||
if (props.type === CoreTextType.small) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "rgba(73, 69, 79, 1)",
|
||||
fontSize: 12,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
fontSizeAdjust: 14,
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (props.type === CoreTextType.large) {
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -34,8 +34,6 @@ export function BehaviorTreeBuilderScreen() {
|
|||
|
||||
React.useEffect(() => {
|
||||
store.init();
|
||||
// @ts-expect-error
|
||||
console.log(ref.current.clientHeight);
|
||||
|
||||
store.dragZoneSetOffset(
|
||||
// @ts-expect-error
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
import { Spin } from "antd";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
import poseIMG from "../../assets/images/pose_estemation.jpg";
|
||||
import { Icon } from "../icons/icons";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
import { CoreButton } from "../button/button";
|
||||
import poseIMG from "../../core/assets/images/pose_estemation.jpg";
|
||||
import { Icon } from "../../core/ui/icons/icons";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
import { CoreButton } from "../../core/ui/button/button";
|
||||
import type { MenuProps } from "antd";
|
||||
import { Dropdown } from "antd";
|
||||
import { ProcessStatus } from "./dataset_model";
|
||||
|
||||
interface IMenuItem {
|
||||
onClick: Function;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const enum CardDataSetType {
|
||||
EMPTY = "EMPTY",
|
||||
|
@ -19,10 +27,22 @@ interface ICardDataSetProps {
|
|||
processStatus?: string;
|
||||
onClickButton?: (id: string) => void;
|
||||
onClickEmptyCard?: Function;
|
||||
onEdit?: Function;
|
||||
onDelete?: Function;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export const CardDataSet = (props: ICardDataSetProps) => {
|
||||
const menu: IMenuItem[] = [
|
||||
{ onClick: () => props.onEdit?.call(props.id), name: "Редактировать" },
|
||||
{ onClick: () => props.onDelete?.call(props.id), name: "Удалить" },
|
||||
];
|
||||
const items: MenuProps["items"] = menu.map((el, index) => {
|
||||
return {
|
||||
key: String(index),
|
||||
label: <CoreText text={el.name} type={CoreTextType.medium} />,
|
||||
};
|
||||
});
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
|
@ -48,7 +68,11 @@ export const CardDataSet = (props: ICardDataSetProps) => {
|
|||
<CoreText text={props.neuralNetworkAction ?? ""} type={CoreTextType.medium} />
|
||||
</div>
|
||||
<div>
|
||||
<Icon type="Settings" />
|
||||
<Dropdown overlayStyle={{ backgroundColor: "rgba(243, 237, 247, 1)" }} menu={{ items }}>
|
||||
<div>
|
||||
<Icon type="Settings" />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -75,7 +99,7 @@ export const CardDataSet = (props: ICardDataSetProps) => {
|
|||
<Spin indicator={<LoadingOutlined style={{ fontSize: 34, color: "rgba(103, 80, 164, 1)" }} spin />} />
|
||||
) : null}
|
||||
<div style={{ width: 20 }} />
|
||||
{props.processStatus === "new" ? (
|
||||
{props.processStatus === ProcessStatus.NEW ? (
|
||||
<CoreButton
|
||||
onClick={() => {
|
||||
if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
|
@ -85,9 +109,19 @@ export const CardDataSet = (props: ICardDataSetProps) => {
|
|||
filled={true}
|
||||
text="Старт"
|
||||
/>
|
||||
) : (
|
||||
<CoreButton text="Завершено" block={true} />
|
||||
)}
|
||||
) : null}
|
||||
|
||||
{props.processStatus === ProcessStatus.ERROR ? (
|
||||
<CoreButton
|
||||
onClick={() => {
|
||||
if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
props.onClickButton(props.id);
|
||||
}
|
||||
}}
|
||||
filled={true}
|
||||
text="Ошибка"
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
|
@ -1,11 +1,16 @@
|
|||
import { InputBuilderViewModel } from "../../core/ui/form_builder/form_view_model";
|
||||
import { Result } from "../../core/helper/result";
|
||||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
export enum ProcessStatus {
|
||||
END = "END",
|
||||
ERROR = "ERROR",
|
||||
NEW = "NEW",
|
||||
}
|
||||
|
||||
export interface IDatasetModel {
|
||||
_id: string;
|
||||
dataSetObjects: string[];
|
||||
processStatus: string;
|
||||
processStatus: ProcessStatus;
|
||||
projectId: string;
|
||||
name: string;
|
||||
formBuilder: FormBuilder;
|
||||
|
@ -77,13 +82,13 @@ export class DataSetModel {
|
|||
return Result.error("project is unknow");
|
||||
}
|
||||
if (this.dataSetObjects.isEmpty()) {
|
||||
return Result.error("not selected details");
|
||||
return Result.error("Не выделены детали");
|
||||
}
|
||||
if (this.datasetType.isEmpty()) {
|
||||
return Result.error("dataset type is empty");
|
||||
return Result.error("Навык датасета не выбран");
|
||||
}
|
||||
if (this.name.isEmpty()) {
|
||||
return Result.error("dataset name is empty");
|
||||
return Result.error("ВВедите имя датасета");
|
||||
}
|
||||
return Result.ok();
|
||||
}
|
||||
|
@ -93,8 +98,6 @@ export const datasetTypes = ["Object Detection - YOLOv8", "Pose Estimation - DOP
|
|||
|
||||
export const datasetFormMockResult = `{
|
||||
"typedataset": \${typedataset:Enum<T>:ObjectDetection},
|
||||
"dataset_path": \${ПУТЬ ДАТАСЕТА:string:none},
|
||||
"models":\${models:Array<MODELS>:[]},
|
||||
"models_randomization":{
|
||||
"loc_range_low": [\${LOC_RANGE_LOW_1:number:-1}, \${LOC_RANGE_LOW_2:number:-1},/\${LOC_RANGE_LOW_3:number:0}],
|
||||
"loc_range_high": [\${LOC_RANGE_HIGH_1:number:1}, \${LOC_RANGE_HIGH_2:number:1},/\${LOC_RANGE_HIGH_3:number:2}]
|
||||
|
@ -123,12 +126,7 @@ ENUM T = "ObjectDetection","PoseEstimation";
|
|||
ENUM L = "POINT","SUN";
|
||||
ENUM F = "JPEG","PNG";
|
||||
ENUM COLLISION_SHAPE = "SHAPE","COLLISION";
|
||||
|
||||
type MODELS = {
|
||||
"id": \${ID:number:1},
|
||||
"name": \${NAME:string:none},
|
||||
"model": \${MODEL:string:models/1.fbx}
|
||||
};
|
||||
|
||||
type OBJECTS_SCENE = {
|
||||
"name": \${NAME:string:default},
|
||||
"collision_shape": \${collision_shape:Enum<COLLISION_SHAPE>:BOX},
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import * as React from "react";
|
||||
import { Drawer, Button, Radio, Card, Checkbox, Input } from "antd";
|
||||
import { Drawer } from "antd";
|
||||
import { FormBuilder } from "../../core/ui/form_builder/form_builder";
|
||||
import { DataSetStore } from "./dataset_store";
|
||||
import { DataSetStore, DrawersDataset } from "./dataset_store";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { datasetTypes } from "./dataset_model";
|
||||
import { MainPage } from "../../core/ui/pages/main_page";
|
||||
import { CardDataSet, CardDataSetType } from "../../core/ui/card/card_dataset";
|
||||
import { CardDataSet, CardDataSetType } from "./card_dataset";
|
||||
import { CoreInput } from "../../core/ui/input/input";
|
||||
import { CoreButton } from "../../core/ui/button/button";
|
||||
import { ListItem } from "./list_item";
|
||||
import { datasetTypes } from "./dataset_model";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
|
||||
export const DatasetsScreenPath = "/dataset";
|
||||
|
||||
|
@ -15,9 +19,6 @@ export const DataSetScreen: React.FunctionComponent = observer(() => {
|
|||
React.useEffect(() => {
|
||||
store.init();
|
||||
}, [store]);
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [open1, setOpen1] = React.useState(false);
|
||||
return (
|
||||
<>
|
||||
<MainPage
|
||||
|
@ -36,7 +37,6 @@ export const DataSetScreen: React.FunctionComponent = observer(() => {
|
|||
}}
|
||||
>
|
||||
{store.datasets?.map((el) => {
|
||||
console.log(el);
|
||||
return (
|
||||
<CardDataSet
|
||||
type={CardDataSetType.COMPLETED}
|
||||
|
@ -46,18 +46,23 @@ export const DataSetScreen: React.FunctionComponent = observer(() => {
|
|||
neuralNetworkAction={el.datasetType.split(" - ").at(0)}
|
||||
neuralNetworkName={el.datasetType.split(" - ").at(1)}
|
||||
id={el._id}
|
||||
onClickButton={(id) => store.runProcess(id)}
|
||||
onClickButton={(id: string) => store.runProcess(id)}
|
||||
onDelete={(id: string) => store.deleteDataset(id)}
|
||||
onEdit={(id: string) => store.editDataset(id)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<CardDataSet
|
||||
type={CardDataSetType.EMPTY}
|
||||
onClickEmptyCard={() => {
|
||||
setOpen(true);
|
||||
store.editDrawer(DrawersDataset.NewDataset, true);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Drawer title="Form builder Drawer" onClose={() => setOpen1(false)} open={open1}>
|
||||
<Drawer
|
||||
title={DrawersDataset.FormBuilderDrawer}
|
||||
onClose={() => store.editDrawer(DrawersDataset.FormBuilderDrawer, false)}
|
||||
open={store.drawers.find((el) => el.name === DrawersDataset.FormBuilderDrawer)?.status ?? false}
|
||||
>
|
||||
<FormBuilder
|
||||
context={store.dataSetModel.formBuilder.context}
|
||||
result={store.dataSetModel.formBuilder.result}
|
||||
|
@ -65,42 +70,69 @@ export const DataSetScreen: React.FunctionComponent = observer(() => {
|
|||
if (el) store.dataSetModel.formBuilder = el;
|
||||
}}
|
||||
/>
|
||||
<div style={{ display: "flex" }}>
|
||||
<CoreButton text="Сохранить" filled={true} />
|
||||
<div style={{ width: 10 }} />
|
||||
<CoreButton text="Отмена" onClick={() => store.editDrawer(DrawersDataset.FormBuilderDrawer, false)} />
|
||||
</div>
|
||||
</Drawer>
|
||||
<Drawer title="New Dataset" onClose={() => setOpen(false)} open={open}>
|
||||
<Input placeholder="Dataset name" onChange={(e) => (store.dataSetModel.name = e.target.value)} />
|
||||
<Button onClick={() => setOpen1(true)}>edit dataset settings</Button>
|
||||
<Button onClick={() => store.saveDataset()}>save dataset</Button>
|
||||
|
||||
<Radio.Group
|
||||
value={store.dataSetModel.datasetType}
|
||||
onChange={(e) => {
|
||||
store.dataSetModel.datasetType = e.target.value;
|
||||
}}
|
||||
<Drawer
|
||||
title={DrawersDataset.NewDataset}
|
||||
onClose={() => store.editDrawer(DrawersDataset.NewDataset, false)}
|
||||
open={store.drawers.find((el) => el.name === DrawersDataset.NewDataset)?.status}
|
||||
>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}
|
||||
>
|
||||
{datasetTypes.map((el) => (
|
||||
<>
|
||||
<Radio.Button value={el}>{el}</Radio.Button>
|
||||
</>
|
||||
))}
|
||||
</Radio.Group>
|
||||
<div>
|
||||
<CoreInput label={"Имя датасета"} onChange={(e) => store.setNewDatasetName(e)} />
|
||||
<div>
|
||||
<CoreText type={CoreTextType.header} text="Тип навыка" />
|
||||
{datasetTypes.map((el) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => store.setSkillSelected(el)}
|
||||
style={{
|
||||
backgroundColor: store.skillIsSelected(el) ? "rgb(103 80 164)" : "rgba(254, 247, 255, 1)",
|
||||
borderRadius: 25,
|
||||
color: store.skillIsSelected(el) ? "white" : "",
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
{el}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div style={{ width: 180, marginTop: 10, marginBottom: 30 }}>
|
||||
<CoreButton
|
||||
onClick={() => store.editDrawer(DrawersDataset.FormBuilderDrawer, true)}
|
||||
text="Настройки датасета"
|
||||
filled={true}
|
||||
/>
|
||||
</div>
|
||||
{store.assets?.assets?.map((el) => {
|
||||
return (
|
||||
<ListItem
|
||||
status={store.assetStatus(el.name)}
|
||||
name={el.name}
|
||||
imgURL={el.image}
|
||||
onChange={() => {
|
||||
store.datasetCheckBox(el);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{store.assets?.assets.map((el) => {
|
||||
return (
|
||||
<>
|
||||
<Checkbox
|
||||
onChange={() => {
|
||||
store.dataSetModel.dataSetObjects.push(el.name);
|
||||
}}
|
||||
defaultChecked={store.dataSetModel.dataSetObjects.includes(el.name)}
|
||||
>
|
||||
include?
|
||||
</Checkbox>
|
||||
<Card title={el.name} style={{ width: 300 }}>
|
||||
<img src={el.image} alt="error" />
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
<div style={{ display: "flex" }}>
|
||||
<CoreButton text="Сохранить" filled={true} onClick={() => store.saveDataset()} />
|
||||
<div style={{ width: 10 }} />
|
||||
<CoreButton text="Отмена" onClick={() => store.editDrawer(DrawersDataset.NewDataset, false)} />
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
</div>
|
||||
}
|
||||
|
@ -108,31 +140,3 @@ export const DataSetScreen: React.FunctionComponent = observer(() => {
|
|||
</>
|
||||
);
|
||||
});
|
||||
{
|
||||
/* <Button onClick={() => setOpen(true)}>new dataset</Button>
|
||||
{store.datasets?.isEmpty() ? (
|
||||
<div>empty dataset</div>
|
||||
) : (
|
||||
<div>
|
||||
<h1>Datasets</h1>
|
||||
<div>
|
||||
{store.datasets?.map((el, index) => {
|
||||
return (
|
||||
<Card title={el.name}>
|
||||
<h2>dataset objects:</h2>
|
||||
<div>
|
||||
{el.dataSetObjects.map((el) => (
|
||||
<div>{el}</div>
|
||||
))}
|
||||
</div>
|
||||
<h2>process status</h2>
|
||||
<div>{el.processStatus}</div>
|
||||
<Button>Edit process</Button>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</> */
|
||||
}
|
||||
|
|
|
@ -2,28 +2,80 @@ import makeAutoObservable from "mobx-store-inheritance";
|
|||
import { DataSetRepository } from "./dataset_repository";
|
||||
import { UiErrorState } from "../../core/store/base_store";
|
||||
import { HttpError } from "../../core/repository/http_repository";
|
||||
import { Assets, DataSetModel, IDatasetModel } from "./dataset_model";
|
||||
import { Asset, Assets, DataSetModel, IDatasetModel } from "./dataset_model";
|
||||
import { message } from "antd";
|
||||
import { UUID } from "../all_projects/data/project_repository";
|
||||
|
||||
export enum DrawersDataset {
|
||||
NewDataset = "Новый датасет",
|
||||
FormBuilderDrawer = "Редактировать датасет",
|
||||
}
|
||||
export interface Drawer {
|
||||
name: string;
|
||||
status: boolean;
|
||||
}
|
||||
export class DataSetStore extends UiErrorState<HttpError> {
|
||||
dataSetRepository: DataSetRepository;
|
||||
assets?: Assets;
|
||||
datasets?: IDatasetModel[];
|
||||
activeProject: UUID;
|
||||
dataSetModel = DataSetModel.empty();
|
||||
drawers: Drawer[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.dataSetRepository = new DataSetRepository();
|
||||
this.drawers = Object.entries(DrawersDataset).map((k, v) => {
|
||||
return {
|
||||
name: k.at(1) ?? "",
|
||||
status: false,
|
||||
};
|
||||
});
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
setNewDatasetName(e: string): void {
|
||||
this.dataSetModel.name = e;
|
||||
}
|
||||
|
||||
setSkillSelected(el: string): void {
|
||||
this.dataSetModel.datasetType = el;
|
||||
}
|
||||
|
||||
skillIsSelected(el: string): boolean {
|
||||
return el === this.dataSetModel.datasetType;
|
||||
}
|
||||
|
||||
assetStatus(name: string): boolean {
|
||||
return this.dataSetModel.dataSetObjects.includes(name);
|
||||
}
|
||||
|
||||
datasetCheckBox(asset: Asset): void {
|
||||
if (this.assetStatus(asset.name)) {
|
||||
this.dataSetModel.dataSetObjects = this.dataSetModel.dataSetObjects.filter((el) => !el.isEqual(asset.name));
|
||||
} else {
|
||||
this.dataSetModel.dataSetObjects.push(asset.name);
|
||||
}
|
||||
}
|
||||
|
||||
editDrawer(drawerName: DrawersDataset, status: boolean): void {
|
||||
this.drawers = this.drawers.map((el) => {
|
||||
if (el.name === drawerName) {
|
||||
el.status = status;
|
||||
}
|
||||
return el;
|
||||
});
|
||||
}
|
||||
editDataset(id: string) {}
|
||||
|
||||
deleteDataset(id: string) {}
|
||||
|
||||
runProcess = async (id: string): Promise<void> => {
|
||||
(await this.dataSetRepository.isRunningProcess()).fold(
|
||||
async (s) => {
|
||||
(await this.dataSetRepository.execDatasetProcess(id)).fold(
|
||||
() => {
|
||||
message.success("процесс запущен");
|
||||
message.success("Процесс запущен");
|
||||
},
|
||||
(e) => message.error(e.message)
|
||||
);
|
||||
|
@ -41,7 +93,7 @@ export class DataSetStore extends UiErrorState<HttpError> {
|
|||
async () => {
|
||||
(await this.dataSetRepository.saveDataSet(this.dataSetModel)).fold(
|
||||
() => {
|
||||
message.success("датасет сохранен");
|
||||
message.success("Датасет сохранен");
|
||||
},
|
||||
(error) => message.error(error.message)
|
||||
);
|
||||
|
|
31
ui/src/features/dataset/list_item.tsx
Normal file
31
ui/src/features/dataset/list_item.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { CoreSwitch } from "../../core/ui/switch/switch";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
|
||||
export interface IListItemProps {
|
||||
name: string;
|
||||
imgURL: string;
|
||||
status: boolean;
|
||||
onChange: (status: boolean, id: string) => void;
|
||||
}
|
||||
export const ListItem = (props: IListItemProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(254, 247, 255, 1)",
|
||||
border: "1px #6750a4 solid",
|
||||
width: "100%",
|
||||
height: 110,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
borderRadius: 12,
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
>
|
||||
<img style={{ height: 50, margin: 10 }} src={props.imgURL} alt="" />
|
||||
<CoreText text={props.name} type={CoreTextType.large} />
|
||||
<CoreSwitch isSelected={props.status} id={props.name} onChange={props.onChange} />
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
|||
import { PipelineInstanceStore } from "./pipeline_instance_store";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Icon } from "../../core/ui/icons/icons";
|
||||
import { CardDataSet, CardDataSetType } from "../../core/ui/card/card_dataset";
|
||||
import { CardDataSet, CardDataSetType } from "../dataset/card_dataset";
|
||||
import { MainPage } from "../../core/ui/pages/main_page";
|
||||
|
||||
export const PipelineInstanceScreenPath = "/pipeline_instance/";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue