added support for drawing pipeline steps

This commit is contained in:
Shubham Takode 2024-02-22 13:59:44 +05:30
parent a6296624de
commit 646843b196
24 changed files with 749 additions and 226 deletions

View File

@ -34,7 +34,16 @@ import { BASE_PATH, COLLECTION_FORMATS } from "../variables";
import { Configuration } from "../configuration";
import { PipelinesDeleteRunsRequest } from "~/business-logic/model/pipelines/pipelinesDeleteRunsRequest";
import { PipelinesDeleteRunsResponse } from "~/business-logic/model/pipelines/pipelinesDeleteRunsResponse";
import { PipelinesCreateRequest, PipelinesCreateResponse, PipelinesCreateStepsRequest, PipelinesCreateStepsResponse } from "../model/pipelines/models";
import {
PipelinesCreateRequest,
PipelinesCreateResponse,
PipelinesCreateStepsRequest,
PipelinesCreateStepsResponse,
PipelinesGetByIdRequest,
PipelinesGetByIdResponse,
PipelinesUpdateRequest,
PipelinesUpdateResponse,
} from "../model/pipelines/models";
@Injectable()
export class ApiPipelinesService {
@ -215,7 +224,7 @@ export class ApiPipelinesService {
}
return this.apiRequest.post<PipelinesCreateResponse>(
`${this.basePath}/pipelines.create`,
`${this.basePath}/pipelines.create_pipeline`,
request,
{
withCredentials: this.configuration.withCredentials,
@ -226,7 +235,6 @@ export class ApiPipelinesService {
);
}
/**
*
* Create a new pipeline step
@ -269,7 +277,7 @@ export class ApiPipelinesService {
}
return this.apiRequest.post<PipelinesCreateStepsResponse>(
`${this.basePath}/pipelines.create.step`,
`${this.basePath}/pipelines.create_step`,
request,
{
withCredentials: this.configuration.withCredentials,
@ -280,5 +288,107 @@ export class ApiPipelinesService {
);
}
/**
*
* Update pipeline information
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public pipelinesUpdate(
request: PipelinesUpdateRequest,
options?: any,
observe: any = "body",
reportProgress: boolean = false
): Observable<any> {
if (request === null || request === undefined) {
throw new Error(
"Required parameter request was null or undefined when calling projectsUpdate."
);
}
let headers = this.defaultHeaders;
if (options && options.async_enable) {
headers = headers.set(this.configuration.asyncHeader, "1");
}
// to determine the Accept header
const httpHeaderAccepts: string[] = ["application/json"];
const httpHeaderAcceptSelected: string | undefined =
this.configuration.selectHeaderAccept(httpHeaderAccepts);
if (httpHeaderAcceptSelected != undefined) {
headers = headers.set("Accept", httpHeaderAcceptSelected);
}
// to determine the Content-Type header
const consumes: string[] = [];
const httpContentTypeSelected: string | undefined =
this.configuration.selectHeaderContentType(consumes);
if (httpContentTypeSelected != undefined) {
headers = headers.set("Content-Type", httpContentTypeSelected);
}
return this.apiRequest.post<PipelinesUpdateResponse>(
`${this.basePath}/pipelines.update_pipeline`,
{...request, pipeline_id: request.id},
{
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress,
}
);
}
/**
*
*
* @param request request body
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
* @param reportProgress flag to report request and response progress.
*/
public pipelinesGetById(
request: PipelinesGetByIdRequest,
options?: any,
observe: any = "body",
reportProgress: boolean = false
): Observable<any> {
if (request === null || request === undefined) {
throw new Error(
"Required parameter request was null or undefined when calling pipelinesGetById."
);
}
let headers = this.defaultHeaders;
if (options && options.async_enable) {
headers = headers.set(this.configuration.asyncHeader, "1");
}
// to determine the Accept header
const httpHeaderAccepts: string[] = ["application/json"];
const httpHeaderAcceptSelected: string | undefined =
this.configuration.selectHeaderAccept(httpHeaderAccepts);
if (httpHeaderAcceptSelected != undefined) {
headers = headers.set("Accept", httpHeaderAcceptSelected);
}
// to determine the Content-Type header
const consumes: string[] = [];
const httpContentTypeSelected: string | undefined =
this.configuration.selectHeaderContentType(consumes);
if (httpContentTypeSelected != undefined) {
headers = headers.set("Content-Type", httpContentTypeSelected);
}
return this.apiRequest.post<PipelinesGetByIdResponse>(
`${this.basePath}/pipelines.get_by_id`,
request,
{
withCredentials: this.configuration.withCredentials,
headers: headers,
observe: observe,
reportProgress: reportProgress,
}
);
}
}

View File

@ -4,5 +4,9 @@ export * from '././pipelinesCreateRequest';
export * from '././pipelinesCreateResponse';
export * from '././pipelinesCreateStepsRequest';
export * from '././pipelinesCreateStepsResponse';
export * from '././pipelinesGetByIdRequest';
export * from '././pipelinesGetByIdResponse';
export * from '././pipelinesUpdateRequest';
export * from '././pipelinesUpdateResponse';
export * from "././pipeline";

View File

@ -15,7 +15,11 @@ import { PipelinesParameter } from './pipelinesParameter';
* Do not edit the class manually.
*/
interface FlowDisplay {
nodes?: Array<unknown>;
edges?: Array<unknown>
}
export interface Pipeline {
/**
@ -70,5 +74,7 @@ export interface Pipeline {
own_models?: number;
hidden?: boolean;
parameters?: Array<PipelinesParameter>
parameters?: Array<PipelinesParameter>;
flow_display?: FlowDisplay;
}

View File

@ -4,4 +4,5 @@ export interface PipelinesCreateResponse {
* Pipeline id
*/
id?: string;
project_id?: string;
}

View File

@ -1,6 +1,6 @@
export interface PipelinesCreateStepsRequest {
/**
* Pipeline name. Unique within the company.
* Pipeline step name. Unique within the company.
*/
name: string;
/**
@ -10,5 +10,7 @@ export interface PipelinesCreateStepsRequest {
experiment?: string;
parameters?: Array<object>
parameters?: Array<object>,
pipeline_id?: string;
}

View File

@ -0,0 +1,8 @@
export interface PipelinesGetByIdRequest {
/**
* Project id
*/
pipeline?: string;
pipeline_name?: string;
}

View File

@ -0,0 +1,7 @@
import { Pipeline } from './pipeline';
export interface PipelinesGetByIdResponse {
pipeline?: Pipeline;
}

View File

@ -0,0 +1,8 @@
import { Pipeline } from "./pipeline";
export interface PipelinesUpdateRequest extends Pipeline {
flow_display?: unknown;
pipeline_id?: string;
}

View File

@ -0,0 +1,28 @@
/**
* projects
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.14
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { Pipeline } from "./pipeline";
export interface PipelinesUpdateResponse extends Pipeline {
/**
* Number of projects updated (0 or 1)
*/
updated?: number;
/**
* Updated fields names and values
*/
fields?: object;
id?: string;
}

View File

