mirror of
https://github.com/clearml/clearml-web
synced 2025-04-23 23:54:26 +00:00
Added create pipeline step modal
This commit is contained in:
parent
4db6069900
commit
c482eacf9a
@ -34,7 +34,7 @@ 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 } from "../model/pipelines/models";
|
||||
import { PipelinesCreateRequest, PipelinesCreateResponse, PipelinesCreateStepsRequest, PipelinesCreateStepsResponse } from "../model/pipelines/models";
|
||||
|
||||
@Injectable()
|
||||
export class ApiPipelinesService {
|
||||
@ -225,4 +225,60 @@ export class ApiPipelinesService {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Create a new pipeline step
|
||||
* @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 pipelinesCreateStep(
|
||||
request: PipelinesCreateStepsRequest,
|
||||
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 pipelinesCreate."
|
||||
);
|
||||
}
|
||||
|
||||
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<PipelinesCreateStepsResponse>(
|
||||
`${this.basePath}/pipelines.create.step`,
|
||||
request,
|
||||
{
|
||||
withCredentials: this.configuration.withCredentials,
|
||||
headers: headers,
|
||||
observe: observe,
|
||||
reportProgress: reportProgress,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -2,4 +2,7 @@ export * from '././pipelinesStartPipelineRequest';
|
||||
export * from '././pipelinesStartPipelineResponse';
|
||||
export * from '././pipelinesCreateRequest';
|
||||
export * from '././pipelinesCreateResponse';
|
||||
export * from '././pipelinesCreateStepsRequest';
|
||||
export * from '././pipelinesCreateStepsResponse';
|
||||
|
||||
export * from "././pipeline";
|
@ -0,0 +1,14 @@
|
||||
export interface PipelinesCreateStepsRequest {
|
||||
/**
|
||||
* Pipeline name. Unique within the company.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Free text comment
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
experiment?: string;
|
||||
|
||||
parameters?: Array<object>
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
|
||||
export interface PipelinesCreateStepsResponse {
|
||||
/**
|
||||
* Pipeline id
|
||||
*/
|
||||
id?: string;
|
||||
}
|
@ -39,7 +39,7 @@
|
||||
<button class="btn btn-icon g-btn" smTooltip="Compile pipeline">
|
||||
<i class="icon i-pipeline-compile lm"></i>
|
||||
</button>
|
||||
<button class="btn btn-icon g-btn" smTooltip="Add new step to pipeline">
|
||||
<button class="btn btn-icon g-btn" smTooltip="Add new step to pipeline" (click)="addNewStep()">
|
||||
<i class="icon i-pipeline-add-new-step lm"></i>
|
||||
</button>
|
||||
<button class="btn btn-icon g-btn" smTooltip="Run pipeline">
|
||||
|
@ -19,40 +19,25 @@ import {EntityTypeEnum} from '~/shared/constants/non-common-consts';
|
||||
export class EditPipelineHeaderComponent extends BaseEntityHeaderComponent implements OnInit{
|
||||
private _tableCols: any;
|
||||
toggleButtons: Option[];
|
||||
@Input() isArchived: boolean;
|
||||
@Input() metricVariants: Array<MetricVariantResult>;
|
||||
@Input() hyperParams: any[];
|
||||
@Input() minimizedView: boolean;
|
||||
@Input() isMetricsLoading: boolean;
|
||||
@Input() tableFilters: { [s: string]: FilterMetadata };
|
||||
@Input() sharedView: boolean;
|
||||
@Input() showNavbarLinks: boolean;
|
||||
@Input() tableMode: 'table' | 'info' | 'compare';
|
||||
@Input() compareView: 'scalars' | 'plots';
|
||||
@Input() showCompareScalarSettings: boolean;
|
||||
@Input() rippleEffect: boolean;
|
||||
@Input() addButtonTemplate: TemplateRef<any>;
|
||||
|
||||
|
||||
@Input() set tableCols(tableCols) {
|
||||
this._tableCols = tableCols?.filter(col => col.header !== '');
|
||||
}
|
||||
|
||||
get tableCols() {
|
||||
return this._tableCols;
|
||||
}
|
||||
|
||||
@Output() isArchivedChanged = new EventEmitter<boolean>();
|
||||
@Output() selectedTableColsChanged = new EventEmitter<ISmCol>();
|
||||
@Output() removeColFromList = new EventEmitter<ISmCol['id']>();
|
||||
@Output() getMetricsToDisplay = new EventEmitter();
|
||||
@Output() selectedMetricToShow = new EventEmitter<SelectionEvent>();
|
||||
@Output() selectedHyperParamToShow = new EventEmitter<{param: string; addCol: boolean}>();
|
||||
@Output() setAutoRefresh = new EventEmitter<boolean>();
|
||||
@Output() toggleShowCompareSettings = new EventEmitter<boolean>();
|
||||
@Output() compareViewChanged = new EventEmitter<'scalars' | 'plots'>();
|
||||
@Output() clearSelection = new EventEmitter();
|
||||
@Output() clearTableFilters = new EventEmitter<{ [s: string]: FilterMetadata }>();
|
||||
@Output() tableModeChanged = new EventEmitter<'table' | 'info' | 'compare'>();
|
||||
@Output() createPipelineStep = new EventEmitter();
|
||||
// @Output() selectedTableColsChanged = new EventEmitter<ISmCol>();
|
||||
// @Output() removeColFromList = new EventEmitter<ISmCol['id']>();
|
||||
// @Output() getMetricsToDisplay = new EventEmitter();
|
||||
// @Output() selectedMetricToShow = new EventEmitter<SelectionEvent>();
|
||||
// @Output() selectedHyperParamToShow = new EventEmitter<{param: string; addCol: boolean}>();
|
||||
// @Output() setAutoRefresh = new EventEmitter<boolean>();
|
||||
// @Output() toggleShowCompareSettings = new EventEmitter<boolean>();
|
||||
// @Output() compareViewChanged = new EventEmitter<'scalars' | 'plots'>();
|
||||
// @Output() clearSelection = new EventEmitter();
|
||||
// @Output() clearTableFilters = new EventEmitter<{ [s: string]: FilterMetadata }>();
|
||||
// @Output() tableModeChanged = new EventEmitter<'table' | 'info' | 'compare'>();
|
||||
protected readonly resourceToIconMap = resourceToIconMap;
|
||||
protected readonly trackByValue = trackByValue;
|
||||
|
||||
@ -63,4 +48,8 @@ export class EditPipelineHeaderComponent extends BaseEntityHeaderComponent imple
|
||||
...(this.entityType === EntityTypeEnum.experiment ? [{label: 'Compare view', value: 'compare', icon: 'al-ico-charts-view'}] : [])
|
||||
];
|
||||
}
|
||||
|
||||
addNewStep() {
|
||||
this.createPipelineStep.emit();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<sm-edit-pipeline-header
|
||||
(createPipelineStep)="createPipeline()"
|
||||
>
|
||||
</sm-edit-pipeline-header>
|
||||
<div class="edit-pipeline-body">
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, 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';
|
||||
|
||||
@Component({
|
||||
selector: 'sm-edit-pipeline-page',
|
||||
@ -6,5 +10,30 @@ import { Component } from '@angular/core';
|
||||
styleUrls: ['./edit-pipeline-page.component.scss']
|
||||
})
|
||||
export class EditPipelinePageComponent {
|
||||
protected dialog = inject(MatDialog);
|
||||
protected store = inject(Store);
|
||||
|
||||
createPipeline() {
|
||||
|
||||
this.dialog.open(PipelineAddStepDialogComponent, {
|
||||
data: {defaultExperimentId: ''},
|
||||
panelClass: 'light-theme',
|
||||
width: '640px'
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe(pipeline => {
|
||||
if (pipeline) {
|
||||
this.store.dispatch(createPipelineStep({pipelinesCreateStepRequest: pipeline}));
|
||||
}
|
||||
});
|
||||
|
||||
// this.dialog.open(PipelineDialogComponent, {
|
||||
// data: {
|
||||
// panelClass: 'light-theme',
|
||||
// },
|
||||
// width: '640px'
|
||||
// });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
<sm-dialog-template iconClass="al-ico-pipelines" header="CREATE NEW PIPELINE">
|
||||
<sm-pipeline-add-step-form
|
||||
[experiments]="experiments$| async"
|
||||
[readOnlyExperimentsNames]="readOnlyExperimentsNames$ | async"
|
||||
[defaultExperimentId]="data?.defaultExperimentId"
|
||||
(stepCreated)="createStep($event)"
|
||||
(filterSearchChanged)="filterSearchChanged($event)"
|
||||
></sm-pipeline-add-step-form>
|
||||
</sm-dialog-template>
|
@ -0,0 +1,4 @@
|
||||
:host{
|
||||
width: 640px;
|
||||
display: block;
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
|
||||
import {Store} from '@ngrx/store';
|
||||
import {Observable} from 'rxjs';
|
||||
import {/* getTablesFilterProjectsOptions, */ resetTablesFilterProjectsOptions} from '@common/core/actions/projects.actions';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {isReadOnly} from '@common/shared/utils/is-read-only';
|
||||
import { Task } from '~/business-logic/model/tasks/task';
|
||||
//import { selectExperimentsList } from '@common/experiments/reducers';
|
||||
import { globalFilterChanged } from '@common/experiments/actions/common-experiments-view.actions';
|
||||
import { PipelinesCreateStepsRequest } from '~/business-logic/model/pipelines/pipelinesCreateStepsRequest';
|
||||
import { selectExperiments } from '../pipelines.reducer';
|
||||
import { getAllExperiments } from '../pipelines.actions';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'sm-pipeline-add-step-dialog',
|
||||
templateUrl: './pipeline-add-step-dialog.component.html',
|
||||
styleUrls: ['./pipeline-add-step-dialog.component.scss']
|
||||
})
|
||||
export class PipelineAddStepDialogComponent {
|
||||
public experiments$: Observable<Task[]>;
|
||||
public readOnlyExperimentsNames$: Observable<string[]>;
|
||||
|
||||
constructor(
|
||||
private store: Store,
|
||||
private matDialogRef: MatDialogRef<PipelineAddStepDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { defaultExperimentId: string}
|
||||
) {
|
||||
this.experiments$ = this.store.select(selectExperiments);
|
||||
this.readOnlyExperimentsNames$ = this.store.select(selectExperiments)
|
||||
.pipe(map(experiments => experiments?.filter(experiment => isReadOnly(experiment)).map(experiment=> experiment.name)));
|
||||
this.store.dispatch(resetTablesFilterProjectsOptions());
|
||||
}
|
||||
|
||||
public createStep(pipelineForm) {
|
||||
const pipeline = this.convertFormToPipeline(pipelineForm);
|
||||
this.matDialogRef.close(pipeline);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private convertFormToPipeline(stepForm: any): PipelinesCreateStepsRequest {
|
||||
return {
|
||||
name: stepForm.name,
|
||||
description: stepForm.description,
|
||||
experiment:stepForm.experiment.value,
|
||||
parameters: stepForm.parameters
|
||||
};
|
||||
}
|
||||
|
||||
filterSearchChanged($event: {value: string; loadMore?: boolean}) {
|
||||
!$event.loadMore && this.store.dispatch(getAllExperiments({query: $event.value}));
|
||||
this.store.dispatch(getAllExperiments({query: $event.value || '', /* loadMore: $event.loadMore, allowPublic: false */}));
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<form (submit)="pipelineForm.invalid && send()" #pipelineForm='ngForm' class="d-flex flex-column">
|
||||
<mat-form-field appearance="outline" hideRequiredMarker class="mat-light">
|
||||
<mat-label>Step name</mat-label>
|
||||
<mat-error *ngIf="name.touched && name.errors?.required">*Please add name.</mat-error>
|
||||
<mat-error *ngIf="name.touched && name.errors?.uniqueName">*Step name already exists.</mat-error>
|
||||
<mat-error *ngIf="name.touched && name.errors?.minlength">*Step name should contain more than 3
|
||||
characters.</mat-error>
|
||||
<input name="stepName" [(ngModel)]="step.name" #name="ngModel" matInput autocomplete="off"
|
||||
smUniqueNameValidator [existingNames]="experimentsNames" required minlength="3">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="w-100" appearance="outline"
|
||||
(mousedown)="!isFocused(experimentInputRef) && experimentInput.value && experimentInput.reset(); experimentInputRef.blur(); experimentInputRef.focus()">
|
||||
<mat-label>Experiment</mat-label>
|
||||
<input matInput type="text" [matAutocomplete]="auto" [ngModel]="step.experiment" name="experimentName"
|
||||
placeholder="Search for existing experiments" #experimentInputRef #experimentInput="ngModel" required
|
||||
(keydown.enter)="experimentInput.control.markAsTouched()" [smExistNameValidator]="experimentsNames"
|
||||
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()"
|
||||
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
|
||||
provide a different name as this experiment name is taken as an Example project
|
||||
</mat-error>
|
||||
<mat-autocomplete #auto="matAutocomplete" class="light-theme" [displayWith]="displayFn"
|
||||
(opened)="setIsAutoCompleteOpen(true)" (closed)="setIsAutoCompleteOpen(false)" autoActiveFirstOption>
|
||||
<!-- Currently we don't have create new project in create report-->
|
||||
<!-- <mat-option-->
|
||||
<!-- class="item"-->
|
||||
<!-- *ngIf="projects !== null && experimentInput.value && !(experimentInput.value | stringIncludedInArray:projectsNames)"-->
|
||||
<!-- [value]="experimentInput.value"-->
|
||||
<!-- (onSelectionChange)="createNewSelected($event)"-->
|
||||
<!-- >"{{experimentInput.value}}" <span class="new">(Create New)</span></mat-option>-->
|
||||
<mat-option *ngFor="let experiment of experimentsOptions; trackBy: trackByValue" [value]="experiment"
|
||||
[smTooltip]="experiment.label" smShowTooltipIfEllipsis (onSelectionChange)="experimentSelected($event)">
|
||||
<div [smSearchText]="experimentInput.value">{{experiment.label}}</div>
|
||||
</mat-option>
|
||||
<div *ngIf="experiments === null" class="p-4 pe-none">
|
||||
<mat-spinner class="m-auto" [diameter]="32" [strokeWidth]="4" color="accent"></mat-spinner>
|
||||
</div>
|
||||
<div *ngIf="experiments && !noMoreOptions" (smScrollEnd)="!loading && loadMore(experimentInput.value)"
|
||||
class="text-center">Loading more...</div>
|
||||
<mat-option disabled style="height: 0; min-height: 0;"></mat-option>
|
||||
<!-- Empty mat-option, so the autocomplete menu will always pop -->
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" hideRequiredMarker>
|
||||
<mat-error *ngIf="description?.touched && description?.invalid">*Please add description.
|
||||
</mat-error>
|
||||
<mat-label>Description</mat-label>
|
||||
<textarea class="step-description" name="description" matInput [(ngModel)]="step.description"
|
||||
#description="ngModel"></textarea>
|
||||
</mat-form-field>
|
||||
<div class="w-100 create-step-button">
|
||||
<button class="btn btn-dark-fill center" data-id="Create Step" [disabled]="pipelineForm.invalid"
|
||||
(click)="send()">ADD STEP
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
@ -0,0 +1,12 @@
|
||||
:host {
|
||||
.create-report-button {
|
||||
padding: 32px 12px 0;
|
||||
}
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
|
||||
.report-description {
|
||||
min-height: 68px;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges, OnDestroy,
|
||||
Output,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import {NgModel} from '@angular/forms';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {trackByValue} from '@common/shared/utils/forms-track-by';
|
||||
import {MatOptionSelectionChange} from '@angular/material/core';
|
||||
import {rootProjectsPageSize} from '@common/constants';
|
||||
import {
|
||||
IOption
|
||||
} from '@common/shared/ui-components/inputs/select-autocomplete-with-chips/select-autocomplete-with-chips.component';
|
||||
import { Task } from '~/business-logic/model/tasks/task';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'sm-pipeline-add-step-form',
|
||||
templateUrl: './pipeline-add-step-form.component.html',
|
||||
styleUrls: ['./pipeline-add-step-form.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class PipelineAddStepFormComponent implements OnChanges, OnDestroy {
|
||||
public filteredExperiments$: Observable<{ label: string; value: string }[]>;
|
||||
private _experiments: Task[];
|
||||
public experimentsOptions: { label: string; value: string }[];
|
||||
public trackByValue = trackByValue;
|
||||
public panelHeight: number;
|
||||
private subs = new Subscription();
|
||||
private rootFiltered: boolean;
|
||||
public readonly experimentsRoot = {label: 'My experiment', value: null};
|
||||
@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<object> } = {
|
||||
name: null,
|
||||
description: '',
|
||||
experiment: null,
|
||||
parameters: [],
|
||||
};
|
||||
filterText: string = '';
|
||||
isAutoCompleteOpen: boolean;
|
||||
|
||||
@Input() readOnlyExperimentsNames: string[];
|
||||
@Input() defaultExperimentId: string;
|
||||
public loading: boolean;
|
||||
public noMoreOptions: boolean;
|
||||
private previousLength: number | undefined;
|
||||
|
||||
@Input() set experiments(experiments: Task[]) {
|
||||
|
||||
this.loading = false;
|
||||
this.noMoreOptions = experiments?.length === this.previousLength || experiments?.length < rootProjectsPageSize;
|
||||
this.previousLength = experiments?.length;
|
||||
this._experiments = experiments;
|
||||
this.experimentsOptions = [
|
||||
...((this.rootFiltered || experiments === null) ? [] : [this.experimentsRoot]),
|
||||
...(experiments ? experiments.map(experiment => ({label: experiment.name, value: experiment.id})) : [])
|
||||
];
|
||||
this.experimentsNames = this.experimentsOptions.map(experiment => experiment.label);
|
||||
}
|
||||
|
||||
get experiments() {
|
||||
return this._experiments;
|
||||
}
|
||||
|
||||
@Output() stepCreated = new EventEmitter();
|
||||
@Output() filterSearchChanged = new EventEmitter<{value: string; loadMore?: boolean}>();
|
||||
|
||||
ngOnInit(): void {
|
||||
this.searchChanged(['*', null].includes(this.defaultExperimentId) ? '' : this.defaultExperimentId);
|
||||
setTimeout(() => {
|
||||
this.subs.add(this.experimentInput.valueChanges.subscribe(searchString => {
|
||||
if (searchString !== this.step.experiment) {
|
||||
this.searchChanged(searchString?.label || searchString || '');
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subs.unsubscribe();
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
if (this.experiments?.length > 0 && this.step.experiment === null) {
|
||||
this.step.experiment = this.experimentsOptions.find(p => p.value === this.defaultExperimentId) || {label: this.experimentsRoot.label, value: null};
|
||||
this.experimentInput.control.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
|
||||
createNewSelected($event: MatOptionSelectionChange) {
|
||||
this.step.experiment = {label: $event.source.value, value: null};
|
||||
}
|
||||
|
||||
experimentSelected($event: MatOptionSelectionChange) {
|
||||
this.step.experiment = {label: $event.source.value.label, value: $event.source.value.value};
|
||||
}
|
||||
setIsAutoCompleteOpen(focus: boolean) {
|
||||
this.isAutoCompleteOpen = focus;
|
||||
}
|
||||
|
||||
displayFn(experiment: IOption | string) {
|
||||
return typeof experiment === 'string' ? experiment : experiment?.label;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.experimentInput.control.setValue('');
|
||||
}
|
||||
|
||||
send() {
|
||||
this.stepCreated.emit(this.step);
|
||||
}
|
||||
|
||||
searchChanged(searchString: string) {
|
||||
this.experimentsOptions = null;
|
||||
this.experimentsNames = null;
|
||||
this.rootFiltered = searchString && !this.experimentsRoot.label.toLowerCase().includes(searchString.toLowerCase());
|
||||
searchString !== null && this.filterSearchChanged.emit({value: searchString, loadMore: false});
|
||||
}
|
||||
|
||||
loadMore(searchString) {
|
||||
this.loading = true;
|
||||
this.filterSearchChanged.emit({value: searchString || '', loadMore: true});
|
||||
}
|
||||
|
||||
isFocused(locationRef: HTMLInputElement) {
|
||||
return document.activeElement === locationRef;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ 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 { PipelinesCreateStepsRequest } from '~/business-logic/model/pipelines/pipelinesCreateStepsRequest';
|
||||
import { Task } from '~/business-logic/model/tasks/task';
|
||||
|
||||
export const PIPELINES_PREFIX = 'PIPELINES_';
|
||||
|
||||
@ -10,7 +12,21 @@ export const createPipeline = createAction(
|
||||
props<{ pipelinesCreateRequest: PipelinesCreateRequest }>()
|
||||
);
|
||||
|
||||
export const createPipelineStep = createAction(
|
||||
PIPELINES_PREFIX + 'CREATE_PIPELINE_STEP',
|
||||
props<{ pipelinesCreateStepRequest: PipelinesCreateStepsRequest }>()
|
||||
);
|
||||
|
||||
export const getAllExperiments = createAction(
|
||||
PIPELINES_PREFIX + 'GET_EXPERIMENTS',
|
||||
props<{ query: string; regExp?: boolean }>()
|
||||
);
|
||||
|
||||
|
||||
export const setExperimentsResults = createAction(
|
||||
PIPELINES_PREFIX + 'SET_EXPERIMENTS',
|
||||
props<{ experiments: Task[]}>()
|
||||
);
|
||||
|
||||
export const updateProject = createAction(
|
||||
PIPELINES_PREFIX + '[update pipeline]',
|
||||
|
@ -6,7 +6,7 @@ import {catchError, filter, map, mergeMap, switchMap, /* tap */} from 'rxjs/oper
|
||||
import {activeLoader, /* addMessage, */ deactivateLoader, setServerError} from '../core/actions/layout.actions';
|
||||
import {requestFailed} from '../core/actions/http.actions';
|
||||
import {
|
||||
createPipeline
|
||||
createPipeline, createPipelineStep, getAllExperiments, setExperimentsResults
|
||||
} from './pipelines.actions';
|
||||
// import {ApiReportsService} from '~/business-logic/api-services/reports.service';
|
||||
/* import {IReport, PAGE_SIZE} from './reports.consts';
|
||||
@ -30,10 +30,11 @@ import {
|
||||
selectSelectedProjectId
|
||||
} from '../core/reducers/projects.reducer';
|
||||
import {TABLE_SORT_ORDER} from '../shared/ui-components/data/table/table.consts';
|
||||
import {selectCurrentUser, selectShowOnlyUserWork} from '../core/reducers/users-reducer';
|
||||
|
||||
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 {
|
||||
ChangeProjectDialogComponent
|
||||
} from '@common/experiments/shared/components/change-project-dialog/change-project-dialog.component';
|
||||
@ -45,6 +46,8 @@ import {selectActiveWorkspaceReady} from '~/core/reducers/view.reducer';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import { PipelinesCreateResponse } from '~/business-logic/model/pipelines/pipelinesCreateResponse';
|
||||
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 {selectRouterParams} from '@common/core/reducers/router-reducer';
|
||||
import {setMainPageTagsFilter} from '@common/core/actions/projects.actions';
|
||||
import {cleanTag} from '@common/shared/utils/helpers.util';
|
||||
@ -59,6 +62,7 @@ export class PipelinesEffects {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private pipelinesApiService: ApiPipelinesService,
|
||||
private experimentsApiService: ApiTasksService,
|
||||
private http: HttpClient,
|
||||
private matDialog: MatDialog,
|
||||
// public projectsApi: ApiProjectsService,
|
||||
@ -66,7 +70,7 @@ export class PipelinesEffects {
|
||||
}
|
||||
|
||||
activeLoader = createEffect(() => this.actions.pipe(
|
||||
ofType(/* getReports, getReport, */ createPipeline,/* updateReport, restoreReport, archiveReport */),
|
||||
ofType(/* getReports, getReport, */ createPipeline, createPipelineStep, getAllExperiments/* updateReport, restoreReport, archiveReport */),
|
||||
filter(action => !action['refresh']),
|
||||
map(action => activeLoader(action.type))
|
||||
));
|
||||
@ -88,6 +92,51 @@ export class PipelinesEffects {
|
||||
})))
|
||||
));
|
||||
|
||||
|
||||
createPipelineStep$ = createEffect(() => this.actions.pipe(
|
||||
ofType(createPipelineStep),
|
||||
switchMap((action) => 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)];
|
||||
}),
|
||||
catchError(err => {
|
||||
return [
|
||||
requestFailed(err),
|
||||
setServerError(err, null, 'failed to create a new pipeline step'),
|
||||
deactivateLoader(createPipelineStep.type),
|
||||
]
|
||||
})))
|
||||
));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
getAllExperiments$ = createEffect(() => this.actions.pipe(
|
||||
ofType(getAllExperiments),
|
||||
switchMap((action) => this.experimentsApiService.tasksGetAllEx({
|
||||
_any_: {
|
||||
pattern: action.query ? action.query : '',
|
||||
fields: ['name', 'id']
|
||||
},
|
||||
size: 20,
|
||||
// user: this.store.select(selectCurrentUser)?.id,
|
||||
only_fields: ['name', 'created', 'status', 'type', 'user.name', 'id', 'company'],
|
||||
// order_by: orderBy,
|
||||
// type: [excludedKey, 'annotation_manual', excludedKey, 'annotation', excludedKey, 'dataset_import'],
|
||||
// system_tags: ['-archived', '-pipeline', '-dataset'],
|
||||
search_hidden: false,
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}).pipe(
|
||||
mergeMap(res => [setExperimentsResults({
|
||||
experiments: res.tasks,
|
||||
}), deactivateLoader(getAllExperiments.type)]),
|
||||
catchError(error => [deactivateLoader(getAllExperiments.type), requestFailed(error)])))
|
||||
));
|
||||
|
||||
// activeLoader = createEffect(() => this.actions.pipe(
|
||||
// ofType(updateProject, getAllProjectsPageProjects),
|
||||
// map(action => activeLoader(action.type))
|
||||
|
@ -51,6 +51,8 @@ import { PipelineState, pipelinesReducer, PIPELINES_KEY } from "./pipelines.redu
|
||||
import { UserPreferences } from "@common/user-preferences";
|
||||
import { createUserPrefFeatureReducer } from "@common/core/meta-reducers/user-pref-reducer";
|
||||
import { PIPELINES_PREFIX } from "./pipelines.actions";
|
||||
import { PipelineAddStepDialogComponent } from "./pipeline-add-step-dialog/pipeline-add-step-dialog.component";
|
||||
import { PipelineAddStepFormComponent } from "./pipeline-add-step-dialog/pipeline-add-step-form/pipeline-add-step-form.component";
|
||||
|
||||
export const pipelinesSyncedKeys = ["projects.showPipelineExamples"];
|
||||
const pipelinesSyncedKeys2 = ['orderBy', 'sortOrder'];
|
||||
@ -96,7 +98,9 @@ const getInitState = (userPreferences: UserPreferences) => ({
|
||||
declarations: [
|
||||
PipelinesPageComponent,
|
||||
PipelineDialogComponent,
|
||||
PipelineAddStepDialogComponent,
|
||||
CreateNewPipelineFormComponent,
|
||||
PipelineAddStepFormComponent,
|
||||
EditPipelinePageComponent,
|
||||
EditPipelineHeaderComponent,
|
||||
],
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
resetPipelinesSearchQuery,
|
||||
resetReadyToDelete,
|
||||
setCurrentScrollId,
|
||||
setExperimentsResults,
|
||||
setNoMorePipelines,
|
||||
setPipelinesOrderBy,
|
||||
setPipelinesSearchQuery,
|
||||
@ -18,7 +19,7 @@ import {
|
||||
} from './pipelines.actions';
|
||||
import {SearchState} from '../common-search/common-search.reducer';
|
||||
import { Pipeline } from '~/business-logic/model/pipelines/pipeline';
|
||||
|
||||
import { Task } from '~/business-logic/model/tasks/task';
|
||||
|
||||
|
||||
export const PIPELINES_KEY = 'pipelines';
|
||||
@ -50,6 +51,7 @@ export interface PipelineState {
|
||||
tableModeAwareness: boolean;
|
||||
showPipelineExamples: boolean;
|
||||
showDatasetExamples: boolean;
|
||||
experiments: Task[];
|
||||
}
|
||||
|
||||
export const pipelinesInitState: PipelineState = {
|
||||
@ -67,6 +69,7 @@ export const pipelinesInitState: PipelineState = {
|
||||
tableModeAwareness: true,
|
||||
showPipelineExamples: false,
|
||||
showDatasetExamples: false,
|
||||
experiments: null
|
||||
};
|
||||
|
||||
const getCorrectSortingOrder = (currentSortOrder: TableSortOrderEnum, currentOrderField: string, nextOrderField: string) => {
|
||||
@ -132,7 +135,11 @@ export const pipelinesReducers = [
|
||||
on(setTableModeAwareness, (state, action) =>
|
||||
({...state, tableModeAwareness: (action as ReturnType<typeof setTableModeAwareness>).awareness})),
|
||||
on(showExamplePipelines, state => ({...state, showPipelineExamples: true})),
|
||||
on(showExampleDatasets, state => ({...state, showDatasetExamples: true}))
|
||||
on(showExampleDatasets, state => ({...state, showDatasetExamples: true})),
|
||||
on(setExperimentsResults, (state, action) => ({
|
||||
...state,
|
||||
experiments: [...action.experiments],
|
||||
})),
|
||||
] as ReducerTypes<PipelineState, ActionCreator[]>[];
|
||||
export const pipelinesReducer = createReducer(pipelinesInitState, ...pipelinesReducers);
|
||||
|
||||
@ -156,3 +163,4 @@ export const selectPipelinesScrollId = createSelector(pipelines, (state): string
|
||||
export const selectTableModeAwareness = createSelector(pipelines, state => state?.tableModeAwareness);
|
||||
export const selectShowPipelineExamples = createSelector(pipelines, state => state?.showPipelineExamples);
|
||||
export const selectShowDatasetExamples = createSelector(pipelines, state => state?.showDatasetExamples);
|
||||
export const selectExperiments = createSelector(pipelines, state => state?.experiments);
|
Loading…
Reference in New Issue
Block a user