progress
This commit is contained in:
parent
e155b4a2a1
commit
c4ddb3dc8c
23 changed files with 333 additions and 132 deletions
|
@ -26,7 +26,9 @@ declare global {
|
|||
toPx(): string;
|
||||
unixFromDate(): string;
|
||||
isValid(str: string): boolean;
|
||||
randRange(min:number,max:number):number
|
||||
randRange(min: number, max: number): number;
|
||||
isPositive(): boolean;
|
||||
isNegative(): boolean;
|
||||
}
|
||||
|
||||
interface String {
|
||||
|
|
|
@ -24,10 +24,22 @@ export const NumberExtensions = () => {
|
|||
return !isNaN(Number(str));
|
||||
};
|
||||
}
|
||||
if(Number().randRange === undefined){
|
||||
if (Number().randRange === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Number.prototype.randRange = function (min,max) {
|
||||
Number.prototype.randRange = function (min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
};
|
||||
}
|
||||
if (Number().isPositive === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Number.prototype.isPositive = function () {
|
||||
return Math.sign(Number(this)) === 1;
|
||||
};
|
||||
}
|
||||
if(Number().isNegative === undefined){
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Number.prototype.isNegative = function (){
|
||||
return !this.isPositive()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,9 +2,13 @@ import { NavigateFunction } from "react-router-dom";
|
|||
import { Result } from "../helper/result";
|
||||
import { UiBaseError } from "../model/ui_base_error";
|
||||
import { HttpError } from "../repository/http_repository";
|
||||
import { message } from "antd";
|
||||
|
||||
export type CoreError = HttpError | Error;
|
||||
|
||||
interface IMessage {
|
||||
successMessage?: string;
|
||||
errorMessage?: string;
|
||||
}
|
||||
export abstract class UiLoader {
|
||||
isLoading = false;
|
||||
async httpHelper<T>(callBack: Promise<Result<any, T>>) {
|
||||
|
@ -32,6 +36,16 @@ export abstract class UiLoader {
|
|||
})
|
||||
);
|
||||
};
|
||||
messageHttp = async <T>(callBack: Promise<Result<CoreError, T>>, report?: IMessage) => {
|
||||
return (await this.httpHelper(callBack)).fold(
|
||||
(s) => {
|
||||
if (report && report.successMessage) message.success(report.successMessage);
|
||||
},
|
||||
(e) => {
|
||||
if (report && report.errorMessage) message.error(report.errorMessage);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
export class SimpleErrorState extends UiLoader {
|
||||
errorHandingStrategy = () => {
|
||||
|
|
|
@ -2,7 +2,6 @@ import makeAutoObservable from "mobx-store-inheritance";
|
|||
import { ProjectRepository, UUID } from "../data/project_repository";
|
||||
import { IProjectModel } from "../model/project_model";
|
||||
import { SimpleErrorState } from "../../../core/store/base_store";
|
||||
import { ActivePipeline } from "../../../core/model/active_pipeline";
|
||||
|
||||
interface IProjectView {
|
||||
isActive: boolean;
|
||||
|
|
|
@ -8,7 +8,7 @@ import type { MenuProps } from "antd";
|
|||
import { Dropdown } from "antd";
|
||||
import { ProcessStatus } from "./dataset_model";
|
||||
|
||||
interface IMenuItem {
|
||||
export interface IMenuItem {
|
||||
onClick: Function;
|
||||
name: string;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ export enum ProcessStatus {
|
|||
ERROR = "ERROR",
|
||||
NEW = "NEW",
|
||||
RUN = "RUN",
|
||||
NONE = "none"
|
||||
}
|
||||
|
||||
export interface IDatasetModel {
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
import { CoreButton } from "../../core/ui/button/button";
|
||||
import { Icon } from "../../core/ui/icons/icons";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
import { ProcessStatus } from "../dataset/dataset_model";
|
||||
|
||||
export interface ISkillCardProps {
|
||||
processStatus?: string;
|
||||
name?: string;
|
||||
emptyOnClick?: Function;
|
||||
empty: boolean;
|
||||
}
|
||||
|
||||
export const SkillCard = (props: ISkillCardProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
margin: 10,
|
||||
backgroundColor: "#f7f2fa",
|
||||
borderRadius: 10,
|
||||
padding: 10,
|
||||
width: 150,
|
||||
height: 100,
|
||||
alignContent: "center",
|
||||
}}
|
||||
>
|
||||
{props.empty ? (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.empty && props.emptyOnClick) props.emptyOnClick();
|
||||
}}
|
||||
style={{ display: "flex", justifyContent: "center", alignItems: "center" }}
|
||||
>
|
||||
<Icon type="PlusCircle" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<CoreText text={props.name ?? ""} type={CoreTextType.medium} />
|
||||
{props.processStatus === ProcessStatus.END ? (
|
||||
<CoreButton
|
||||
onClick={() => {
|
||||
// if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
// props.onClickButton(props.id);
|
||||
// }
|
||||
}}
|
||||
text="Завершен"
|
||||
/>
|
||||
) : null}
|
||||
{props.processStatus === ProcessStatus.RUN ? (
|
||||
<>
|
||||
<CoreButton
|
||||
onClick={() => {
|
||||
// if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
// props.onClickButton(props.id);
|
||||
}}
|
||||
block={true}
|
||||
text="Стоп"
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
{props.processStatus === ProcessStatus.ERROR ? (
|
||||
<CoreButton
|
||||
style={{
|
||||
backgroundColor: "red",
|
||||
}}
|
||||
onClick={() => {}}
|
||||
filled={true}
|
||||
text="Ошибка"
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
149
ui/src/features/skils/skill_card.tsx
Normal file
149
ui/src/features/skils/skill_card.tsx
Normal file
|
@ -0,0 +1,149 @@
|
|||
import { CoreButton } from "../../core/ui/button/button";
|
||||
import { Icon } from "../../core/ui/icons/icons";
|
||||
import { Dropdown } from "antd";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
import { ProcessStatus } from "../dataset/dataset_model";
|
||||
import { IMenuItem } from "../dataset/card_dataset";
|
||||
import type { MenuProps } from "antd";
|
||||
|
||||
export interface ISkillCardProps {
|
||||
processStatus?: string;
|
||||
name?: string;
|
||||
isFinished?: boolean;
|
||||
id?: string;
|
||||
emptyOnClick?: Function;
|
||||
startOnClick?: (id: string) => void;
|
||||
continueOnClick?: (id: string) => void;
|
||||
empty: boolean;
|
||||
onEdit?: (id: string) => void;
|
||||
onDelete?: (id: string) => void;
|
||||
}
|
||||
|
||||
export const SkillCard = (props: ISkillCardProps) => {
|
||||
const menu: IMenuItem[] = [
|
||||
{
|
||||
onClick: () => {
|
||||
if (props.onEdit && props.id) props.onEdit(props.id);
|
||||
},
|
||||
name: "Редактировать",
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
if (props.onDelete && props.id) props.onDelete(props.id);
|
||||
},
|
||||
name: "Удалить",
|
||||
},
|
||||
];
|
||||
|
||||
const items: MenuProps["items"] = menu.map((el, index) => {
|
||||
return {
|
||||
key: String(index),
|
||||
label: <CoreText text={el.name} type={CoreTextType.medium} />,
|
||||
onClick: () => el.onClick(props.id),
|
||||
};
|
||||
});
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
margin: 10,
|
||||
backgroundColor: "#f7f2fa",
|
||||
borderRadius: 10,
|
||||
padding: 10,
|
||||
width: 150,
|
||||
height: "max-content",
|
||||
alignContent: "center",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "stretch",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
{props.empty ? (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.empty && props.emptyOnClick) props.emptyOnClick();
|
||||
}}
|
||||
style={{ display: "flex", justifyContent: "center", alignItems: "center" }}
|
||||
>
|
||||
<Icon type="PlusCircle" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<CoreText text={props.name ?? ""} type={CoreTextType.medium} />
|
||||
<Dropdown overlayStyle={{ backgroundColor: "rgba(243, 237, 247, 1)" }} menu={{ items }}>
|
||||
<div>
|
||||
<Icon type="Settings" />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div style={{ height: 10 }} />
|
||||
<div>
|
||||
{props.processStatus === ProcessStatus.NONE ? (
|
||||
<>
|
||||
<CoreButton
|
||||
text="старт"
|
||||
onClick={() => {
|
||||
if (props.startOnClick && props.id) props.startOnClick(props.id);
|
||||
}}
|
||||
/>
|
||||
<div style={{ height: 10 }} />
|
||||
<CoreButton
|
||||
text="продолжить"
|
||||
onClick={() => {
|
||||
if (props.continueOnClick && props.id) props.continueOnClick(props.id);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
{props.processStatus === ProcessStatus.END ? (
|
||||
<>
|
||||
<CoreButton
|
||||
text="старт"
|
||||
onClick={() => {
|
||||
if (props.startOnClick && props.id) props.startOnClick(props.id);
|
||||
}}
|
||||
/>
|
||||
<div style={{ height: 10 }} />
|
||||
<CoreButton
|
||||
text="продолжить"
|
||||
onClick={() => {
|
||||
if (props.continueOnClick && props.id) props.continueOnClick(props.id);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
{props.processStatus === ProcessStatus.RUN ? (
|
||||
<>
|
||||
<CoreButton
|
||||
onClick={() => {
|
||||
// if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
// props.onClickButton(props.id);
|
||||
}}
|
||||
block={true}
|
||||
text="Стоп"
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
{props.processStatus === ProcessStatus.ERROR ? (
|
||||
<CoreButton
|
||||
style={{
|
||||
backgroundColor: "red",
|
||||
}}
|
||||
onClick={() => {}}
|
||||
filled={true}
|
||||
text="Ошибка"
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
26
ui/src/features/skils/skill_model.ts
Normal file
26
ui/src/features/skils/skill_model.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { Result } from "../../core/helper/result";
|
||||
|
||||
export class SkillModel {
|
||||
constructor(public name: string, public datasetId: string, public project: string, public epoch: number) {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
static empty() {
|
||||
return new SkillModel("", "", "", 0);
|
||||
}
|
||||
valid(): Result<string, SkillModel> {
|
||||
if (this.name.isEmpty()) {
|
||||
return Result.error("введите имя скила");
|
||||
}
|
||||
if (this.datasetId.isEmpty()) {
|
||||
return Result.error("datasetId is empty");
|
||||
}
|
||||
if (this.project.isEmpty()) {
|
||||
return Result.error("project is empty");
|
||||
}
|
||||
if (this.epoch.isNegative()) {
|
||||
return Result.error("эпоха не может принимать отрицательное число");
|
||||
}
|
||||
return Result.ok(this);
|
||||
}
|
||||
}
|
|
@ -2,8 +2,9 @@ import { Result } from "../../core/helper/result";
|
|||
import { HttpError, HttpMethod, HttpRepository } from "../../core/repository/http_repository";
|
||||
import { UUID } from "../all_projects/data/project_repository";
|
||||
import { IDatasetModel } from "../dataset/dataset_model";
|
||||
import { SkillModel } from "./skills_store";
|
||||
export interface IEducations {
|
||||
import { SkillModel } from "./skill_model";
|
||||
|
||||
export interface ISkils {
|
||||
_id: string;
|
||||
name: string;
|
||||
processStatus: string;
|
||||
|
@ -13,13 +14,20 @@ export interface IEducations {
|
|||
__v: number;
|
||||
}
|
||||
|
||||
export class SkillsRepository extends HttpRepository {
|
||||
export class SkillsHttpRepository extends HttpRepository {
|
||||
execSkill(id: string) {
|
||||
return this._jsonRequest(HttpMethod.GET, `/weights/exec?id=${id}`);
|
||||
}
|
||||
editSkill(model: ISkils) {
|
||||
return this._jsonRequest(HttpMethod.PUT, `/weights/`, model);
|
||||
}
|
||||
getAllSkills = async () => {
|
||||
return this._jsonRequest<IEducations[]>(HttpMethod.GET, "/weights");
|
||||
return this._jsonRequest<ISkils[]>(HttpMethod.GET, "/weights");
|
||||
};
|
||||
deleteSkill = async (id: string) => {
|
||||
return this._jsonRequest<void>(HttpMethod.DELETE, `/weights?id=${id}`);
|
||||
};
|
||||
|
||||
addNewSkill = async (model: SkillModel) => {
|
||||
return this._jsonRequest(HttpMethod.POST, "/weights", model);
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from "react";
|
||||
import { Drawer } from "antd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { MainPage } from "../../core/ui/pages/main_page";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
import { DrawersSkill, SkillStore } from "./skills_store";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { SkillCard } from "./skil_card";
|
||||
import { Drawer } from "antd";
|
||||
import { SkillCard } from "./skill_card";
|
||||
import { CoreInput } from "../../core/ui/input/input";
|
||||
import { CoreButton } from "../../core/ui/button/button";
|
||||
import { CoreSwitch } from "../../core/ui/switch/switch";
|
||||
|
@ -17,6 +17,7 @@ interface IItem {
|
|||
const skills: IItem[] = [{ name: "ML", isActive: true }];
|
||||
|
||||
export const SkillPath = "/skills";
|
||||
|
||||
export const SkillScreen = observer(() => {
|
||||
const [store] = React.useState(() => new SkillStore());
|
||||
|
||||
|
@ -47,7 +48,7 @@ export const SkillScreen = observer(() => {
|
|||
}}
|
||||
>
|
||||
{store.skils?.map((el) => (
|
||||
<SkillCard name={el.name} processStatus={el.processStatus} empty={false} />
|
||||
<SkillCard id={el._id} isFinished={el.isFinished} name={el.name} processStatus={el.processStatus} empty={false} startOnClick={(id) => store.execSkill(id)} continueOnClick={(id) => store.continueSkill(id)} />
|
||||
))}
|
||||
<SkillCard empty={true} emptyOnClick={() => store.edtDrawer(DrawersSkill.NEW_SKILL, true)} />
|
||||
</div>
|
||||
|
@ -61,6 +62,11 @@ export const SkillScreen = observer(() => {
|
|||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
|
||||
<CoreInput value={store.skill.name} label={"Имя навыка"} onChange={(e) => store.changeSkillName(e)} />
|
||||
<CoreInput
|
||||
value={store.skill.epoch.toString()}
|
||||
label="Количество эпох"
|
||||
onChange={(e) => store.changeEpoch(Number(e))}
|
||||
/>
|
||||
<div style={{ height: "100%" }}>
|
||||
{store.datasets?.map((el) => (
|
||||
<div
|
||||
|
|
|
@ -1,38 +1,18 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { NavigateFunction } from "react-router-dom";
|
||||
import { HttpError } from "../../core/repository/http_repository";
|
||||
import { UiErrorState } from "../../core/store/base_store";
|
||||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { IEducations as ISkils, SkillsRepository as SkillsHttpRepository } from "./skills_repository";
|
||||
import { ISkils, SkillsHttpRepository } from "./skills_repository";
|
||||
import { Drawer } from "../dataset/dataset_store";
|
||||
import { IDatasetModel } from "../dataset/dataset_model";
|
||||
import { Result } from "../../core/helper/result";
|
||||
import { message } from "antd";
|
||||
import { UUID } from "../all_projects/data/project_repository";
|
||||
import { SkillModel } from "./skill_model";
|
||||
|
||||
export enum DrawersSkill {
|
||||
NEW_SKILL = "Новый навык",
|
||||
}
|
||||
|
||||
export class SkillModel {
|
||||
constructor(public name: string, public datasetId: string, public project: string) {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
static empty() {
|
||||
return new SkillModel("", "", "");
|
||||
}
|
||||
valid(): Result<string, SkillModel> {
|
||||
if (this.name.isEmpty()) {
|
||||
return Result.error("name is empty");
|
||||
}
|
||||
if (this.datasetId.isEmpty()) {
|
||||
return Result.error("datasetId is empty");
|
||||
}
|
||||
if (this.project.isEmpty()) {
|
||||
return Result.error("project is empty");
|
||||
}
|
||||
return Result.ok(this);
|
||||
}
|
||||
}
|
||||
export class SkillStore extends UiErrorState<HttpError> {
|
||||
drawers: Drawer[];
|
||||
skillsHttpRepository: SkillsHttpRepository;
|
||||
|
@ -54,6 +34,27 @@ export class SkillStore extends UiErrorState<HttpError> {
|
|||
this.skillsHttpRepository = new SkillsHttpRepository();
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
getSkillById = (id: string) => this.skils?.find((el) => el._id === id);
|
||||
continueSkill = async (id: string) => {
|
||||
const skill = this.getSkillById(id) as ISkils;
|
||||
skill.processStatus = "new";
|
||||
await this.skillsHttpRepository.editSkill(skill);
|
||||
this.messageHttp(this.skillsHttpRepository.execSkill(id), {
|
||||
errorMessage: "Ошибка",
|
||||
successMessage: "Обучение продолжено",
|
||||
});
|
||||
};
|
||||
|
||||
execSkill = async (id: string) => {
|
||||
const skill = this.getSkillById(id) as ISkils;
|
||||
skill.processStatus = "exec";
|
||||
await this.skillsHttpRepository.editSkill(skill);
|
||||
this.messageHttp(this.skillsHttpRepository.execSkill(id), {
|
||||
errorMessage: "Ошибка",
|
||||
successMessage: "Обучение стартовало",
|
||||
});
|
||||
};
|
||||
|
||||
errorHandingStrategy: (error: HttpError) => {};
|
||||
init = async (navigate?: NavigateFunction | undefined) => {
|
||||
await this.mapOk("skils", this.skillsHttpRepository.getAllSkills());
|
||||
|
@ -63,8 +64,10 @@ export class SkillStore extends UiErrorState<HttpError> {
|
|||
changeSkillName(name: string): void {
|
||||
this.skill.name = name;
|
||||
}
|
||||
changeEpoch(epoch: number) {
|
||||
this.skill.epoch = epoch;
|
||||
}
|
||||
saveSkill() {
|
||||
console.log(this.activeProjectId);
|
||||
this.skill.project = this.activeProjectId?.id ?? "";
|
||||
this.skill.valid().fold(
|
||||
async (model) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue