Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Workflow Button in Dataset Explorer Page #2622

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,31 @@ <h6 style="font-weight: lighter; font-size: 0.9em">Choose a Version:</h6>
(click)="onClickOpenVersionCreator()">
+ Version
</button>
<button
nz-button
nzType="primary"
*ngIf="userHasWriteAccess()"
class="rounded-button"
(click)="onClickCreateWorkflowFromDataset()">
Create Workflow
</button>
<br />
<div style="padding: 20px">
<label for="scan-options">Select an option:</label>
<select
id="scan-options"
name="scan-options"
[(ngModel)]="scanOption">
<option value="JSONLFileScan">JSONL File Scan</option>
<option value="CSVFileScan">CSV File Scan</option>
<option value="TextInput">Text Input</option>
<option
value="FileScan"
selected>
File Scan
</option>
</select>
</div>
</div>
</nz-sider>
</nz-layout>
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ import {
import { DatasetVersion } from "../../../../../common/type/dataset";
import { switchMap } from "rxjs/operators";
import { NotificationService } from "../../../../../common/service/notification/notification.service";

import { Injectable } from "@angular/core";
import { UserWorkflowService } from "../../../service/user-workflow/user-workflow.service";
import { EnvironmentService } from "../../../service/user-environment/environment.service";
import { WorkflowPersistService } from "src/app/common/service/workflow-persist/workflow-persist.service";
import { WorkflowActionService } from "src/app/workspace/service/workflow-graph/model/workflow-action.service";
import { Point } from "plotly.js-basic-dist-min";
@UntilDestroy()
@Component({
templateUrl: "./user-dataset-explorer.component.html",
styleUrls: ["./user-dataset-explorer.component.scss"],
})
export class UserDatasetExplorerComponent implements OnInit {
public scanOption: string = "";
public did: number | undefined;
public datasetName: string = "";
public datasetDescription: string = "";
Expand All @@ -41,7 +47,10 @@ export class UserDatasetExplorerComponent implements OnInit {
private route: ActivatedRoute,
private router: Router,
private datasetService: DatasetService,
private notificationService: NotificationService
private notificationService: NotificationService,
private userWorkflowService: UserWorkflowService,
private environmentService: EnvironmentService,
private workflowPersistService: WorkflowPersistService
) {}

// item for control the resizeable sider
Expand Down Expand Up @@ -224,6 +233,51 @@ export class UserDatasetExplorerComponent implements OnInit {
this.isRightBarCollapsed = !this.isRightBarCollapsed;
}

onClickCreateWorkflowFromDataset(): void {
const datasetFile: string = "/" + this.datasetName + this.currentDisplayedFileName;
this.userWorkflowService
.onClickCreateNewWorkflowFromDatasetDashboard(datasetFile, this.scanOption) // initializes a scan operator in workflow
.pipe(untilDestroyed(this))
.subscribe({
next: wid => {
if (wid && this.did) {
// initialize workflow action service
this.retrieveEnvironmentAndAddDataset(wid);
}
},
});
}

private retrieveEnvironmentAndAddDataset(wid: number): void {
this.workflowPersistService
.retrieveWorkflowEnvironment(wid)
.pipe(untilDestroyed(this))
.subscribe({
next: env => {
if (env.eid && this.did) {
this.addDatasetToEnvironmentAndNavigate(env.eid, wid);
}
},
});
}

private addDatasetToEnvironmentAndNavigate(eid: number, wid: number): void {
if (this.did != null && this.did != undefined) {
this.environmentService
.addDatasetToEnvironment(eid, this.did)
.pipe(untilDestroyed(this))
.subscribe({
next: response => {
this.navigateToWorkflowPage(wid);
},
});
}
}

private navigateToWorkflowPage(wid: number): void {
this.router.navigate([`/workflow/${wid}`]);
}

onVersionSelected(version: DatasetVersion): void {
this.selectedVersion = version;
if (this.did && this.selectedVersion.dvid)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { Injectable } from "@angular/core";
import { Input, ViewChild } from "@angular/core";
import { Router } from "@angular/router";
import { NzModalService } from "ng-zorro-antd/modal";
import { firstValueFrom, observable, of } from "rxjs";
import {
DEFAULT_WORKFLOW_NAME,
WorkflowPersistService,
} from "../../../../common/service/workflow-persist/workflow-persist.service";
import { DashboardEntry } from "../../type/dashboard-entry";
import { UserService } from "../../../../common/service/user/user.service";
import { untilDestroyed } from "@ngneat/until-destroy";
import { NotificationService } from "../../../../common/service/notification/notification.service";
import { WorkflowContent } from "../../../../common/type/workflow";
import { FileSaverService } from "../../service/user-file/file-saver.service";
import { FiltersComponent } from "../../component/filters/filters.component";
import { SearchResultsComponent } from "../../component/search-results/search-results.component";
import { SearchService } from "../../service/search.service";
import { SortMethod } from "../../type/sort-method";
import { isDefined } from "../../../../common/util/predicate";
import { UserProjectService } from "../../service/user-project/user-project.service";
import { map, mergeMap, tap } from "rxjs/operators";
import { Observable } from "rxjs";
import { WorkflowActionService } from "../../../../workspace/service/workflow-graph/model/workflow-action.service";
import { WorkflowUtilService } from "../../../../workspace/service/workflow-graph/util/workflow-util.service";

import { observe } from "@ngx-formly/core/lib/utils";
import { Point } from "../../../../workspace/types/workflow-common.interface";

/**
* UserWorkflowService facilitates creating a new workflow from the dataset dashboard
*/

@Injectable({
providedIn: "root",
})
export class UserWorkflowService {
public ROUTER_WORKFLOW_BASE_URL = "workflow";
private _searchResultsComponent?: SearchResultsComponent;
@ViewChild(SearchResultsComponent) get searchResultsComponent(): SearchResultsComponent {
if (this._searchResultsComponent) {
return this._searchResultsComponent;
}
throw new Error("Property cannot be accessed before it is initialized.");
}
set searchResultsComponent(value: SearchResultsComponent) {
this._searchResultsComponent = value;
}
private _filters?: FiltersComponent;
@ViewChild(FiltersComponent) get filters(): FiltersComponent {
if (this._filters) {
return this._filters;
}
throw new Error("Property cannot be accessed before it is initialized.");
}
set filters(value: FiltersComponent) {
value.masterFilterListChange.pipe(untilDestroyed(this)).subscribe({ next: () => this.search() });
this._filters = value;
}
private masterFilterList: ReadonlyArray<string> | null = null;

// receive input from parent components (UserProjectSection), if any
@Input() public pid?: number = undefined;
@Input() public accessLevel?: string = undefined;
public sortMethod = SortMethod.EditTimeDesc;
lastSortMethod: SortMethod | null = null;

constructor(
private workflowPersistService: WorkflowPersistService,
private userProjectService: UserProjectService,
private searchService: SearchService,
private workflowUtilService: WorkflowUtilService
) {}

/* Creates a workflow from the dataset dashboard with a pre-initialized scan operator*/
public onClickCreateNewWorkflowFromDatasetDashboard(
datasetFile: string,
scanOption: string
): Observable<number | undefined> {
let operatorPredicate = this.workflowUtilService.getNewOperatorPredicate(scanOption);
operatorPredicate = this.workflowUtilService.addFileName(operatorPredicate, datasetFile); // add the filename

let localPid = this.pid;
let point: Point = { x: 474, y: 235 };
let emptyWorkflowContent: WorkflowContent = {
operators: [operatorPredicate],
commentBoxes: [],
groups: [],
links: [],
operatorPositions: {
[operatorPredicate.operatorID]: { x: 474, y: 235 },
},
};

return this.workflowPersistService.createWorkflow(emptyWorkflowContent, DEFAULT_WORKFLOW_NAME).pipe(
tap(createdWorkflow => {
if (!createdWorkflow.workflow.wid) {
throw new Error("Workflow creation failed.");
}
}),
mergeMap(createdWorkflow => {
// Check if localPid is defined; if so, add the workflow to the project
if (localPid) {
return this.userProjectService.addWorkflowToProject(localPid, createdWorkflow.workflow.wid!).pipe(
// Regardless of the project addition outcome, pass the wid downstream
map(() => createdWorkflow.workflow.wid)
);
} else {
// If there's no localPid, skip adding to the project and directly pass the wid downstream
return of(createdWorkflow.workflow.wid);
}
})
//untilDestroyed(this)
);
}

/**
* Searches workflows with keywords and filters given in the masterFilterList.
* @returns
*/
async search(forced: Boolean = false): Promise<void> {
const sameList =
this.masterFilterList !== null &&
this.filters.masterFilterList.length === this.masterFilterList.length &&
this.filters.masterFilterList.every((v, i) => v === this.masterFilterList![i]);
if (!forced && sameList && this.sortMethod === this.lastSortMethod) {
// If the filter lists are the same, do no make the same request again.
return;
}
this.lastSortMethod = this.sortMethod;
this.masterFilterList = this.filters.masterFilterList;
let filterParams = this.filters.getSearchFilterParameters();
if (isDefined(this.pid)) {
// force the project id in the search query to be the current pid.
filterParams.projectIds = [this.pid];
}
this.searchResultsComponent.reset(async (start, count) => {
const results = await firstValueFrom(
this.searchService.search(
this.filters.getSearchKeywords(),
filterParams,
start,
count,
"workflow",
this.sortMethod
)
);
return {
entries: results.results.map(i => {
if (i.workflow) {
return new DashboardEntry(i.workflow);
} else {
throw new Error("Unexpected type in SearchResult.");
}
}),
more: results.more,
};
});
await this.searchResultsComponent.loadMore();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,17 @@ export class WorkflowUtilService {
outputPorts,
};
}

/* Helper function for initializing scan operators with file to scan included.*/
public addFileName(op: OperatorPredicate, fileName: string): OperatorPredicate {
const updatedPredicate: OperatorPredicate = { ...op };
const updatedProperties = { ...updatedPredicate.operatorProperties, fileName };

const updatedOperatorPredicate: OperatorPredicate = {
...updatedPredicate,
operatorProperties: updatedProperties,
};

return updatedOperatorPredicate;
}
}
Loading