This commit is contained in:
IDONTSUDO 2024-04-26 15:44:09 +03:00
parent e155b4a2a1
commit c4ddb3dc8c
23 changed files with 333 additions and 132 deletions

View file

@ -1,8 +1,17 @@
{
"cSpell.words": [
"введите",
"Ведите",
"Количество",
"может",
"навык",
"Навыки",
"отрицательное",
"принимать",
"скила",
"число",
"эпох",
"эпоха",
"skils",
"typedataset"
]

View file

@ -0,0 +1,15 @@
import { Result } from "../helpers/result";
import { CreateFolderUseCase } from "../usecases/create_folder_usecase";
export class CreateManyFolderScenario {
call = async (path: string, foldersNames: string[]): Promise<Result<Error, string>> => {
try {
foldersNames.forEach(async (folder) => {
await new CreateFolderUseCase().call(`${path}${folder}`.normalize());
});
return Result.ok("many folder created");
} catch (error) {
return Result.error(new Error(error));
}
};
}

View file

@ -6,6 +6,7 @@ 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_scenario";
import { FolderStructure } from "../../projects/domain/upload_file_to_to_project_scenario";
export class ExecDatasetProcessScenario extends CallbackStrategyWithIdQuery {
idValidationExpression = new MongoIdValidation();
@ -14,6 +15,8 @@ export class ExecDatasetProcessScenario extends CallbackStrategyWithIdQuery {
return (await new ReadByIdDataBaseModelUseCase<IDatasetModel>(DatasetDBModel).call(id)).map(async (model) => {
return (await new IsHaveActiveProcessUseCase().call()).map(async () => {
await DatasetDBModel.findById(id).updateOne({ processStatus: "RUN" });
model.local_path = `${model.local_path}/${FolderStructure.datasets}/`;
console.log(`blenderproc run $PYTHON_BLENDER_PROC --cfg '${JSON.stringify(model)}'`);
return new ExecProcessUseCase().call(
`${model.project.rootDir}/`,
`blenderproc run $PYTHON_BLENDER_PROC --cfg '${JSON.stringify(model)}'`,
@ -23,4 +26,4 @@ export class ExecDatasetProcessScenario extends CallbackStrategyWithIdQuery {
});
});
};
}
}

View file

@ -9,6 +9,7 @@ export class FormBuilderValidationModel {
public context: string;
@IsArray()
public form: [];
output: any;
}
export enum ProcessStatus {
END = "END",
@ -16,7 +17,7 @@ export enum ProcessStatus {
NEW = "NEW",
}
export interface IDatasetModel {
_id?:string;
_id?: string;
name: string;
local_path: string;
dataSetObjects: string[];

View file

@ -6,6 +6,14 @@ import { IProjectInstanceModel } from "../models/project_instance_database_model
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
import { ProjectDBModel } from "../../_projects/models/project_database_model";
import { ExecProcessUseCase } from "../../../core/usecases/exec_process_usecase";
import { Result } from "../../../core/helpers/result";
import { CreateManyFolderScenario } from "../../../core/scenarios/create_many_folder_scenario";
export enum FolderStructure {
assets = "assets",
weights = "weights",
datasets = "datasets",
}
export class UploadCadFileToProjectScenario extends CallbackStrategyWithFileUpload {
checkingFileExpression: RegExp = new RegExp(".FCStd");
@ -14,13 +22,18 @@ export class UploadCadFileToProjectScenario extends CallbackStrategyWithFileUplo
async call(file: IFile, id: string): ResponseBase {
return (await new ReadByIdDataBaseModelUseCase<IProjectInstanceModel>(ProjectDBModel).call(id)).map(
async (databaseModel) =>
(await new CreateFileUseCase().call(`${databaseModel.rootDir}/${file.name}`, file.data)).map(
async () =>
(await new CreateFileUseCase().call(`${databaseModel.rootDir}/${file.name}`, file.data)).map(async () =>
(
await new ExecProcessUseCase().call(
`${databaseModel.rootDir}/`,
'',
`python3 $PYTHON_BLENDER --path '${databaseModel.rootDir}/assets/'`
`python3 $PYTHON_BLENDER --path '${databaseModel.rootDir}/assets/'`,
""
)
).map(async () =>
(await new CreateManyFolderScenario().call(databaseModel.rootDir, Object.keys(FolderStructure).map((el) => `/${el}`))).map(() =>
Result.ok("file upload and save")
)
)
)
);
}

View file

@ -13,14 +13,16 @@ export class ExecWeightProcessScenario extends CallbackStrategyWithIdQuery {
return (await new ReadByIdDataBaseModelUseCase<IWeightModel>(WeightDBModel).call(id)).map(async (model) => {
return (await new IsHaveActiveProcessUseCase().call()).map(async () => {
await WeightDBModel.findById(id).updateOne({ processStatus: "RUN" });
if (typeof model.project === "object" && typeof model.datasetId === "object") {
console.log(
`python3 $PYTHON_EDUCATION --path ${model.project.rootDir} --name ${model.name} --datasetName ${model.datasetId.name}`
);
if (model.processStatus === "exec") {
console.log(20);
}
if (model.processStatus === "new") {
console.log(20);
}
return new ExecProcessUseCase().call(
`${model.project.rootDir}/`,
`python3 $PYTHON_EDUCATION --path ${model.project.rootDir} --name ${model.name} --datasetName ${model.datasetId.name}`,
`python3 $PYTHON_EDUCATION --path ${model.project.rootDir} --name ${model.name} --datasetName ${model.datasetId.name} --outpath ${model.project.rootDir} --type ${model.datasetId.formBuilder.output.typedataset} --epoch ${model.epoch} `,
id,
new ProcessWatcherAndDatabaseUpdateService(id as unknown as ObjectId, WeightDBModel)
);

View file

@ -1,11 +1,12 @@
import { IsMongoId, IsString } from "class-validator";
import { IsNumber, IsString } from "class-validator";
import { IWeightModel } from "./weights_validation_model";
export class WeightValidationModel implements IWeightModel {
public processStatus: string;
@IsNumber()
public epoch: number;
@IsString()
public name: string;
@IsMongoId()
public datasetId: string;
@IsMongoId()
public project: string;
}

View file

@ -7,6 +7,8 @@ export interface IWeightModel {
name: string;
datasetId: string | IDatasetModel;
project: string | IProjectModel;
processStatus: string;
epoch: number;
}
export const WeightSchema = new Schema({
name: {
@ -22,11 +24,15 @@ export const WeightSchema = new Schema({
type: String,
default: "none",
},
// the user selects
isFinished: {
type: Boolean,
default: false,
},
epoch:{
type:Number,
},
datasetId: {
type: Schema.Types.ObjectId,
ref: datasetSchema,

View file

@ -5,6 +5,7 @@ import { extensions } from "./core/extensions/extensions";
import { httpRoutes } from "./core/controllers/routes";
import { executorProgramService } from "./core/usecases/exec_process_usecase";
extensions();
const socketSubscribers = [new SocketSubscriber(executorProgramService, "realtime")];

View file

@ -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 {

View file

@ -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()
}
}
};

View file

@ -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 = () => {

View file

@ -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;

View file

@ -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;
}

View file

@ -6,6 +6,7 @@ export enum ProcessStatus {
ERROR = "ERROR",
NEW = "NEW",
RUN = "RUN",
NONE = "none"
}
export interface IDatasetModel {

View file

@ -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>
);
};

View 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>
);
};

View 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);
}
}

View file

@ -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);
};

View file

@ -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

View file

@ -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) => {

View file

@ -8,13 +8,13 @@ args = parser.parse_args()
def copy_and_move_folder(src, dst):
try:
if os.path.exists(dst):
shutil.rmtree(dst)
if os.path.exists(src):
shutil.rmtree(src)
shutil.copytree(src, dst)
print(f"Folder {src} successfully copied")
except shutil.Error as e:
print(f"Error: {e}")
source_folder = os.path.dirname(os.path.abspath(__file__)) + "/blender" #"/home/shalenikol/web_p/blender/"
source_folder = os.path.dirname(os.path.abspath(__file__)) + "/blender/assets/"
copy_and_move_folder(source_folder, args.path)

View file

@ -4,9 +4,13 @@ import os.path
from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument("--path")
parser.add_argument("--name")
parser.add_argument("--path", required=True, help="Path for dataset")
parser.add_argument("--name", required=True, help="String with result weights name")
parser.add_argument("--datasetName", required=True, help="String with dataset name")
parser.add_argument("--outpath", default="weights", help="Output path for weights")
parser.add_argument("--type", default="ObjectDetection", help="Type of implementation")
parser.add_argument("--epoch", default=3, help="How many training epochs")
args = parser.parse_args()