@ -58,4 +58,5 @@ export interface ProjectsGetAllResponseSingle {
sub_projects?: Array<ProjectsGetAllResponseSingleSubProjects>;
isRoot?: boolean;
last_update?: string; //MANUALLY
//basename?: string;
}

View File

@ -1,4 +1,7 @@
<div class="d-flex justify-content-end align-items-center header-container">
<div class="pipeline-name">
Editing {{pipelineData?.name}}
</div>
<!-- <sm-clear-filters-button
*ngIf="!minimizedView"
[tableFilters]="tableFilters"
@ -33,7 +36,7 @@
(setAutoRefresh)="setAutoRefresh.emit($event)"
>
</sm-refresh-button> -->
<button class="btn btn-icon g-btn" smTooltip="Save pipeline">
<button class="btn btn-icon g-btn" smTooltip="Save pipeline" (click)="savePipelineClicked()">
<i class="icon i-pipeline-save lm"></i>
</button>
<button class="btn btn-icon g-btn" smTooltip="Compile pipeline">

View File

@ -29,6 +29,10 @@
visibility: hidden;
}
.pipeline-name {
color: white;
flex-grow: 1;
}
&.archive-mode {
transition: background-color 0.5s;
background-color: #333746;

View File

@ -8,6 +8,7 @@ import {Option} from '@common/shared/ui-components/inputs/button-toggle/button-t
import {trackByValue} from '@common/shared/utils/forms-track-by';
import {resourceToIconMap} from '~/features/experiments/experiments.consts';
import {EntityTypeEnum} from '~/shared/constants/non-common-consts';
import { Pipeline } from '~/business-logic/model/pipelines/pipeline';
@ -25,8 +26,9 @@ export class EditPipelineHeaderComponent extends BaseEntityHeaderComponent imple
get tableCols() {
return this._tableCols;
}
@Input() pipelineData: Pipeline;
@Output() createPipelineStep = new EventEmitter();
@Output() savePipeline = new EventEmitter();
// @Output() selectedTableColsChanged = new EventEmitter<ISmCol>();
// @Output() removeColFromList = new EventEmitter<ISmCol['id']>();
// @Output() getMetricsToDisplay = new EventEmitter();
@ -52,4 +54,7 @@ export class EditPipelineHeaderComponent extends BaseEntityHeaderComponent imple
addNewStep() {
this.createPipelineStep.emit();
}
savePipelineClicked() {
this.savePipeline.emit();
}
}

View File

@ -1,10 +1,12 @@
<sm-edit-pipeline-header
(createPipelineStep)="createPipeline()"
(createPipelineStep)="createPipelineStep()"
(savePipeline)="savePipeline()"
[pipelineData]="selectedPipeline"
>
</sm-edit-pipeline-header>
<div class="edit-pipeline-body">
<!-- <div class="details"><i class="icon no-output-icon i-no-code-dark"></i>&nbsp;DETAILS</div> -->
<sm-flow-editor/>
<sm-flow-editor *ngIf="selectedPipeline?.flow_display?.nodes?.length" [pipelineData]="selectedPipeline" (nodesChangedInReactFlow)="nodesChangedInReactFlow($event)" (edgesChangedInReactFlow)="edgesChangedInReactFlow($event)" />
<div class="pipeline-empty">
<i class="icon i-fingers-white mx-auto xxl"></i>
<br/>

View File

@ -1,19 +1,72 @@
import { Component, inject } from '@angular/core';
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
import { PipelineAddStepDialogComponent } from '../pipeline-add-step-dialog/pipeline-add-step-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { createPipelineStep } from '../pipelines.actions';
import { createPipelineStep, getPipelineById, resetPipelines, resetPipelinesSearchQuery, updatePipeline } from '../pipelines.actions';
import { selectRouterParams } from '@common/core/reducers/router-reducer';
import { Observable, Subscription, map } from 'rxjs';
import { Params } from '@angular/router';
import { selectSelectedPipeline } from '../pipelines.reducer';
import { Pipeline } from '~/business-logic/model/pipelines/models';
import { cloneDeep } from 'lodash-es';
@Component({
selector: 'sm-edit-pipeline-page',
templateUrl: './edit-pipeline-page.component.html',
styleUrls: ['./edit-pipeline-page.component.scss']
})
export class EditPipelinePageComponent {
export class EditPipelinePageComponent implements OnInit, OnDestroy {
protected dialog = inject(MatDialog);
protected store = inject(Store);
public subs = new Subscription();
public selectedPipelineId$: Observable<string>;
private selectedPipeline$: Observable<Pipeline>;
public selectedPipeline: Pipeline;
pipelineId: string;
private reactFlowState = {nodes: [], edges: []};
constructor() {
this.selectedPipelineId$ = this.store.select(selectRouterParams).pipe(map((params: Params) => {
// eslint-disable-next-line @ngrx/avoid-mapping-selectors
//console.log(params);
return params?.id
}));
this.selectedPipeline$ = this.store.select(selectSelectedPipeline)
}
ngOnInit() {
this.subs.add(this.selectedPipelineId$.pipe(
).subscribe((pipelineId) => {
this.pipelineId = pipelineId;
setTimeout(()=> {
this.store.dispatch(getPipelineById({id: pipelineId, name: ''}))
}, 1000)
}
));
this.subs.add(this.selectedPipeline$.pipe(
).subscribe((pipelineData) => {
this.selectedPipeline = pipelineData;
// eslint-disable-next-line no-console
console.log(pipelineData);
}
));
}
createPipeline() {
ngOnDestroy() {
this.subs.unsubscribe();
this.store.dispatch(resetPipelines());
this.store.dispatch(resetPipelinesSearchQuery());
}
savePipeline () {
const pipelineState = cloneDeep(this.selectedPipeline);
pipelineState.flow_display.nodes = this.reactFlowState.nodes;
pipelineState.flow_display.edges = this.reactFlowState.edges;
this.store.dispatch(updatePipeline({changes: {...pipelineState}}));
}
createPipelineStep() {
this.dialog.open(PipelineAddStepDialogComponent, {
data: {defaultExperimentId: ''},
@ -23,7 +76,7 @@ export class EditPipelinePageComponent {
.afterClosed()
.subscribe(pipeline => {
if (pipeline) {
this.store.dispatch(createPipelineStep({pipelinesCreateStepRequest: pipeline}));
this.store.dispatch(createPipelineStep({pipelinesCreateStepRequest: {...pipeline, pipeline_id: this.pipelineId}}));
}
});
@ -36,4 +89,19 @@ export class EditPipelinePageComponent {
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public nodesChangedInReactFlow(data) {
this.reactFlowState.nodes = data;
//console.log("nodes changed", data);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public edgesChangedInReactFlow(data) {
this.reactFlowState.edges = data;
// eslint-disable-next-line no-console
console.log("edges changed", data);
}
}

View File

@ -5,6 +5,7 @@ import { createRoot } from 'react-dom/client';
import { PipelineFlowComponent } from './react/pipeline-flow.component';
import { Pipeline } from '~/business-logic/model/pipelines/pipeline';
@ -20,26 +21,29 @@ const containerElementName = 'myReactComponentContainer';
export class FlowEditorComponent implements OnChanges, OnDestroy, AfterViewInit {
@ViewChild(containerElementName, {static: false}) containerRef: ElementRef;
@Input() public counter = 10;
@Output() public componentClick = new EventEmitter<void>();
@Input() public pipelineData: Pipeline;
@Output() nodesChangedInReactFlow = new EventEmitter<Array<unknown>>();
@Output() edgesChangedInReactFlow = new EventEmitter<Array<unknown>>();
initialNodes = [
/* initialNodes = [
{ id: '1', position: { x: 0, y: 0 }, data: { label: '1' }, type: "normal" },
{ id: '2', position: { x: 0, y: 100 }, data: { label: '2' }, type: "normal" },
];
initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];
initialEdges = [{ id: 'e1-2', source: '1', target: '2' }]; */
constructor() {
this.handleDivClicked = this.handleDivClicked.bind(this);
//this.handleDivClicked = this.handleDivClicked.bind(this);
this.nodesDataChanged = this.nodesDataChanged.bind(this);
this.edgesDataChanged = this.edgesDataChanged.bind(this);
window.React = React;
}
public handleDivClicked() {
if (this.componentClick) {
this.componentClick.emit();
this.render();
}
}
// public handleDivClicked() {
// if (this.componentClick) {
// this.componentClick.emit();
// this.render();
// }
// }
// eslint-disable-next-line @typescript-eslint/no-unused-vars
ngOnChanges(_changes: SimpleChanges): void {
@ -51,8 +55,21 @@ export class FlowEditorComponent implements OnChanges, OnDestroy, AfterViewInit
}
ngOnDestroy() {
if(this.containerRef?.nativeElement)
ReactDOM.unmountComponentAtNode(this.containerRef.nativeElement);
}
public nodesDataChanged(nodes: Array<unknown>) {
this.nodesChangedInReactFlow?.emit(nodes)
}
public edgesDataChanged(edges: Array<unknown>) {
this.edgesChangedInReactFlow?.emit(edges)
}
private render() {
const root = createRoot(this.containerRef.nativeElement);
@ -60,8 +77,10 @@ export class FlowEditorComponent implements OnChanges, OnDestroy, AfterViewInit
root.render(React.createElement("div", {
className: 'i-am-classy',
children: React.createElement(PipelineFlowComponent, {
initialNodes: this.initialNodes,
initialEdges: this.initialEdges
initialNodes: this.pipelineData?.flow_display?.nodes,
initialEdges: this.pipelineData?.flow_display?.edges,
onNodesDataChanged: this.nodesDataChanged,
onEdgesDataChanged: this.edgesDataChanged
})
}));
}

View File

@ -1,57 +1,79 @@
import * as React from 'react';
import { FunctionComponent, useCallback, useEffect, useRef, useState } from 'react';
import ReactFlow, { Background, Controls, MarkerType, MiniMap, addEdge, useEdgesState, useNodesState, updateEdge, BackgroundVariant } from 'reactflow';
import PipelineStepComponent from './pipeline-step.component';
import * as React from "react";
import {
FunctionComponent,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import ReactFlow, {
Background,
Controls,
MarkerType,
MiniMap,
addEdge,
useEdgesState,
useNodesState,
updateEdge,
BackgroundVariant,
} from "reactflow";
import PipelineStepComponent from "./pipeline-step.component";
export interface IMyComponentProps {
initialNodes: any;
initialEdges: any;
onNodesDataChanged: Function,
onEdgesDataChanged: Function
}
const edgeOptions = {
animated: true,
style: {
stroke: '#B0B0B0',
stroke: "#B0B0B0",
},
markerEnd: {
type: MarkerType.ArrowClosed,
color: '#B0B0B0',
strokeWidth: 2
color: "#B0B0B0",
strokeWidth: 2,
},
};
const connectionLineStyle = { stroke: '#B0B0B0' };
export const PipelineFlowComponent: FunctionComponent<IMyComponentProps> = (props: IMyComponentProps) => {
const connectionLineStyle = { stroke: "#B0B0B0" };
export const PipelineFlowComponent: FunctionComponent<IMyComponentProps> = (
props: IMyComponentProps
) => {
// const timerHandle = useRef<number | null>(null);
// const [stateCounter, setStateCounter] = useState(42);
const [nodes, setNodes, onNodesChange] = useNodesState(props.initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(props.initialEdges);
const [nodes, setNodes, onNodesChange] = useNodesState(props.initialNodes?.length ? props.initialNodes : []);
const [edges, setEdges, onEdgesChange] = useEdgesState(props.initialEdges?.length? props.initialEdges: []);
const edgeUpdateSuccessful = useRef(true);
React.useEffect(() => {
props.onNodesDataChanged(nodes);
}, [nodes]);
React.useEffect(() => {
props.onEdgesDataChanged(edges)
}, [edges])
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[setEdges],
[setEdges]
);
/**
/**
* @function onEdgeUpdateEnd
* @description this function is triggger when edge is connected to other node.
*/
const onEdgeUpdateEnd = useCallback((_a, edge) => {
if (!edgeUpdateSuccessful.current) {
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
}
edgeUpdateSuccessful.current = true;
}, []);
const onEdgeUpdateEnd = useCallback((_a, edge) => {
if (!edgeUpdateSuccessful.current) {
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
}
edgeUpdateSuccessful.current = true;
}, []);
/**
/**
* @function onEdgeUpdateStart
* @description this function is trigger when we will start updating of Edge
*/
@ -59,16 +81,16 @@ export const PipelineFlowComponent: FunctionComponent<IMyComponentProps> = (prop
edgeUpdateSuccessful.current = false;
}, []);
/**
/**
* @function onEdgeUpdate
* @description this function is trigger when Edge will updated.
*/
const onEdgeUpdate = useCallback((oldEdge, newConnection) => {
edgeUpdateSuccessful.current = true;
setEdges((els) => updateEdge(oldEdge, newConnection, els));
}, []);
const onEdgeUpdate = useCallback((oldEdge, newConnection) => {
edgeUpdateSuccessful.current = true;
setEdges((els) => updateEdge(oldEdge, newConnection, els));
}, []);
/**
/**
* @function onNodeClick
* @description to update node Data
*/
@ -77,7 +99,6 @@ export const PipelineFlowComponent: FunctionComponent<IMyComponentProps> = (prop
// setNodeData(clone(node));
};
const onPaneClick = () => {
// setIsShowNodeEditModal(false);
};
@ -88,35 +109,42 @@ export const PipelineFlowComponent: FunctionComponent<IMyComponentProps> = (prop
}),
["NormalOperationPipelineNode"]
);
return <div style={{ width: '100%', height: 'calc(100vh - 130px)', position: "relative", overflow: "hidden" }}>
<ReactFlow
nodes={nodes}
onNodesChange={onNodesChange}
edges={edges}
defaultEdgeOptions={edgeOptions}
onEdgesChange={onEdgesChange}
onEdgeUpdateEnd={onEdgeUpdateEnd}
onEdgeUpdateStart={onEdgeUpdateStart}
defaultViewport={{ x: 0, y: 0, zoom: 1 }}
maxZoom={1.5}
onConnect={onConnect}
onEdgeUpdate={onEdgeUpdate}
fitView
/* style={{
return (
<div
style={{
width: "100%",
height: "calc(100vh - 130px)",
position: "relative",
overflow: "hidden",
}}
>
<ReactFlow
nodes={nodes}
onNodesChange={onNodesChange}
edges={edges}
defaultEdgeOptions={edgeOptions}
onEdgesChange={onEdgesChange}
onEdgeUpdateEnd={onEdgeUpdateEnd}
onEdgeUpdateStart={onEdgeUpdateStart}
defaultViewport={{ x: 0, y: 0, zoom: 1 }}
maxZoom={1.5}
onConnect={onConnect}
onEdgeUpdate={onEdgeUpdate}
fitView
/* style={{
backgroundColor: "#1a1e2c"
}} */
connectionLineStyle={connectionLineStyle}
onNodeClick={onNodeClick}
onPaneClick={onPaneClick}
nodeTypes={nodeTypes}
>
{/* <Background variant={BackgroundVariant.Lines} gap={20} size={0.4} /> */}
<MiniMap nodeStrokeWidth={3} />
<Controls />
</ReactFlow>
{/* <ReactFlow nodes={nodes}
connectionLineStyle={connectionLineStyle}
onNodeClick={onNodeClick}
onPaneClick={onPaneClick}
nodeTypes={nodeTypes}
>
{/* <Background variant={BackgroundVariant.Lines} gap={20} size={0.4} /> */}
<MiniMap nodeStrokeWidth={3} />
<Controls />
</ReactFlow>
{/* <ReactFlow nodes={nodes}
edges={edges} onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}>
@ -124,5 +152,6 @@ export const PipelineFlowComponent: FunctionComponent<IMyComponentProps> = (prop
<MiniMap />
<Background gap={12} size={1} />
</ReactFlow> */}
</div>;
};
</div>
);
};

View File

@ -2,7 +2,8 @@ import React from "react";
import { Handle, Position } from 'reactflow';
/* import "./pipeline-step.css" */
export default function PipelineStepComponent({ data }) {
export default function PipelineStepComponent({ data, ...others }) {
//console.log("from reactstep", others)
return (
<div className="" style={{
padding: "2px",
@ -32,7 +33,7 @@ export default function PipelineStepComponent({ data }) {
textOverflow: "ellipsis",
whiteSpace: "nowrap"
}
}>{data.name} test name</div>
}>{data.name}</div>
</div>
<div className="step-part step-footer queued" style={{

View File

@ -17,7 +17,7 @@
smUniqueNameValidator >
<i matSuffix class="al-icon sm-md search-icon me-2"
[ngClass]="experimentInput.value? 'al-ico-dialog-x pointer':'al-ico-search'"
(mousedown)="!isFocused(experimentInputRef) && experimentInput.value && clear(); experimentInput.reset(); experimentInputRef.focus()"
(mousedown)="!isFocused(experimentInputRef) && experimentInput.value && clear(); experimentInput.reset(); experimentInputRef.focus(); step.parameters = [];"
smClickStopPropagation></i>
<mat-error *ngIf="experimentInput?.errors?.existName">Please provide a experiment</mat-error>
<mat-error *ngIf="experimentInput?.errors?.uniqueName && !readOnlyExperimentsNames.includes(step.experiment?.label)">Please
@ -52,7 +52,7 @@
<textarea class="step-description" name="description" matInput [(ngModel)]="step.description"
#description="ngModel"></textarea>
</mat-form-field>
<div class="parameters">
<div class="parameters" *ngIf="step?.parameters?.length">
<div class="search">
<mat-label>Parameters</mat-label>
<sm-search search-button

View File

@ -18,8 +18,9 @@ import {
} from '@common/shared/ui-components/inputs/select-autocomplete-with-chips/select-autocomplete-with-chips.component';
import { Task } from '~/business-logic/model/tasks/task';
import { PipelineParametersComponent } from '@common/pipelines/pipeline-parameters/pipeline-parameters.component';
import { PipelinesParameter } from '~/business-logic/model/pipelines/pipelinesParameter';
import { cloneDeep } from 'lodash-es';
import { ParamsItem } from '~/business-logic/model/tasks/paramsItem';
@Component({
@ -36,22 +37,16 @@ export class PipelineAddStepFormComponent implements OnChanges, OnDestroy {
public panelHeight: number;
private subs = new Subscription();
private rootFiltered: boolean;
public readonly experimentsRoot = {label: 'My experiment', value: null};
public readonly experimentsRoot = {label: 'My experiment', value: null, parameters: []};
@ViewChild('experimentInput') experimentInput: NgModel;
public pipelinesNames: Array<string>;
public experimentsNames: Array<string>;
public step: { name: string; description: string; experiment: { label: string; value: string }, parameters: Array<PipelinesParameter> } = {
public step: { name: string; description: string; experiment: { label: string; value: string }, parameters: Array<ParamsItem> } = {
name: null,
description: '',
experiment: null,
parameters: [{
name: "Paramter1",
value: ""
}, {
name: "Parameter2",
value: ""
}],
parameters: [],
};
filterText: string = '';
isAutoCompleteOpen: boolean;
@ -95,7 +90,7 @@ export class PipelineAddStepFormComponent implements OnChanges, OnDestroy {
this._experiments = experiments;
this.experimentsOptions = [
...((this.rootFiltered || experiments === null) ? [] : [this.experimentsRoot]),
...(experiments ? experiments.map(experiment => ({label: experiment.name, value: experiment.id})) : [])
...(experiments ? experiments.map(experiment => ({label: experiment.name, value: experiment.id, parameters: experiment.hyperparams})) : [])
];
this.experimentsNames = this.experimentsOptions.map(experiment => experiment.label);
}
@ -136,6 +131,25 @@ export class PipelineAddStepFormComponent implements OnChanges, OnDestroy {
experimentSelected($event: MatOptionSelectionChange) {
this.step.experiment = {label: $event.source.value.label, value: $event.source.value.value};
this.step.parameters = [];
for (const section in $event.source?.value?.parameters) {
for (const param in $event.source?.value?.parameters[section]) {
// eslint-disable-next-line no-console
console.log($event.source?.value?.parameters[section][param]);
this.step.parameters.push({
name: $event.source?.value?.parameters[section][param].name,
value: $event.source?.value?.parameters[section][param].value,
section: $event.source?.value?.parameters[section][param].section
})
}
}
//.map((paramSectionKey, paramSectionValue) => {
// eslint-disable-next-line no-console
// paramSec.forEach((paraKey, paramVal) => {
//
// })
//});
}
setIsAutoCompleteOpen(focus: boolean) {
this.isAutoCompleteOpen = focus;

View File

@ -1,46 +1,50 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import {pageSize} from '@common/projects/common-projects.consts';
import {CommonProjectsPageComponent} from '@common/projects/containers/projects-page/common-projects-page.component';
import {isExample} from '@common/shared/utils/shared-utils';
import {trackById} from '@common/shared/utils/forms-track-by';
import { Component, OnDestroy, OnInit } from "@angular/core";
import { pageSize } from "@common/projects/common-projects.consts";
import { CommonProjectsPageComponent } from "@common/projects/containers/projects-page/common-projects-page.component";
import { isExample } from "@common/shared/utils/shared-utils";
import { trackById } from "@common/shared/utils/forms-track-by";
import {
addProjectTags,
getProjectsTags, setBreadcrumbsOptions,
getProjectsTags,
setBreadcrumbsOptions,
setDefaultNestedModeForFeature,
setSelectedProjectId,
setTags
} from '@common/core/actions/projects.actions';
setTags,
} from "@common/core/actions/projects.actions";
import {
selectDefaultNestedModeForFeature,
selectMainPageTagsFilter,
selectMainPageTagsFilterMatchMode,
selectProjectTags
} from '@common/core/reducers/projects.reducer';
import {combineLatest, Observable, Subscription} from 'rxjs';
import {Project} from '~/business-logic/model/projects/project';
selectProjectTags,
} from "@common/core/reducers/projects.reducer";
import { combineLatest, Observable, Subscription } from "rxjs";
import { Project } from "~/business-logic/model/projects/project";
import {
getAllProjectsPageProjects,
resetProjects,
showExamplePipelines,
updateProject
} from '@common/projects/common-projects.actions';
import {ProjectsGetAllResponseSingle} from '~/business-logic/model/projects/projectsGetAllResponseSingle';
import {selectShowPipelineExamples} from '@common/projects/common-projects.reducer';
import {EntityTypeEnum} from '~/shared/constants/non-common-consts';
updateProject,
} from "@common/projects/common-projects.actions";
import { ProjectsGetAllResponseSingle } from "~/business-logic/model/projects/projectsGetAllResponseSingle";
import { selectShowPipelineExamples } from "@common/projects/common-projects.reducer";
import { EntityTypeEnum } from "~/shared/constants/non-common-consts";
// import {
// PipelinesEmptyStateComponent
// } from '@common/pipelines/pipelines-page/pipelines-empty-state/pipelines-empty-state.component';
import {debounceTime, skip, withLatestFrom} from 'rxjs/operators';
import {ProjectTypeEnum} from '@common/nested-project-view/nested-project-view-page/nested-project-view-page.component';
import { PipelineDialogComponent } from '../pipeline-dialog/pipeline-dialog.component';
import { createPipeline } from '../pipelines.actions';
import { debounceTime, skip, withLatestFrom } from "rxjs/operators";
import { ProjectTypeEnum } from "@common/nested-project-view/nested-project-view-page/nested-project-view-page.component";
import { PipelineDialogComponent } from "../pipeline-dialog/pipeline-dialog.component";
import { createPipeline } from "../pipelines.actions";
@Component({
selector: 'sm-pipelines-page',
templateUrl: './pipelines-page.component.html',
styleUrls: ['./pipelines-page.component.scss']
selector: "sm-pipelines-page",
templateUrl: "./pipelines-page.component.html",
styleUrls: ["./pipelines-page.component.scss"],
})
export class PipelinesPageComponent extends CommonProjectsPageComponent implements OnInit, OnDestroy {
export class PipelinesPageComponent
extends CommonProjectsPageComponent
implements OnInit, OnDestroy
{
initPipelineCode = `from clearml import PipelineDecorator
@PipelineDecorator.component(cache=True, execution_queue="default")
@ -76,38 +80,42 @@ if __name__ == '__main__':
private mainPageFilterSub: Subscription;
public isNested$: Observable<boolean>;
constructor() {
super();
}
override ngOnInit() {
super.ngOnInit();
this.store.dispatch(getProjectsTags({entity: this.getName()}));
this.store.dispatch(getProjectsTags({ entity: this.getName() }));
this.showExamples$ = this.store.select(selectShowPipelineExamples);
this.projectsTags$ = this.store.select(selectProjectTags);
this.mainPageFilterSub = combineLatest([
this.store.select(selectMainPageTagsFilter),
this.store.select(selectMainPageTagsFilterMatchMode)
]).pipe(debounceTime(0), skip(1))
this.store.select(selectMainPageTagsFilterMatchMode),
])
.pipe(debounceTime(0), skip(1))
.subscribe(() => {
this.store.dispatch(resetProjects());
this.store.dispatch(getAllProjectsPageProjects());
});
}
override ngOnDestroy() {
super.ngOnDestroy();
this.headerUserFocusSub?.unsubscribe();
this.mainPageFilterSub.unsubscribe();
this.store.dispatch(setTags({tags: []}));
this.store.dispatch(setTags({ tags: [] }));
}
addTag(project: Project, newTag: string) {
const tags = [...project.tags, newTag];
this.store.dispatch(updateProject({id: project.id, changes: {tags}}));
this.store.dispatch(addProjectTags({tags: [newTag], systemTags: []}));
this.store.dispatch(updateProject({ id: project.id, changes: { tags } }));
this.store.dispatch(addProjectTags({ tags: [newTag], systemTags: [] }));
}
removeTag(project: Project, deleteTag: string) {
const tags = project.tags?.filter(tag => tag != deleteTag);
this.store.dispatch(updateProject({id: project.id, changes: {tags}}));
const tags = project.tags?.filter((tag) => tag != deleteTag);
this.store.dispatch(updateProject({ id: project.id, changes: { tags } }));
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -116,13 +124,27 @@ if __name__ == '__main__':
}
public override projectCardClicked(project: ProjectsGetAllResponseSingle) {
this.router.navigate([project.id, 'experiments'], {relativeTo: this.projectId ? this.route.parent.parent.parent : this.route});
this.store.dispatch(setSelectedProjectId({projectId: project.id, example: isExample(project)}));
this.router.navigate([project.id, "experiments"], {
relativeTo: this.projectId ? this.route.parent.parent.parent : this.route,
});
this.store.dispatch(
setSelectedProjectId({
projectId: project.id,
example: isExample(project),
})
);
}
public pipelineEditClicked(pipeline: ProjectsGetAllResponseSingle) {
this.router.navigate([pipeline.id, 'edit'], {relativeTo: this.projectId ? this.route.parent.parent.parent : this.route});
this.store.dispatch(setSelectedProjectId({projectId: pipeline.id, example: isExample(pipeline)}));
this.router.navigate([pipeline.id, /* pipeline.basename, */ "edit"], {
relativeTo: this.projectId ? this.route.parent.parent.parent : this.route,
});
this.store.dispatch(
setSelectedProjectId({
projectId: pipeline.id,
example: isExample(pipeline),
})
);
}
protected override getName() {
@ -130,20 +152,22 @@ if __name__ == '__main__':
}
protected override getDeletePopupEntitiesList() {
return 'run';
return "run";
}
createPipeline() {
this.dialog.open(PipelineDialogComponent, {
data: {defaultProjectId: this.projectId},
panelClass: 'light-theme',
width: '690px'
})
this.dialog
.open(PipelineDialogComponent, {
data: { defaultProjectId: this.projectId },
panelClass: "light-theme",
width: "690px",
})
.afterClosed()
.subscribe(pipeline => {
.subscribe((pipeline) => {
if (pipeline) {
this.store.dispatch(createPipeline({pipelinesCreateRequest: pipeline}));
this.store.dispatch(
createPipeline({ pipelinesCreateRequest: pipeline })
);
}
});
@ -153,7 +177,6 @@ if __name__ == '__main__':
// },
// width: '640px'
// });
}
createExamples() {
@ -161,44 +184,69 @@ if __name__ == '__main__':
}
override shouldReRoute(selectedProject, config) {
const relevantSubProjects = selectedProject?.sub_projects?.filter(proj => proj.name.includes('.pipelines'));
return config[2] === 'projects' && selectedProject.id !== '*' && (relevantSubProjects?.every(subProject => subProject.name.startsWith(selectedProject.name + '/.')));
const relevantSubProjects = selectedProject?.sub_projects?.filter((proj) =>
proj.name.includes(".pipelines")
);
return (
config[2] === "projects" &&
selectedProject.id !== "*" &&
relevantSubProjects?.every((subProject) =>
subProject.name.startsWith(selectedProject.name + "/.")
)
);
}
override noProjectsReRoute() {
return this.router.navigate(['..', 'pipelines'], {relativeTo: this.route});
return this.router.navigate(["..", "pipelines"], {
relativeTo: this.route,
});
}
toggleNestedView(nested: boolean) {
this.store.dispatch(setDefaultNestedModeForFeature({feature: 'pipelines', isNested: nested}));
this.store.dispatch(
setDefaultNestedModeForFeature({ feature: "pipelines", isNested: nested })
);
if (nested) {
this.router.navigate(['*', 'projects'], {relativeTo: this.route});
this.router.navigate(["*", "projects"], { relativeTo: this.route });
} else {
this.router.navigateByUrl('pipelines');
this.router.navigateByUrl("pipelines");
}
}
override setupBreadcrumbsOptions() {
this.subs.add(this.selectedProject$.pipe(
withLatestFrom(this.store.select(selectDefaultNestedModeForFeature))
).subscribe(([selectedProject, defaultNestedModeForFeature]) => {
this.store.dispatch(setBreadcrumbsOptions({
breadcrumbOptions: {
showProjects: !!selectedProject,
featureBreadcrumb: {
name: 'PIPELINES',
url: defaultNestedModeForFeature['pipelines'] ? 'pipelines/*/projects' : 'pipelines'
},
projectsOptions: {
basePath: 'pipelines',
filterBaseNameWith: ['.pipelines'],
compareModule: null,
showSelectedProject: selectedProject?.id !== '*',
...(selectedProject && selectedProject?.id !== '*' && {selectedProjectBreadcrumb: {name: selectedProject?.basename}})
}
}
}));
}));
this.subs.add(
this.selectedProject$
.pipe(
withLatestFrom(this.store.select(selectDefaultNestedModeForFeature))
)
.subscribe(([selectedProject, defaultNestedModeForFeature]) => {
this.store.dispatch(
setBreadcrumbsOptions({
breadcrumbOptions: {
showProjects: !!selectedProject,
featureBreadcrumb: {
name: "PIPELINES",
url: defaultNestedModeForFeature["pipelines"]
? "pipelines/*/projects"
: "pipelines",
},
projectsOptions: {
basePath: "pipelines",
filterBaseNameWith: [".pipelines"],
compareModule: null,
showSelectedProject: selectedProject?.id !== "*",
...(selectedProject &&
selectedProject?.id !== "*" && {
selectedProjectBreadcrumb: {
name: selectedProject?.basename,
},
}),
},
},
})
);
})
);
}
}

View File

@ -1,8 +1,9 @@
import {createAction, props} from '@ngrx/store';
// import {ReportsGetAllExResponse} from '~/business-logic/model/reports/reportsGetAllExResponse';
// import {IReport} from './reports.consts';
import { Pipeline, PipelinesCreateRequest } from '~/business-logic/model/pipelines/models';
import { Pipeline, PipelinesCreateRequest, PipelinesUpdateRequest, PipelinesUpdateResponse } from '~/business-logic/model/pipelines/models';
import { PipelinesCreateStepsRequest } from '~/business-logic/model/pipelines/pipelinesCreateStepsRequest';
import { TasksGetByIdRequest } from '~/business-logic/model/tasks/models';
import { Task } from '~/business-logic/model/tasks/task';
export const PIPELINES_PREFIX = 'PIPELINES_';
@ -17,6 +18,31 @@ export const createPipelineStep = createAction(
props<{ pipelinesCreateStepRequest: PipelinesCreateStepsRequest }>()
);
export const getPipelineById = createAction(
PIPELINES_PREFIX + '[GET_PIPELINE_BY_ID]',
props<{ id: string, name: string }>()
);
export const setSelectedPipeline = createAction(
PIPELINES_PREFIX + '[SET_SELECTED_PIPELINE]',
props<{ data: PipelinesUpdateResponse }>()
);
export const updatePipeline = createAction(
PIPELINES_PREFIX + '[update pipeline]',
props<{changes: Partial<PipelinesUpdateRequest>}>()
);
export const updatePipelineSuccess = createAction(
PIPELINES_PREFIX + '[update pipeline success]',
props<{changes: Partial<PipelinesUpdateResponse>}>()
);
export const getExperimentById = createAction(
PIPELINES_PREFIX + 'GET_EXPERIMENTS_BY_ID',
props<{ getExperimentByIdRequest: TasksGetByIdRequest }>()
);
export const getAllExperiments = createAction(
PIPELINES_PREFIX + 'GET_EXPERIMENTS',
props<{ query: string; regExp?: boolean }>()
@ -28,14 +54,7 @@ export const setExperimentsResults = createAction(
props<{ experiments: Task[]}>()
);
export const updateProject = createAction(
PIPELINES_PREFIX + '[update pipeline]',
props<{id: string; changes: Partial<Pipeline>}>()
);
export const updatePipelineSuccess = createAction(
PIPELINES_PREFIX + '[update pipeline success]',
props<{id: string; changes: Partial<Pipeline>}>()
);
export const getAllPipelinesPagePipelines = createAction(
PIPELINES_PREFIX + 'GET_PIPELINES'
);
@ -108,10 +127,7 @@ export const showExampleDatasets = createAction(PIPELINES_PREFIX + '[show datase
// props<{ reports: IReport[]; scroll: ReportsGetAllExResponse['scroll_id']; noMoreReports: boolean }>()
// );
// export const getReport = createAction(
// PIPELINES_PREFIX + '[get report]',
// props<{ id: string }>()
// );
// export const setReport = createAction(
// PIPELINES_PREFIX + '[set report]',
@ -130,7 +146,10 @@ export const showExampleDatasets = createAction(PIPELINES_PREFIX + '[show datase
// );
// export const moveReport = createAction(
// PIPELINES_PREFIX + '[move report]',
// The `setExperimentsResults` action is updating the state with a new array of experiments. It
// takes in an action containing the new array of experiments and updates the `experiments` field
// in the state with this new array.
// PIPELINES_PREFIX + '[move report]',
// props<{ report: IReport }>()
// );

View File

@ -1,12 +1,12 @@
import {Injectable} from '@angular/core';
import {Actions, /* concatLatestFrom, */ createEffect, ofType} from '@ngrx/effects';
import {Actions, concatLatestFrom, /* concatLatestFrom, */ createEffect, ofType} from '@ngrx/effects';
import {/* Action, */ Store} from '@ngrx/store';
import {ActivatedRoute, Router} from '@angular/router';
import {catchError, filter, map, mergeMap, switchMap, /* tap */} from 'rxjs/operators';
import {activeLoader, /* addMessage, */ deactivateLoader, setServerError} from '../core/actions/layout.actions';
import {activeLoader, addMessage, /* addMessage, */ deactivateLoader, setServerError} from '../core/actions/layout.actions';
import {requestFailed} from '../core/actions/http.actions';
import {
createPipeline, createPipelineStep, getAllExperiments, setExperimentsResults
createPipeline, createPipelineStep, getAllExperiments, getExperimentById, getPipelineById, setExperimentsResults, setSelectedPipeline, updatePipeline, updatePipelineSuccess
} from './pipelines.actions';
// import {ApiReportsService} from '~/business-logic/api-services/reports.service';
/* import {IReport, PAGE_SIZE} from './reports.consts';
@ -34,7 +34,7 @@ import {TABLE_SORT_ORDER} from '../shared/ui-components/data/table/table.consts'
import {escapeRegex} from '../shared/utils/escape-regex';
import {MESSAGES_SEVERITY} from '../constants'; */
import {MatDialog} from '@angular/material/dialog';
import {selectCurrentUser} from '../core/reducers/users-reducer';
// import {selectCurrentUser} from '../core/reducers/users-reducer';
/* import {
ChangeProjectDialogComponent
} from '@common/experiments/shared/components/change-project-dialog/change-project-dialog.component';
@ -48,11 +48,59 @@ import { PipelinesCreateResponse } from '~/business-logic/model/pipelines/pipeli
import { ApiPipelinesService } from '~/business-logic/api-services/pipelines.service';
import { PipelinesCreateStepsResponse } from '~/business-logic/model/pipelines/pipelinesCreateStepsResponse';
import { ApiTasksService } from '~/business-logic/api-services/tasks.service';
import { PipelinesUpdateResponse } from '~/business-logic/model/pipelines/pipelinesUpdateResponse';
import { selectSelectedPipeline } from './pipelines.reducer';
import { cloneDeep } from 'lodash-es';
import { MESSAGES_SEVERITY } from '@common/constants';
/* import {selectRouterParams} from '@common/core/reducers/router-reducer';
import {setMainPageTagsFilter} from '@common/core/actions/projects.actions';
import {cleanTag} from '@common/shared/utils/helpers.util';
import {excludedKey, getTagsFilters} from '@common/shared/utils/tableParamEncode'; */
const checkIsBetween = (preNodePos: number, curNodePos: number, buffer1: number) => {
if (
preNodePos > curNodePos - buffer1 &&
preNodePos < curNodePos + buffer1
) {
return true;
}
return false;
};
const checkOverlappingNodes = (allNodes, x, y, buffer) => {
const overlap = (nodes, x1, y1, buffer) => {
const isOverlap = nodes.filter((node) => {
const isCheckX = checkIsBetween(node?.position?.x, x1, buffer);
const isCheckY = checkIsBetween(node?.position?.y, y1, buffer);
return isCheckX && isCheckY;
});
if (isOverlap.length > 0) {
return true;
}
return false;
};
const overlapChecks = (nodes1, x, y, buffer) => {
let X1 = x;
let Y1 = y;
let resp = false;
if (nodes1) {
resp = overlap(nodes1, x, y, buffer);
}
if (resp === true) {
X1 += buffer;
Y1 += buffer;
return overlapChecks(nodes1, X1, Y1, buffer);
}
return { x: X1, y: Y1 };
};
return overlapChecks(allNodes, x, y, buffer);
}
@Injectable()
export class PipelinesEffects {
@ -69,6 +117,8 @@ export class PipelinesEffects {
) {
}
activeLoader = createEffect(() => this.actions.pipe(
ofType(/* getReports, getReport, */ createPipeline, createPipelineStep, getAllExperiments/* updateReport, restoreReport, archiveReport */),
filter(action => !action['refresh']),
@ -79,11 +129,10 @@ export class PipelinesEffects {
ofType(createPipeline),
switchMap((action) => this.pipelinesApiService.pipelinesCreate(action.pipelinesCreateRequest)
.pipe(mergeMap((res: PipelinesCreateResponse) => {
this.router.navigate(['pipelines', res.id, 'edit']);
return [deactivateLoader(createPipeline.type)];
this.router.navigate(['pipelines', res.project_id, 'edit']);
return [deactivateLoader(createPipeline.type), addMessage(MESSAGES_SEVERITY.SUCCESS, 'New pipeline created successfully. You can now start adding steps to it.')];
}),
catchError(err => {
// this.router.navigate(['pipelines', 'b2c6686fb12e4649a954991ca7c24518', 'edit']);
return [
requestFailed(err),
setServerError(err, null, 'failed to create a new pipeline'),
@ -95,12 +144,54 @@ export class PipelinesEffects {
createPipelineStep$ = createEffect(() => this.actions.pipe(
ofType(createPipelineStep),
switchMap((action) => this.pipelinesApiService.pipelinesCreateStep(action.pipelinesCreateStepRequest)
concatLatestFrom(() => [
this.store.select(selectSelectedPipeline)
]),
switchMap(([action, selectedPipelineData]) => this.pipelinesApiService.pipelinesCreateStep(action.pipelinesCreateStepRequest)
.pipe(mergeMap((res: PipelinesCreateStepsResponse) => {
// eslint-disable-next-line no-console
console.log(res)
// this.router.navigate(['pipelines', res.id, 'edit']);
return [deactivateLoader(createPipeline.type)];
//this.pipelinesApiService.pipelinesGetById({pipeline: action.pipelinesCreateStepRequest.pipeline_id}).pipe()
//const selectedPipeline =
if(res.id) {
const position = checkOverlappingNodes(selectedPipelineData?.flow_display?.nodes, 0, 0, 50);
const pipelineData = cloneDeep(selectedPipelineData);
const newNodeData = cloneDeep(action.pipelinesCreateStepRequest);
if (newNodeData) {
const newNode = {
id: String(res.id),
// type: 'consoleJobNode',
position,
data: {
...newNodeData
},
sourcePosition: 'right',
targetPosition: 'left',
type: 'normal'
};
if (pipelineData?.flow_display?.nodes?.length) {
pipelineData.flow_display.nodes.push(newNode);
//setNodes([...nodes, newNode]);
//updatePipelineData([...nodes, newNode]);
} else {
try {
pipelineData.flow_display.nodes = [newNode];
} catch(e) {
// eslint-disable-next-line no-console
console.log(e);
}
//updatePipelineData([...nodes, newNode]);
}
this.store.dispatch(setSelectedPipeline({data: cloneDeep(pipelineData)}));
this.store.dispatch(updatePipeline({changes: pipelineData}))
// reactFlowInstance.addNodes(newNode);
}
}
return [deactivateLoader(createPipelineStep.type), addMessage(MESSAGES_SEVERITY.SUCCESS, 'Added new step to pipeline')];
}),
catchError(err => {
return [
@ -111,8 +202,26 @@ export class PipelinesEffects {
})))
));
// not using
getExperimentById$ = createEffect(() => this.actions.pipe(
ofType(getExperimentById),
switchMap((action) => this.experimentsApiService.tasksGetByIdEx({
...action.getExperimentByIdRequest,
// eslint-disable-next-line @typescript-eslint/naming-convention
only_fields: ['name', 'comment', 'parent.name', 'parent.project.id', 'runtime', 'configuration', 'status']
}).pipe(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
mergeMap((res) => {
return [/* setSelectedPipelineStep({step: res?.tasks[0]}), */ deactivateLoader(getExperimentById.type)]
}
),
catchError(error => [
requestFailed(error),
deactivateLoader(action.type),
])
))
));
getAllExperiments$ = createEffect(() => this.actions.pipe(
@ -124,7 +233,7 @@ export class PipelinesEffects {
},
size: 20,
// user: this.store.select(selectCurrentUser)?.id,
only_fields: ['name', 'created', 'status', 'type', 'user.name', 'id', 'company'],
only_fields: ['name', 'status', 'type', 'user.name', 'id', 'hyperparams'],
// order_by: orderBy,
// type: [excludedKey, 'annotation_manual', excludedKey, 'annotation', excludedKey, 'dataset_import'],
// system_tags: ['-archived', '-pipeline', '-dataset'],
@ -142,22 +251,46 @@ export class PipelinesEffects {
// map(action => activeLoader(action.type))
// ));
// updateProject = createEffect(() => this.actions.pipe(
// ofType(updateProject),
// mergeMap(action => this.projectsApi.projectsUpdate({project: action.id, ...action.changes})
// .pipe(
// mergeMap((res: ProjectsUpdateResponse) => [
// deactivateLoader(action.type),
// updateProjectSuccess({id: action.id, changes: res.fields})
// ]),
// catchError(error => [deactivateLoader(action.type), requestFailed(error),
// setServerError(error, undefined, error?.error?.meta?.result_subcode === 800 ?
// 'Name should be 3 characters long' : error?.error?.meta?.result_subcode === 801 ? 'Name' +
// ' already' +
// ' exists in this project' : undefined)])
// )
// )
// ));
updatePipeline = createEffect(() => this.actions.pipe(
ofType(updatePipeline),
mergeMap(action => this.pipelinesApiService.pipelinesUpdate({...action.changes})
.pipe(
mergeMap((res: PipelinesUpdateResponse) => [
deactivateLoader(action.type),
updatePipelineSuccess({changes: res}),
addMessage(MESSAGES_SEVERITY.SUCCESS, 'Pipeline saved successfully')
]),
catchError(error => [deactivateLoader(action.type), requestFailed(error),
setServerError(error, undefined, error?.error?.meta?.result_subcode === 800 ?
'Name should be 3 characters long' : error?.error?.meta?.result_subcode === 801 ? 'Name' +
' already' +
' exists in this pipeline' : undefined)])
)
)
));
getPipelineById$ = createEffect(() => this.actions.pipe(
ofType(getPipelineById),
switchMap((action) => this.pipelinesApiService.pipelinesGetById({
pipeline: action.id,
pipeline_name: action.name,
// eslint-disable-next-line @typescript-eslint/naming-convention
// only_fields: ['name', 'status', 'company.id', 'user.id', 'comment', 'report', 'tags', 'system_tags', 'report_assets', 'project.name']
}).pipe(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
mergeMap((res) => {
return [setSelectedPipeline({data: res?.pipeline}), deactivateLoader(getPipelineById.type)]
}
),
catchError(error => [
requestFailed(error),
deactivateLoader(action.type),
])
)),
));
// getAllProjects = createEffect(() => this.actions.pipe(
// ofType(getAllProjectsPageProjects),

View File

@ -12,6 +12,7 @@ import {
setNoMorePipelines,
setPipelinesOrderBy,
setPipelinesSearchQuery,
setSelectedPipeline,
setTableModeAwareness,
showExampleDatasets,
showExamplePipelines,
@ -43,7 +44,7 @@ export interface PipelineState {
pipelines: Pipeline[];
pipelinesNonFilteredList: Pipeline[];
selectedProjectId: string;
selectedProject: Pipeline;
selectedPipeline: Pipeline;
projectReadyForDeletion: CommonReadyForDeletion;
validatedProject: Pipeline;
noMorePipelines: boolean;
@ -57,7 +58,7 @@ export interface PipelineState {
export const pipelinesInitState: PipelineState = {
pipelines: null,
selectedProjectId: '',
selectedProject: {},
selectedPipeline: {},
orderBy: 'last_update',
sortOrder: TABLE_SORT_ORDER.DESC,
searchQuery: null,
@ -88,12 +89,13 @@ export const pipelinesReducers = [
on(setCurrentScrollId, (state, action) => ({...state, scrollId: action.scrollId})),
on(setNoMorePipelines, (state, action) => ({...state, noMorePipelines: action.payload})),
on(updatePipelineSuccess, (state, action) => ({
...state, pipelines: state.pipelines?.map(pr => pr.id === action.id ? {
...state, pipelines: state.pipelines?.map(pr => pr.id === action.changes.id ? {
...pr,
...action.changes,
...(!!action.changes?.name && {basename: action.changes?.name.split('/').at(-1)})
} : pr)
})),
on(setSelectedPipeline, (state, action) => ({...state, selectedPipeline: {...action.data}})),
on(resetPipelines, state => ({
...state,
scrollId: null,
@ -140,6 +142,7 @@ export const pipelinesReducers = [
...state,
experiments: [...action.experiments],
})),
] as ReducerTypes<PipelineState, ActionCreator[]>[];
export const pipelinesReducer = createReducer(pipelinesInitState, ...pipelinesReducers);
@ -148,7 +151,7 @@ export const pipelines = state => state.pipelines as PipelineState;
export const selectPipelines = createSelector(pipelines, state => state[PIPELINES_KEY]);
export const selectNonFilteredPipelinesList = createSelector(pipelines, state => state?.pipelinesNonFilteredList || []);
// export const selectSelectedProjectId = createSelector(selectRouterParams, (params: any) => params ? params.projectId : '');
export const selectSelectedPipeline = createSelector(pipelines, state => state?.selectedPipeline);
export const selectPipelinesOrderBy = createSelector(pipelines, state => state?.orderBy || '');
export const selectPipelinesSortOrder = createSelector(pipelines, state => state?.sortOrder || TABLE_SORT_ORDER.DESC);
export const selectPipelinesSearchQuery = createSelector(pipelines, state => state?.searchQuery);