From f9c8ec4f6a85368c949e44bb7db110757a3f907d Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 28 Apr 2025 16:12:08 -0700 Subject: [PATCH 001/104] finish the frontend refinement --- .../power-button/computing-unit-selection.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/core/gui/src/app/workspace/component/power-button/computing-unit-selection.component.html b/core/gui/src/app/workspace/component/power-button/computing-unit-selection.component.html index ecd70042b7c..8c299085b00 100644 --- a/core/gui/src/app/workspace/component/power-button/computing-unit-selection.component.html +++ b/core/gui/src/app/workspace/component/power-button/computing-unit-selection.component.html @@ -137,6 +137,7 @@ nz-menu-divider>
  • Date: Tue, 29 Apr 2025 08:03:15 -0700 Subject: [PATCH 002/104] add computing unit id to the execution request --- .../computing-unit-status/computing-unit-status.service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/gui/src/app/workspace/service/computing-unit-status/computing-unit-status.service.ts b/core/gui/src/app/workspace/service/computing-unit-status/computing-unit-status.service.ts index 978ffc668d2..3b9f1956e34 100644 --- a/core/gui/src/app/workspace/service/computing-unit-status/computing-unit-status.service.ts +++ b/core/gui/src/app/workspace/service/computing-unit-status/computing-unit-status.service.ts @@ -275,4 +275,11 @@ export class ComputingUnitStatusService implements OnDestroy { public getSelectedComputingUnitValue(): DashboardWorkflowComputingUnit | null { return this.selectedUnitSubject.value; } + + /** + * Get the current selected computing unit value synchronously + */ + public getSelectedComputingUnitValue(): DashboardWorkflowComputingUnit | null { + return this.selectedUnitSubject.value; + } } From 7a5a297f4bebb7d79f63b8742119c9e0e0b87f60 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 7 May 2025 14:24:51 -0700 Subject: [PATCH 003/104] incorporate with cuid changes --- core/dao/src/main/resources/sql/texera_db.sql | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 core/dao/src/main/resources/sql/texera_db.sql diff --git a/core/dao/src/main/resources/sql/texera_db.sql b/core/dao/src/main/resources/sql/texera_db.sql new file mode 100644 index 00000000000..59f1d1d722c --- /dev/null +++ b/core/dao/src/main/resources/sql/texera_db.sql @@ -0,0 +1,13 @@ +CREATE TABLE IF NOT EXISTS execution ( + eid SERIAL PRIMARY KEY, + wid INTEGER, + uid INTEGER, + name TEXT, + creation_time TIMESTAMP, + last_modified_time TIMESTAMP, + status TEXT, + result TEXT, + stats TEXT, + cuid INTEGER REFERENCES workflow_computing_unit(cuid) ON DELETE SET NULL, + CONSTRAINT uid_wid_fk FOREIGN KEY (uid, wid) REFERENCES workflow (uid, wid) ON DELETE CASCADE +); \ No newline at end of file From b54e88e8432d6411e5510865356dfb0734821a60 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 8 May 2025 17:54:48 -0700 Subject: [PATCH 004/104] finish the first version --- deployment/k8s/texera-helmchart/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment/k8s/texera-helmchart/values.yaml b/deployment/k8s/texera-helmchart/values.yaml index 3ea6fd300bd..b36b2797956 100644 --- a/deployment/k8s/texera-helmchart/values.yaml +++ b/deployment/k8s/texera-helmchart/values.yaml @@ -128,7 +128,7 @@ workflowComputingUnitManager: name: workflow-computing-unit-manager numOfPods: 1 serviceAccountName: workflow-computing-unit-manager-service-account - imageName: texera/workflow-computing-unit-managing-service:cluster + imageName: bobbai/computing-unit-managing-service:cu-refactor service: type: ClusterIP port: 8888 @@ -177,7 +177,7 @@ workflowComputingUnitPool: name: texera-workflow-computing-unit # Note: the namespace of the workflow computing unit pool might conflict when there are multiple texera deployments in the same cluster namespace: texera-workflow-computing-unit-pool - imageName: texera/computing-unit-master:cluster + imageName: bobbai/computing-unit-master:refactor-cu service: port: 8085 targetPort: 8085 From c261665bdbb1825c9c795b23114b60ce3aa075db Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sun, 11 May 2025 16:06:45 -0700 Subject: [PATCH 005/104] add cuid polling --- .../workflow-computing-unit-managing.service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/gui/src/app/workspace/service/workflow-computing-unit/workflow-computing-unit-managing.service.ts b/core/gui/src/app/workspace/service/workflow-computing-unit/workflow-computing-unit-managing.service.ts index 5091ea0522b..27f9b0cb8df 100644 --- a/core/gui/src/app/workspace/service/workflow-computing-unit/workflow-computing-unit-managing.service.ts +++ b/core/gui/src/app/workspace/service/workflow-computing-unit/workflow-computing-unit-managing.service.ts @@ -179,4 +179,10 @@ export class WorkflowComputingUnitManagingService { .get(`${AppSettings.getApiEndpoint()}/${COMPUTING_UNIT_BASE_URL}/${cuid}`) .pipe(map(raw => this.parseDashboardUnit(raw))); } + + public getComputingUnit(cuid: number): Observable { + return this.http.get( + `${AppSettings.getApiEndpoint()}/${COMPUTING_UNIT_BASE_URL}/${cuid}` + ); + } } From 226ae19ed952c7aa353f6112d799fa90cbaa66f5 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sun, 11 May 2025 23:15:52 -0700 Subject: [PATCH 006/104] recover the image names --- deployment/k8s/texera-helmchart/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment/k8s/texera-helmchart/values.yaml b/deployment/k8s/texera-helmchart/values.yaml index b36b2797956..891b3116eee 100644 --- a/deployment/k8s/texera-helmchart/values.yaml +++ b/deployment/k8s/texera-helmchart/values.yaml @@ -128,7 +128,7 @@ workflowComputingUnitManager: name: workflow-computing-unit-manager numOfPods: 1 serviceAccountName: workflow-computing-unit-manager-service-account - imageName: bobbai/computing-unit-managing-service:cu-refactor + imageName: texera/computing-unit-managing-service:cluster service: type: ClusterIP port: 8888 @@ -177,7 +177,7 @@ workflowComputingUnitPool: name: texera-workflow-computing-unit # Note: the namespace of the workflow computing unit pool might conflict when there are multiple texera deployments in the same cluster namespace: texera-workflow-computing-unit-pool - imageName: bobbai/computing-unit-master:refactor-cu + imageName: texera/computing-unit-master:cluster service: port: 8085 targetPort: 8085 From 1e6a605414198b39a3f05a92fba7051ed105ee69 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sun, 11 May 2025 23:18:58 -0700 Subject: [PATCH 007/104] recover the image names --- deployment/k8s/texera-helmchart/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/k8s/texera-helmchart/values.yaml b/deployment/k8s/texera-helmchart/values.yaml index 891b3116eee..3ea6fd300bd 100644 --- a/deployment/k8s/texera-helmchart/values.yaml +++ b/deployment/k8s/texera-helmchart/values.yaml @@ -128,7 +128,7 @@ workflowComputingUnitManager: name: workflow-computing-unit-manager numOfPods: 1 serviceAccountName: workflow-computing-unit-manager-service-account - imageName: texera/computing-unit-managing-service:cluster + imageName: texera/workflow-computing-unit-managing-service:cluster service: type: ClusterIP port: 8888 From d02f2731e3de58222732439472740346f1595a1a Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 12 May 2025 09:38:04 -0700 Subject: [PATCH 008/104] add the header --- core/dao/src/main/resources/sql/texera_db.sql | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 core/dao/src/main/resources/sql/texera_db.sql diff --git a/core/dao/src/main/resources/sql/texera_db.sql b/core/dao/src/main/resources/sql/texera_db.sql deleted file mode 100644 index 59f1d1d722c..00000000000 --- a/core/dao/src/main/resources/sql/texera_db.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE IF NOT EXISTS execution ( - eid SERIAL PRIMARY KEY, - wid INTEGER, - uid INTEGER, - name TEXT, - creation_time TIMESTAMP, - last_modified_time TIMESTAMP, - status TEXT, - result TEXT, - stats TEXT, - cuid INTEGER REFERENCES workflow_computing_unit(cuid) ON DELETE SET NULL, - CONSTRAINT uid_wid_fk FOREIGN KEY (uid, wid) REFERENCES workflow (uid, wid) ON DELETE CASCADE -); \ No newline at end of file From de42fc84a4486df2e3147fbf7260f3313f9e094b Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 12 May 2025 18:46:38 -0700 Subject: [PATCH 009/104] recover some unexpected changes --- deployment/texera-web-application.dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/deployment/texera-web-application.dockerfile b/deployment/texera-web-application.dockerfile index f3464b6bba8..28722707c82 100644 --- a/deployment/texera-web-application.dockerfile +++ b/deployment/texera-web-application.dockerfile @@ -17,9 +17,6 @@ FROM node:18.17 AS build-gui -RUN apt-get update && apt-get install -y --no-install-recommends \ - python3 build-essential git ca-certificates - WORKDIR /gui COPY core/gui /gui RUN rm -f /gui/.yarnrc.yml From c1b81c016ce34641ceee57f1037819b52410a99b Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sun, 13 Apr 2025 17:17:28 -0700 Subject: [PATCH 010/104] add initial version of gui --- core/gui/src/app/app.module.ts | 2 + .../left-panel/left-panel.component.ts | 2 + .../workflow-suggestion.component.html | 60 ++ .../workflow-suggestion.component.scss | 110 ++++ .../workflow-suggestion.component.ts | 618 ++++++++++++++++++ 5 files changed, 792 insertions(+) create mode 100644 core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.html create mode 100644 core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.scss create mode 100644 core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.ts diff --git a/core/gui/src/app/app.module.ts b/core/gui/src/app/app.module.ts index 5087fcb0fc7..0dac287d6b5 100644 --- a/core/gui/src/app/app.module.ts +++ b/core/gui/src/app/app.module.ts @@ -67,6 +67,7 @@ import { MiniMapComponent } from "./workspace/component/workflow-editor/mini-map import { MenuComponent } from "./workspace/component/menu/menu.component"; import { OperatorLabelComponent } from "./workspace/component/left-panel/operator-menu/operator-label/operator-label.component"; import { OperatorMenuComponent } from "./workspace/component/left-panel/operator-menu/operator-menu.component"; +import { WorkflowSuggestionComponent } from "./workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component"; import { SettingsComponent } from "./workspace/component/left-panel/settings/settings.component"; import { PropertyEditorComponent } from "./workspace/component/property-editor/property-editor.component"; import { TypeCastingDisplayComponent } from "./workspace/component/property-editor/typecasting-display/type-casting-display.component"; @@ -181,6 +182,7 @@ registerLocaleData(en); WorkspaceComponent, MenuComponent, OperatorMenuComponent, + WorkflowSuggestionComponent, SettingsComponent, PropertyEditorComponent, VersionsListComponent, diff --git a/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts b/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts index 6c8f32c4b9e..a83bb5c8b38 100644 --- a/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts +++ b/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts @@ -28,6 +28,7 @@ import { TimeTravelComponent } from "./time-travel/time-travel.component"; import { SettingsComponent } from "./settings/settings.component"; import { calculateTotalTranslate3d } from "../../../common/util/panel-dock"; import { PanelService } from "../../service/panel/panel.service"; +import { WorkflowSuggestionComponent } from "./workflow-suggestion/workflow-suggestion.component"; import { GuiConfigService } from "../../../common/service/gui-config.service"; @UntilDestroy() @Component({ @@ -49,6 +50,7 @@ export class LeftPanelComponent implements OnDestroy, OnInit, AfterViewInit { items = [ { component: null, title: "", icon: "", enabled: true }, { component: OperatorMenuComponent, title: "Operators", icon: "appstore", enabled: true }, + { component: WorkflowSuggestionComponent, title: "Suggestions", icon: "experiment", enabled: true }, { component: VersionsListComponent, title: "Versions", icon: "schedule", enabled: false }, { component: SettingsComponent, diff --git a/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.html b/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.html new file mode 100644 index 00000000000..fb6c9d94bb0 --- /dev/null +++ b/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.html @@ -0,0 +1,60 @@ +
    +
    +

    Workflow Suggestions

    +

    Click on a suggestion to preview

    +
    + +
    +
    +
    + {{suggestion.description}} +
    + +
    + + + +
    +
    + +
    + No suggestions available +
    +
    +
    diff --git a/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.scss b/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.scss new file mode 100644 index 00000000000..f40da3b5e34 --- /dev/null +++ b/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.scss @@ -0,0 +1,110 @@ +#suggestion-container { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + overflow: hidden; +} + +.header { + padding: 10px; + border-bottom: 1px solid #ddd; + + h3 { + margin-bottom: 5px; + } + + p { + color: #666; + margin-bottom: 0; + font-size: 12px; + } +} + +.suggestions-list { + flex: 1; + overflow-y: auto; + padding: 10px; +} + +.suggestion-item { + border: 1px solid #ddd; + border-radius: 4px; + margin-bottom: 10px; + transition: all 0.3s; + + &:hover { + border-color: #1890ff; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09); + } + + &.active { + border-color: #1890ff; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + + .suggestion-content { + background-color: #e6f7ff; + } + } +} + +.suggestion-content { + padding: 10px; + cursor: pointer; + transition: background-color 0.3s; + + .description { + display: block; + word-break: break-word; + } +} + +.suggestion-actions { + display: flex; + padding: 5px 10px 10px; + flex-wrap: wrap; + gap: 5px; + + button { + flex: 1; + min-width: 80px; + + &:nth-child(1) { + // Apply button + background-color: #52c41a; + border-color: #52c41a; + + &:hover { + background-color: #73d13d; + border-color: #73d13d; + } + } + + &:nth-child(2) { + // Dislike button + color: #f5222d; + border-color: #f5222d; + + &:hover { + background-color: #fff1f0; + } + } + + &:nth-child(3) { + // Cancel button + color: #595959; + border-color: #d9d9d9; + + &:hover { + color: #1890ff; + border-color: #1890ff; + } + } + } +} + +.no-suggestions { + text-align: center; + color: #999; + padding: 20px; +} diff --git a/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.ts b/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.ts new file mode 100644 index 00000000000..90b5469981c --- /dev/null +++ b/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.ts @@ -0,0 +1,618 @@ +import { Component, OnInit, HostListener } from "@angular/core"; +import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; +import { WorkflowActionService } from "../../../service/workflow-graph/model/workflow-action.service"; +import { WorkflowUtilService } from "../../../service/workflow-graph/util/workflow-util.service"; +import { JointUIService } from "../../../service/joint-ui/joint-ui.service"; +import { NzMessageService } from "ng-zorro-antd/message"; +import { WorkflowPersistService } from "../../../../common/service/workflow-persist/workflow-persist.service"; +import { Workflow, WorkflowContent } from "../../../../common/type/workflow"; +import { cloneDeep, isEqual } from "lodash"; +import { DynamicSchemaService } from "../../../service/dynamic-schema/dynamic-schema.service"; + +interface WorkflowSuggestion { + id: string; + description: string; + operatorsToAdd: { + operatorType: string; + position: { x: number; y: number }; + properties?: object; + }[]; + operatorPropertiesToChange: { + operatorId: string; + properties: object; + }[]; + operatorsToDelete: string[]; // IDs of operators to delete + linksToAdd: { + source: { operatorId: string; portId: string }; + target: { operatorId: string; portId: string }; + }[]; + isPreviewActive: boolean; +} + +@UntilDestroy() +@Component({ + selector: "texera-workflow-suggestion", + templateUrl: "workflow-suggestion.component.html", + styleUrls: ["workflow-suggestion.component.scss"], +}) +export class WorkflowSuggestionComponent implements OnInit { + public suggestions: WorkflowSuggestion[] = []; + public activePreviewId: string | null = null; + public canModify = true; + + // Store the workflow state before a preview is applied + private workflowBeforePreview: Workflow | null = null; + + // Store original operator properties to compare with changed properties + private originalOperatorProperties: Map = new Map(); + + // Store property style maps for highlighting changed properties + private propertyStyleMaps: Map> = new Map(); + + // Flag to prevent clicks inside the component from triggering the document click handler + private clickedInside = false; + + constructor( + private workflowActionService: WorkflowActionService, + private workflowUtilService: WorkflowUtilService, + private jointUIService: JointUIService, + private messageService: NzMessageService, + private workflowPersistService: WorkflowPersistService, + private dynamicSchemaService: DynamicSchemaService + ) {} + + ngOnInit(): void { + this.workflowActionService + .getWorkflowModificationEnabledStream() + .pipe(untilDestroyed(this)) + .subscribe(canModify => (this.canModify = canModify)); + + // Listen to operator highlight events to apply property style maps + this.workflowActionService + .getJointGraphWrapper() + .getJointOperatorHighlightStream() + .pipe(untilDestroyed(this)) + .subscribe(operatorIDArray => { + // Handle operatorID which comes as a readonly string array + // Extract the first operator ID from the array + const operatorID = operatorIDArray.length > 0 ? operatorIDArray[0] : ""; + + if (this.propertyStyleMaps.has(operatorID)) { + // Wait for the property editor to appear + setTimeout(() => { + // Apply property style maps to highlight changed properties + const operatorHighlightedEvent = new CustomEvent("operator-highlighted", { + detail: { + operatorID: operatorID, + propertyStyleMap: this.propertyStyleMaps.get(operatorID), + }, + }); + document.dispatchEvent(operatorHighlightedEvent); + + // Add event listener for the property editor component + this.setupPropertyEditorListener(); + }, 100); + } + }); + + // Load mock suggestions when component initializes + this.loadMockSuggestions(); + } + + @HostListener("click") + onClickInside() { + this.clickedInside = true; + } + + @HostListener("document:click") + onClickOutside() { + // Reset the flag for the next click + this.clickedInside = false; + // Remove this functionality - previews should only be cleared via the Cancel button + // No longer clearing preview when clicking outside + } + + private setupPropertyEditorListener(): void { + // Look for elements with specific CSS class that the property editor uses + const formlyFields = document.querySelectorAll(".formly-field"); + + // Try to find and target the property editor + const propertyEditor = document.querySelector(".property-editor-content"); + if (propertyEditor) { + // Find all the formly fields and apply styles + formlyFields.forEach(field => { + const key = field.getAttribute("data-field-key"); + // Apply highlighting for changed properties + const activeOperatorID = this.workflowActionService + .getJointGraphWrapper() + .getCurrentHighlightedOperatorIDs()[0]; + + if (activeOperatorID && this.propertyStyleMaps.has(activeOperatorID)) { + const styleMap = this.propertyStyleMaps.get(activeOperatorID)!; + if (key && styleMap.has(key)) { + const highlightStyle = styleMap.get(key)!; + // Apply the highlighting style to this field + const fieldElement = field.querySelector(".formly-field-content"); + if (fieldElement) { + fieldElement.setAttribute("style", highlightStyle.toString()); + } + } + } + }); + } + } + + private loadMockSuggestions(): void { + this.suggestions = [ + { + id: "suggestion1", + description: "Add a KeywordSearch operator with sentiment analysis", + operatorsToAdd: [ + { + operatorType: "KeywordSearch", + position: { x: 400, y: 300 }, + properties: { + keyword: "climate change", + attributes: ["content", "title"], + }, + }, + { + operatorType: "SentimentAnalysis", + position: { x: 600, y: 300 }, + properties: { + attribute: "content", + resultAttribute: "sentiment", + }, + }, + ], + operatorPropertiesToChange: [ + { + operatorId: "View-Results-1", + properties: { + limit: 20, + offset: 0, + }, + }, + ], + operatorsToDelete: [], + linksToAdd: [ + { + source: { operatorId: "Source-Scan-1", portId: "output-0" }, + target: { operatorId: "KeywordSearch-1", portId: "input-0" }, + }, + { + source: { operatorId: "KeywordSearch-1", portId: "output-0" }, + target: { operatorId: "SentimentAnalysis-1", portId: "input-0" }, + }, + { + source: { operatorId: "SentimentAnalysis-1", portId: "output-0" }, + target: { operatorId: "View-Results-1", portId: "input-0" }, + }, + ], + isPreviewActive: false, + }, + { + id: "suggestion2", + description: "Replace ScanSource with CSVFileScan for better performance", + operatorsToAdd: [ + { + operatorType: "CSVFileScan", + position: { x: 200, y: 200 }, + properties: { + fileName: "data.csv", + limit: -1, + offset: 0, + schema: "auto", + }, + }, + ], + operatorPropertiesToChange: [], + operatorsToDelete: ["Source-Scan-1"], + linksToAdd: [ + { + source: { operatorId: "CSVFileScan-1", portId: "output-0" }, + target: { operatorId: "View-Results-1", portId: "input-0" }, + }, + ], + isPreviewActive: false, + }, + { + id: "suggestion3", + description: "Enhance workflow with projection and sorting", + operatorsToAdd: [ + { + operatorType: "Projection", + position: { x: 400, y: 200 }, + properties: { + attributes: ["id", "name", "price", "category"], + }, + }, + { + operatorType: "Sort", + position: { x: 600, y: 200 }, + properties: { + sortAttributesList: [ + { + attributeName: "price", + order: "desc", + }, + ], + }, + }, + ], + operatorPropertiesToChange: [ + { + operatorId: "Source-Scan-1", + properties: { + tableName: "products", + limit: 1000, + }, + }, + ], + operatorsToDelete: [], + linksToAdd: [ + { + source: { operatorId: "Source-Scan-1", portId: "output-0" }, + target: { operatorId: "Projection-1", portId: "input-0" }, + }, + { + source: { operatorId: "Projection-1", portId: "output-0" }, + target: { operatorId: "Sort-1", portId: "input-0" }, + }, + { + source: { operatorId: "Sort-1", portId: "output-0" }, + target: { operatorId: "View-Results-1", portId: "input-0" }, + }, + ], + isPreviewActive: false, + }, + ]; + } + + public cancelPreview(): void { + if (this.activePreviewId) { + this.clearPreviewAndRestoreWorkflow(); + this.messageService.info("Preview cancelled"); + } + } + + public togglePreview(suggestion: WorkflowSuggestion): void { + // If there's an active preview, clear it and restore the previous workflow + if (this.activePreviewId) { + this.clearPreviewAndRestoreWorkflow(); + } + + if (this.activePreviewId === suggestion.id) { + // Deactivate preview if clicking the same suggestion (already handled by clearing above) + this.activePreviewId = null; + } else { + // Save the current workflow state before creating the preview + this.saveWorkflowState(); + + // Clear property style maps and original properties + this.propertyStyleMaps.clear(); + this.originalOperatorProperties.clear(); + + // Activate preview for this suggestion + this.activePreviewId = suggestion.id; + this.createPreview(suggestion); + } + } + + private saveWorkflowState(): void { + const currentWorkflow = this.workflowActionService.getWorkflow(); + // Create a deep copy of the workflow to ensure we don't have references to the original + this.workflowBeforePreview = cloneDeep(currentWorkflow); + } + + private restoreWorkflowState(): void { + if (!this.workflowBeforePreview) return; + + // Clear the current workflow + this.workflowActionService.clearWorkflow(); + + // Restore operators and links from the saved workflow state + if (this.workflowBeforePreview.content) { + const content = this.workflowBeforePreview.content; + + // Create array of operators with positions + const operatorsAndPositions = content.operators.map(op => { + const position = content.operatorPositions[op.operatorID]; + return { + op: op, + pos: position, + }; + }); + + // Add all operators and links back to the workflow + this.workflowActionService.addOperatorsAndLinks(operatorsAndPositions, content.links, content.commentBoxes); + } + + // Reset the saved workflow state + this.workflowBeforePreview = null; + + // Clear data structures + this.propertyStyleMaps.clear(); + this.originalOperatorProperties.clear(); + } + + /** + * Calculates which properties have changed between two objects + * @param original The original object + * @param modified The modified object + * @returns Array of property names that have changed + */ + private getChangedPropertyNames(original: object, modified: object): string[] { + const changedProperties: string[] = []; + + // Find all properties that exist in either original or modified + const allKeys = new Set([...Object.keys(original), ...Object.keys(modified)]); + + // Compare each property + allKeys.forEach(key => { + const originalValue = (original as any)[key]; + const modifiedValue = (modified as any)[key]; + + // Check if the property value is different + if (!isEqual(originalValue, modifiedValue)) { + changedProperties.push(key); + } + }); + + return changedProperties; + } + + private createPreview(suggestion: WorkflowSuggestion): void { + const texeraGraph = this.workflowActionService.getTexeraGraph(); + const jointGraph = this.workflowActionService.getJointGraph(); + + // Handle operators to delete first + suggestion.operatorsToDelete.forEach(operatorId => { + if (texeraGraph.hasOperator(operatorId)) { + // Highlight the operator in red before "deleting" it + const operatorCell = jointGraph.getCell(operatorId); + if (operatorCell) { + operatorCell.attr({ + rect: { + fill: "rgba(255, 200, 200, 0.6)", + stroke: "rgba(255, 0, 0, 0.6)", + "stroke-width": 2, + }, + }); + } + } + }); + + // Add preview operators + suggestion.operatorsToAdd.forEach(opToAdd => { + // Create a new operator predicate + const operatorPredicate = this.workflowUtilService.getNewOperatorPredicate(opToAdd.operatorType); + + // Set properties if provided + if (opToAdd.properties) { + Object.assign(operatorPredicate.operatorProperties, opToAdd.properties); + } + + // Add operator to graph + this.workflowActionService.addOperator(operatorPredicate, opToAdd.position); + + // Make the operator semi-transparent to indicate it's a preview + const operatorCell = jointGraph.getCell(operatorPredicate.operatorID); + if (operatorCell) { + operatorCell.attr({ + ".": { opacity: 0.6 }, + rect: { stroke: "#1890ff", "stroke-width": 2 }, + }); + + // Handle operator click to highlight it + operatorCell.on("cell:pointerclick", () => { + // Get the operator ID as a simple string + const opId: string = operatorPredicate.operatorID; + // Now highlight it - use spread syntax to convert single string to varargs + this.workflowActionService.getJointGraphWrapper().highlightOperators(opId); + }); + } + }); + + // Change operator properties for existing operators + suggestion.operatorPropertiesToChange.forEach(propChange => { + const operator = texeraGraph.getOperator(propChange.operatorId); + if (operator) { + // Store original properties for comparison + this.originalOperatorProperties.set(propChange.operatorId, cloneDeep(operator.operatorProperties)); + + // Apply property changes (these will be undone by restoring the workflow state) + const newProperties = { + ...operator.operatorProperties, + ...propChange.properties, + }; + + this.workflowActionService.setOperatorProperty(propChange.operatorId, newProperties); + + // Calculate which properties have changed + const changedPropertyNames = this.getChangedPropertyNames( + this.originalOperatorProperties.get(propChange.operatorId)!, + newProperties + ); + + // Create a map of property styles for highlighting changed properties + const propertyStyleMap = new Map(); + changedPropertyNames.forEach(propName => { + propertyStyleMap.set( + propName, + "background-color: rgba(82, 196, 26, 0.2); border: 1px solid #52c41a; border-radius: 4px;" + ); + }); + + // Store the property style map for this operator + this.propertyStyleMaps.set(propChange.operatorId, propertyStyleMap); + + // Make the operator light green to indicate property changes + const operatorCell = jointGraph.getCell(propChange.operatorId); + if (operatorCell) { + operatorCell.attr({ + rect: { + fill: "rgba(82, 196, 26, 0.2)", + stroke: "#52c41a", + "stroke-width": 2, + }, + }); + + // Handle operator click to highlight it + operatorCell.on("cell:pointerclick", () => { + // Get the operator ID as a simple string + const opId: string = propChange.operatorId; + // Now highlight it - use spread syntax to convert single string to varargs + this.workflowActionService.getJointGraphWrapper().highlightOperators(opId); + }); + } + } + }); + + // Add preview links + suggestion.linksToAdd.forEach(linkToAdd => { + let sourceOperatorId = linkToAdd.source.operatorId; + let targetOperatorId = linkToAdd.target.operatorId; + + if (texeraGraph.hasOperator(sourceOperatorId) && texeraGraph.hasOperator(targetOperatorId)) { + try { + const link = { + linkID: `link-preview-${Date.now()}`, + source: { + operatorID: sourceOperatorId, + portID: linkToAdd.source.portId, + }, + target: { + operatorID: targetOperatorId, + portID: linkToAdd.target.portId, + }, + }; + + this.workflowActionService.addLink(link); + + // Make the link semi-transparent + const linkCell = jointGraph.getCell(link.linkID); + if (linkCell) { + linkCell.attr({ + ".connection": { opacity: 0.6, stroke: "#1890ff" }, + ".marker-target": { opacity: 0.6, fill: "#1890ff" }, + }); + } + } catch (error) { + console.error("Error adding preview link:", error); + } + } + }); + } + + private clearPreviewAndRestoreWorkflow(): void { + // Reset active preview ID + this.activePreviewId = null; + + // Restore the workflow to its state before the preview + this.restoreWorkflowState(); + } + + public applySuggestion(suggestion: WorkflowSuggestion): void { + // First clear any previews and restore the original workflow + this.clearPreviewAndRestoreWorkflow(); + + // Then apply the changes for real + try { + // Delete operators first + if (suggestion.operatorsToDelete.length > 0) { + this.workflowActionService.deleteOperatorsAndLinks(suggestion.operatorsToDelete); + } + + // Add operators + const operatorsToAdd = suggestion.operatorsToAdd.map(opToAdd => { + const operatorPredicate = this.workflowUtilService.getNewOperatorPredicate(opToAdd.operatorType); + + // Set properties if provided + if (opToAdd.properties) { + Object.assign(operatorPredicate.operatorProperties, opToAdd.properties); + } + + return { + op: operatorPredicate, + pos: opToAdd.position, + }; + }); + + // Add all operators at once + this.workflowActionService.addOperatorsAndLinks(operatorsToAdd); + + // Add links + suggestion.linksToAdd.forEach(linkToAdd => { + let sourceOperatorId = linkToAdd.source.operatorId; + let targetOperatorId = linkToAdd.target.operatorId; + + // Map operator IDs if needed + if (!this.workflowActionService.getTexeraGraph().hasOperator(sourceOperatorId)) { + const newOperator = operatorsToAdd.find(op => op.op.operatorType === sourceOperatorId.split("-")[0]); + if (newOperator) sourceOperatorId = newOperator.op.operatorID; + } + + if (!this.workflowActionService.getTexeraGraph().hasOperator(targetOperatorId)) { + const newOperator = operatorsToAdd.find(op => op.op.operatorType === targetOperatorId.split("-")[0]); + if (newOperator) targetOperatorId = newOperator.op.operatorID; + } + + if ( + this.workflowActionService.getTexeraGraph().hasOperator(sourceOperatorId) && + this.workflowActionService.getTexeraGraph().hasOperator(targetOperatorId) + ) { + const link = { + linkID: `link-${Date.now()}`, + source: { + operatorID: sourceOperatorId, + portID: linkToAdd.source.portId, + }, + target: { + operatorID: targetOperatorId, + portID: linkToAdd.target.portId, + }, + }; + + this.workflowActionService.addLink(link); + } + }); + + // Change properties for existing operators + suggestion.operatorPropertiesToChange.forEach(propChange => { + if (this.workflowActionService.getTexeraGraph().hasOperator(propChange.operatorId)) { + const operator = this.workflowActionService.getTexeraGraph().getOperator(propChange.operatorId); + this.workflowActionService.setOperatorProperty(propChange.operatorId, { + ...operator.operatorProperties, + ...propChange.properties, + }); + } + }); + + // Save the workflow to materialize the changes + const workflow = this.workflowActionService.getWorkflow(); + this.workflowPersistService.persistWorkflow(workflow).subscribe(() => { + this.messageService.success("Successfully applied and saved the suggestion!"); + + // Remove the applied suggestion from the list + this.suggestions = this.suggestions.filter(s => s.id !== suggestion.id); + }); + } catch (error) { + console.error("Error applying suggestion:", error); + this.messageService.error("Failed to apply the suggestion."); + } + } + + public dislikeSuggestion(suggestion: WorkflowSuggestion): void { + // If this is the active suggestion, restore the workflow first + if (this.activePreviewId === suggestion.id) { + this.clearPreviewAndRestoreWorkflow(); + } + + // Remove the suggestion from the list + this.suggestions = this.suggestions.filter(s => s.id !== suggestion.id); + + // Show a message to confirm the action + this.messageService.info("Suggestion removed from the list."); + } +} From 390530b37635439e6212db695d504e134a37aca1 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sun, 13 Apr 2025 20:32:09 -0700 Subject: [PATCH 011/104] add initial suggestion service --- core/suggestion-service/.gitignore | 83 +++++++++ core/suggestion-service/model/DataSchema.py | 95 ++++++++++ .../model/EditingOperation.py | 72 ++++++++ core/suggestion-service/model/Operator.py | 102 +++++++++++ core/suggestion-service/model/Port.py | 89 ++++++++++ core/suggestion-service/model/Workflow.py | 65 +++++++ core/suggestion-service/model/__init__.py | 0 .../model/texera/TexeraOperator.py | 94 ++++++++++ .../model/texera/TexeraOperatorTypes.py | 77 ++++++++ .../model/texera/TexeraPort.py | 62 +++++++ .../model/texera/TexeraWorkflow.py | 165 ++++++++++++++++++ .../model/texera/__init__.py | 0 core/suggestion-service/requirements.txt | 11 ++ 13 files changed, 915 insertions(+) create mode 100644 core/suggestion-service/.gitignore create mode 100644 core/suggestion-service/model/DataSchema.py create mode 100644 core/suggestion-service/model/EditingOperation.py create mode 100644 core/suggestion-service/model/Operator.py create mode 100644 core/suggestion-service/model/Port.py create mode 100644 core/suggestion-service/model/Workflow.py create mode 100644 core/suggestion-service/model/__init__.py create mode 100644 core/suggestion-service/model/texera/TexeraOperator.py create mode 100644 core/suggestion-service/model/texera/TexeraOperatorTypes.py create mode 100644 core/suggestion-service/model/texera/TexeraPort.py create mode 100644 core/suggestion-service/model/texera/TexeraWorkflow.py create mode 100644 core/suggestion-service/model/texera/__init__.py create mode 100644 core/suggestion-service/requirements.txt diff --git a/core/suggestion-service/.gitignore b/core/suggestion-service/.gitignore new file mode 100644 index 00000000000..8db194340ae --- /dev/null +++ b/core/suggestion-service/.gitignore @@ -0,0 +1,83 @@ +# Python bytecode +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +dist/ +build/ +*.egg-info/ +*.egg +.eggs/ +.installed.cfg +develop-eggs/ +downloads/ +eggs/ +parts/ +sdist/ +var/ +wheels/ + +# Unit test / coverage reports +.pytest_cache/ +.coverage +htmlcov/ +.tox/ +.nox/ +.hypothesis/ +.pytest_cache/ +coverage.xml +*.cover + +# Virtual environments +venv/ +env/ +ENV/ +.env +.venv +pythonenv* +.python-version + +# Development environments +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# Jupyter Notebook +.ipynb_checkpoints + +# Type checking +.mypy_cache/ +.dmypy.json +dmypy.json +.pyre/ +.pytype/ + +# ML specific +*.model +*.pkl +*.h5 +*.joblib +*.pt +model_checkpoints/ + +# Logs +logs/ +*.log + +# Local development settings +local_settings.py +instance/ + +# Database +*.db +*.sqlite +*.sqlite3 + +# Large media files that might get added accidentally +*.mp4 +*.mov +*.wav +*.mp3 \ No newline at end of file diff --git a/core/suggestion-service/model/DataSchema.py b/core/suggestion-service/model/DataSchema.py new file mode 100644 index 00000000000..723e5294073 --- /dev/null +++ b/core/suggestion-service/model/DataSchema.py @@ -0,0 +1,95 @@ +# the abstraction of data schema and attributes + +## define some constants related to type +### maybe define a class/enum called: AttributeType +### the possible values are: Integer, Long, Double, String, Boolean, binary + + +## the method of Attribute(not an abstract class), +## member variables: +## - name: the name of the attribute, a str +## - type: AttributeType +## methods: Getters for the name and type +## constructor: take the name and type to construct + + +## the method of DataSchema, which should also be a concrete class +## member variables: +## - a set of Attribute + +## methods: +## - the getter to return this set of attributes + +## constructors +## take a list of Attriubte +from enum import Enum +from typing import List, Set + + +# Define the AttributeType enum +from enum import Enum +from typing import List, FrozenSet + + +# Define the AttributeType enum +class AttributeType(Enum): + INTEGER = "integer" + LONG = "long" + DOUBLE = "double" + STRING = "string" + BOOLEAN = "boolean" + BINARY = "binary" + TIMESTAMP = "timestamp" + +# Define the Attribute class +class Attribute: + def __init__(self, name: str, attr_type: AttributeType): + self._name = name + self._type = attr_type + + @property + def name(self) -> str: + return self._name + + @property + def type(self) -> AttributeType: + return self._type + + def __str__(self) -> str: + return f"Attribute(name={self.name}, type={self.type.value})" + + def __repr__(self) -> str: + return f"Attribute(name={self.name!r}, type={self.type!r})" + + def __eq__(self, other) -> bool: + if isinstance(other, Attribute): + return self._name == other._name and self._type == other._type + return False + + def __hash__(self) -> int: + return hash((self._name, self._type)) + +# Define the DataSchema class +class DataSchema: + def __init__(self, attributes: List[Attribute]): + self._attributes: FrozenSet[Attribute] = frozenset(attributes) + + @property + def attributes(self) -> FrozenSet[Attribute]: + return self._attributes + + def __str__(self) -> str: + attributes_str = ', '.join(str(attr) for attr in self.attributes) + return f"DataSchema(attributes=[{attributes_str}])" + + def __repr__(self) -> str: + attributes_repr = ', '.join(repr(attr) for attr in self.attributes) + return f"DataSchema(attributes=[{attributes_repr}])" + + def __eq__(self, other) -> bool: + if isinstance(other, DataSchema): + return self._attributes == other._attributes + return False + + def __hash__(self) -> int: + return hash(self._attributes) \ No newline at end of file diff --git a/core/suggestion-service/model/EditingOperation.py b/core/suggestion-service/model/EditingOperation.py new file mode 100644 index 00000000000..604231c2e1a --- /dev/null +++ b/core/suggestion-service/model/EditingOperation.py @@ -0,0 +1,72 @@ +from typing import Tuple, List + +from abc import ABC, abstractmethod + +from model.Operator import Operator +from model.Port import Port +from model.Workflow import Workflow + + +class EditingOperationType: + AddOperator = "add_op" + RemoveOperator = "remove_op" + UpdateOperator = "edit_op" + AddLink = "add_link" + RemoveLink = "remove_link" + UpdateLink = "edit_link" + Misc = "misc" + Unchanged = "unchanged" + Void = "void" + +class EditingOperation(ABC): + @abstractmethod + def GetBaseWorkflow(self) -> Workflow: + pass + + @abstractmethod + def GetBase(self) -> (None + | List[Operator] + | List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]]): + """ + Return the base of this operation + add operator(s) -> None + remove operator(s) -> List[Operator] + modify operator(s) -> List[Operator] + + add link -> List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]] + remove link -> List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]] + modify link -> List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]] + """ + pass + + @abstractmethod + def GetModification(self) -> (None + | List[Operator] + | List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]]): + """ + Return "what's new" brought by this patch. + + add operator(s) -> List[Operator] + remove operator(s) -> None + modify operator(s) -> List[Operator] + + add link -> None + remove link -> None + modify link -> List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]] + """ + pass + + @abstractmethod + def GetType(self) -> EditingOperationType: + """ + Return the type of the operation + """ + pass + + @abstractmethod + def GetRawPatch(self) -> dict: + pass + + @abstractmethod + def IsValid(self) -> bool: + pass \ No newline at end of file diff --git a/core/suggestion-service/model/Operator.py b/core/suggestion-service/model/Operator.py new file mode 100644 index 00000000000..f77db8f9f87 --- /dev/null +++ b/core/suggestion-service/model/Operator.py @@ -0,0 +1,102 @@ +# the abstraction of operators + +# Operator Methods +# - GetName() -> str, return the name of the operator(e.g. Filter out all qualtified year) +# - GetType() -> str, return the type of the operator(e.g. FileScan, CSVFileScan, Filter, Projection) +# - GetId() -> str, return the id of the operator +# - GetProperties() -> dict, return a dict that contains the key(property name), the value(property values, could be single value, tuple, another dict, or list) +# - GetInputPort() -> list of Ports +# - GetOutputPort() -> list of Ports +# - GetInputSchema() -> dict, the key is the input port, the value is the DataSchema +# - GetOutputSchema() -> dict, the key is the output port, the value is the DataSchema +# - IsDynamicInputPorts() -> bool +# - IsDynamicOutputPorts() -> bool +# - IsDisabled() -> bool +# - IsViewResult() -> bool + +from abc import ABC, abstractmethod +from typing import Dict, List + +class Operator(ABC): + @abstractmethod + def GetName(self) -> str: + """ + Return the name of the operator (e.g. 'Filter out all qualified year'). + """ + pass + + @abstractmethod + def GetType(self) -> str: + """ + Return the type of the operator (e.g. 'FileScan', 'CSVFileScan', 'Filter', 'Projection'). + """ + pass + + @abstractmethod + def GetId(self) -> str: + """ + Return the ID of the operator. + """ + pass + + @abstractmethod + def GetProperties(self) -> Dict: + """ + Return a dictionary containing the properties of the operator. + The dictionary can contain property names as keys and property values (which could be a single value, tuple, another dict, or list) as values. + """ + pass + + @abstractmethod + def GetInputSchemaByPortID(self, portID: str) -> 'DataSchema': + pass + + @abstractmethod + def GetInputPorts(self) -> List['Port']: + """ + Return a list of input ports for the operator. + """ + pass + + @abstractmethod + def GetOutputPorts(self) -> List['Port']: + """ + Return a list of output ports for the operator. + """ + pass + + @abstractmethod + def GetError(self) -> str: + """ + Return the static error on this operator if any + :return: + """ + pass + + @abstractmethod + def IsDynamicInputPorts(self) -> bool: + """ + Return whether the operator has dynamic input ports. + """ + pass + + @abstractmethod + def IsDynamicOutputPorts(self) -> bool: + """ + Return whether the operator has dynamic output ports. + """ + pass + + @abstractmethod + def IsDisabled(self) -> bool: + """ + Return whether the operator is disabled. + """ + pass + + @abstractmethod + def IsViewResult(self) -> bool: + """ + Return whether the operator is for viewing results. + """ + pass \ No newline at end of file diff --git a/core/suggestion-service/model/Port.py b/core/suggestion-service/model/Port.py new file mode 100644 index 00000000000..17a576a96b7 --- /dev/null +++ b/core/suggestion-service/model/Port.py @@ -0,0 +1,89 @@ +# the abstraction of port + +# Port Methods +# IsInputPort() -> bool, return whether this port is input port +# IsOutputPort() -> bool, return whether this port is output port +# GetId() -> str, return the id of the port +# GetDisplayName() -> str, return the display name of the port +# AllowMultiInputs() -> bool, return whether the port allow multiple input edges +# IsDynamicPort() -> bool, return whether this port is a dynamic port +# Dependencies() -> str[], return a list of dependencies of the port id str if any + +from abc import ABC, abstractmethod +from typing import List + +class Port(ABC): + @abstractmethod + def IsInputPort(self) -> bool: + """ + Return whether this port is an input port. + """ + pass + + @abstractmethod + def IsOutputPort(self) -> bool: + """ + Return whether this port is an output port. + """ + pass + + @abstractmethod + def GetId(self) -> str: + """ + Return the ID of the port. + """ + pass + + @abstractmethod + def GetDisplayName(self) -> str: + """ + Return the display name of the port. + """ + pass + + @abstractmethod + def AllowMultiInputs(self) -> bool: + """ + Return whether the port allows multiple input edges. + """ + pass + + @abstractmethod + def IsDynamicPort(self) -> bool: + """ + Return whether this port is a dynamic port. + """ + pass + + @abstractmethod + def GetDataSchema(self) -> 'DataSchema': + """ + return the data schema annotated to this port + """ + pass + + @abstractmethod + def GetDependencies(self) -> List[str]: + """ + Return a list of dependencies of the port ID as strings, if any. + """ + pass + + @abstractmethod + def GetAffiliateOperator(self) -> 'Operator': + """ + Return the operator that this port is affiliated to + """ + pass + + def GetTargetPorts(self) -> List['Port']: + """ + Return the list of ports that this port(must be an output port) is sourcing + """ + pass + + def GetSourcePorts(self) -> List['Port']: + """ + Return the list of ports that this port(must be an input port) is being targeting + """ + pass \ No newline at end of file diff --git a/core/suggestion-service/model/Workflow.py b/core/suggestion-service/model/Workflow.py new file mode 100644 index 00000000000..3bdbcee9621 --- /dev/null +++ b/core/suggestion-service/model/Workflow.py @@ -0,0 +1,65 @@ +# the abstraction of workflows +# Methods +# 1. GetOperators(a list of operator type) -> a list of operators, if given list of types are none empty, filter and only keep those operators that of those types +# 2. GetLinks() -> a list of links(between these operators) +# 3. GetSubWorkflowByIndex(idx: int) -> (Workflow, one operator, a list of link related to that operator): this method will topologically sort the operators in the workflow, and +# it will return a sub workflow containing operator [0, idx-1], the idx-th operator, the links connected to this idx-th operator. + + +from abc import ABC, abstractmethod +from typing import List, Tuple, Dict + +from model.Operator import Operator +from model.Port import Port + + +class Workflow(ABC): + @abstractmethod + def GetWorkflowContent(self) -> str: + """ + return the raw content of the workflow + :return: + """ + pass + + @abstractmethod + def GetWorkflowId(self) -> int: + """ + return the id of the workflow + :return: + """ + pass + @abstractmethod + def GetOperators(self, types: List[str] = None) -> List['Operator']: + """ + Return a list of operators. If the given list of types is non-empty, + filter and only keep those operators of those types. + """ + pass + + @abstractmethod + def TopologicalSort(self) -> List['Operator']: + """ + Perform a topological sort on the operators in the workflow. + """ + pass + + @abstractmethod + def VisualizeDAG(self): + pass + + @abstractmethod + def GetDAG(self): + pass + + @abstractmethod + def GetSchemaToNextOperatorDistributionMapping(self) -> Dict['DataSchema', Dict[str, int]]: + pass + + @abstractmethod + def GetOperatorTypeToNextOperatorDistributionMapping(self) -> Dict[str, Dict[str, int]]: + pass + + @abstractmethod + def GetAdditionPairs(self) -> List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]]: + pass diff --git a/core/suggestion-service/model/__init__.py b/core/suggestion-service/model/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/core/suggestion-service/model/texera/TexeraOperator.py b/core/suggestion-service/model/texera/TexeraOperator.py new file mode 100644 index 00000000000..a20423fdb28 --- /dev/null +++ b/core/suggestion-service/model/texera/TexeraOperator.py @@ -0,0 +1,94 @@ +from typing import Dict, List, Tuple + +from model.DataSchema import DataSchema +from model.Operator import Operator +from model.texera.TexeraPort import TexeraPort + + +class TexeraOperator(Operator): + def __init__(self, operator_dict: dict, port_indexed_input_schemas: List['DataSchema'] = [], error: str = ""): + self.operator_id = operator_dict.get('operatorID', '') + self.operator_type = operator_dict.get('operatorType', '') + self.operator_version = operator_dict.get('operatorVersion', '') + self.operator_properties = operator_dict.get('operatorProperties', {}) + + # a mapping from port id to port + self.input_ports: Dict[str, 'TexeraPort'] = { + port_dict.get('portID'): + TexeraPort( + port_dict=port_dict, + is_input_port=True, + operator=self, + schema=port_indexed_input_schemas[i] if i < len(port_indexed_input_schemas) else DataSchema([]) + ) for i, port_dict in enumerate(operator_dict.get('inputPorts', [])) + } + + self.output_ports: Dict[str, 'TexeraPort'] = { + port_dict.get('portID'): + TexeraPort( + port_dict=port_dict, + is_input_port=False, + operator=self, + schema=DataSchema([])) for port_dict in operator_dict.get('outputPorts', []) + } + self.show_advanced = operator_dict.get('showAdvanced', False) + self.is_disabled = operator_dict.get('isDisabled', False) + self.custom_display_name = operator_dict.get('customDisplayName', '') + self.dynamic_input_ports = operator_dict.get('dynamicInputPorts', False) + self.dynamic_output_ports = operator_dict.get('dynamicOutputPorts', False) + self.view_result = operator_dict.get('viewResult', False) + self.input_schema = port_indexed_input_schemas + self.error = error + def GetName(self) -> str: + return self.custom_display_name + + def GetType(self) -> str: + return self.operator_type + + def GetId(self) -> str: + return self.operator_id + + def GetProperties(self) -> Dict: + return self.operator_properties + + def GetInputSchemaByPortID(self, portID: str) -> 'DataSchema': + port = self.input_ports.get(portID) + if port is not None: + return port.GetDataSchema() + return None + + def GetInputPorts(self) -> List['Port']: + return list(self.input_ports.values()) + + def GetOutputPorts(self) -> List['Port']: + return list(self.output_ports.values()) + + def GetError(self) -> str: + return self.error + + def IsDynamicInputPorts(self) -> bool: + return self.dynamic_input_ports + + def IsDynamicOutputPorts(self) -> bool: + return self.dynamic_output_ports + + def IsDisabled(self) -> bool: + return self.is_disabled + + def IsViewResult(self) -> bool: + return self.view_result + + def __str__(self) -> str: + input_ports_str = '\n '.join([str(port) for port in self.GetInputPorts()]) + output_ports_str = '\n '.join([str(port) for port in self.GetOutputPorts()]) + return ( + f"TexeraOperator(\n" + f" ID={self.operator_id},\n" + f" Type={self.operator_type},\n" + f" Version={self.operator_version},\n" + f" Properties={self.operator_properties},\n" + f" InputPorts=[\n {input_ports_str}\n ],\n" + f" OutputPorts=[\n {output_ports_str}\n ],\n" + f" Error={self.error}\n" + f")" + ) \ No newline at end of file diff --git a/core/suggestion-service/model/texera/TexeraOperatorTypes.py b/core/suggestion-service/model/texera/TexeraOperatorTypes.py new file mode 100644 index 00000000000..75fb2e1482f --- /dev/null +++ b/core/suggestion-service/model/texera/TexeraOperatorTypes.py @@ -0,0 +1,77 @@ +class TexeraOperatorTypes: + class DataInput: + CSVFileScan = "CSVFileScan" + FileScan = "FileScan" + JSONLFileScan = "JSONLFileScan" + TextInput = "TextInput" + ParallelCSVFileScan = "ParallelCSVFileScan" + + class MachineLearning: + class SkLearn: + SklearnLogisticRegression = "SklearnLogisticRegression" + SklearnLogisticRegressionCV = "SklearnLogisticRegressionCV" + SklearnRidge = "SklearnRidge" + SklearnRidgeCV = "SklearnRidgeCV" + SklearnSDG = "SklearnSDG" + SklearnPassiveAggressive = "SklearnPassiveAggressive" + SklearnPerceptron = "SklearnPerceptron" + SklearnKNN = "SklearnKNN" + SklearnNearestCentroid = "SklearnNearestCentroid" + SklearnSVM = "SklearnSVM" + SklearnLinearSVM = "SklearnLinearSVM" + SklearnLinearRegression = "SklearnLinearRegression" + SklearnDecisionTree = "SklearnDecisionTree" + SklearnExtraTree = "SklearnExtraTree" + SklearnMultiLayerPerceptron = "SklearnMultiLayerPerceptron" + SklearnProbabilityCalibration = "SklearnProbabilityCalibration" + SklearnRandomForest = "SklearnRandomForest" + SklearnBagging = "SklearnBagging" + SklearnGradientBoosting = "SklearnGradientBoosting" + SklearnAdaptiveBoosting = "SklearnAdaptiveBoosting" + SklearnExtraTrees = "SklearnExtraTrees" + SklearnGaussianNaiveBayes = "SklearnGaussianNaiveBayes" + SklearnMultinomialNaiveBayes = "SklearnMultinomialNaiveBayes" + SklearnComplementNaiveBayes = "SklearnComplementNaiveBayes" + SklearnBernoulliNaiveBayes = "SklearnBernoulliNaiveBayes" + SklearnDummyClassifier = "SklearnDummyClassifier" + SklearnPrediction = "SklearnPrediction" + + class AdvancedSkLearn: + KNNClassifierTrainer = "KNNClassifierTrainer" + KNNRegressorTrainer = "KNNRegressorTrainer" + SVCTrainer = "SVCTrainer" + SVRTrainer = "SVRTrainer" + + class HuggingFace: + HuggingFaceSentimentAnalysis = "HuggingFaceSentimentAnalysis" + HuggingFaceTextSummarization = "HuggingFaceTextSummarization" + HuggingFaceSpamSMSDetection = "HuggingFaceSpamSMSDetection" + HuggingFaceIrisLogisticRegression = "HuggingFaceIrisLogisticRegression" + + class General: + Scorer = "Scorer" + Split = "Split" + SentimentAnalysis = "SentimentAnalysis" + + class UDF: + PythonUDFV2 = "PythonUDFV2" + PythonUDFSourceV2 = "PythonUDFSourceV2" + DualInputPortsPythonUDFV2 = "DualInputPortsPythonUDFV2" + PythonLambdaFunction = "PythonLambdaFunction" + PythonTableReducer = "PythonTableReducer" + + JavaUDF = "JavaUDF" + RUDF = "RUDF" + RUDFSource = "RUDFSource" + +def get_flat_list(cls): + result = [] + for attr_name in dir(cls): + attr_value = getattr(cls, attr_name) + if not attr_name.startswith('__') and not callable(attr_value): + if isinstance(attr_value, type): + result.extend(get_flat_list(attr_value)) + else: + result.append(attr_value) + return result + diff --git a/core/suggestion-service/model/texera/TexeraPort.py b/core/suggestion-service/model/texera/TexeraPort.py new file mode 100644 index 00000000000..50f7e20d0ca --- /dev/null +++ b/core/suggestion-service/model/texera/TexeraPort.py @@ -0,0 +1,62 @@ +from typing import List + +from model.DataSchema import DataSchema +from model.Operator import Operator +from model.Port import Port + + +class TexeraPort(Port): + def __init__(self, port_dict: dict, is_input_port: bool, operator: Operator, schema: DataSchema = DataSchema([])): + self.port_id = port_dict.get('portID', '') + self.display_name = port_dict.get('displayName', '') + self.allow_multi_inputs = port_dict.get('allowMultiInputs', False) + self.is_dynamic_port = port_dict.get('isDynamicPort', False) + self.dependencies = port_dict.get('dependencies', {}) + self.is_input_port = is_input_port + self.affiliate_operator = operator + self.data_schema = schema + + def IsInputPort(self) -> bool: + return self.is_input_port + + def IsOutputPort(self) -> bool: + return not self.is_input_port + + def GetId(self) -> str: + return self.port_id + + def GetDisplayName(self) -> str: + return self.display_name + + def AllowMultiInputs(self) -> bool: + return self.allow_multi_inputs + + def IsDynamicPort(self) -> bool: + return self.is_dynamic_port + + def GetDependencies(self) -> List[str]: + return self.dependencies + + def GetSourcePorts(self) -> List['Port']: + if self.IsOutputPort(): + raise RuntimeError("output port doesn't have the source ports!") + return self.source_ports + + def GetTargetPorts(self) -> List['Port']: + if self.IsInputPort(): + raise RuntimeError("Input port doesn't have the target ports") + return self.target_ports + + def GetDataSchema(self) -> 'DataSchema': + return self.data_schema + + def GetAffiliateOperator(self) -> 'Operator': + return self.affiliate_operator + + def __str__(self) -> str: + return ( + f"TexeraPort(\n" + f" ID={self.port_id}, \n" + f" DataSchema={self.data_schema}, \n" + f")" + ) diff --git a/core/suggestion-service/model/texera/TexeraWorkflow.py b/core/suggestion-service/model/texera/TexeraWorkflow.py new file mode 100644 index 00000000000..bcf02897c07 --- /dev/null +++ b/core/suggestion-service/model/texera/TexeraWorkflow.py @@ -0,0 +1,165 @@ +import json + +from typing import List, Dict, Tuple +import networkx as nx + +from model.Operator import Operator +from model.Port import Port +from model.DataSchema import DataSchema + +from model.Workflow import Workflow +from model.texera.TexeraOperator import TexeraOperator + + +class TexeraWorkflow(Workflow): + def __init__( + self, + workflow_content: str, + operator_id_to_port_indexed_input_schemas_mapping: Dict[str, List['DataSchema']]=None, + operator_id_to_error_mapping: Dict[str, str]=None, + wid: int = 0, + workflow_title: str = "" + ): + + if operator_id_to_port_indexed_input_schemas_mapping is None: + operator_id_to_port_indexed_input_schemas_mapping = {} + if operator_id_to_error_mapping is None: + operator_id_to_error_mapping = {} + + self.workflow_content = workflow_content + self.workflow_dict = json.loads(workflow_content) + operators_dict = self.workflow_dict.get("operators", {}) + links_dict = self.workflow_dict.get("links", {}) + + self.DAG = nx.DiGraph() + self.workflow_title = workflow_title + self.operators: Dict[str, 'TexeraOperator'] = { + op_dict['operatorID']: + TexeraOperator( + operator_dict = op_dict, + port_indexed_input_schemas=operator_id_to_port_indexed_input_schemas_mapping.get(op_dict['operatorID'], []), + error=operator_id_to_error_mapping.get(op_dict['operatorID'], "") + ) for op_dict in operators_dict + } + # self.links = [TexeraLink(link_dict, self.operators) for link_dict in links_dict] + self.wid = wid + + # start to build the DAG + for operator in self.GetOperators(): + self.DAG.add_node(operator.GetId(), + type=operator.GetType(), + inputPorts=[port.GetId() for port in operator.GetInputPorts()], + outputPorts=[port.GetId() for port in operator.GetOutputPorts()], + error=operator.GetError() + ) + + # then start to add link + for link in links_dict: + source_op_id = link.get('source').get('operatorID') + src_port_id = link.get('source').get('portID') + target_op_id = link.get('target').get('operatorID') + target_port_id = link.get('target').get('portID') + + op = self.operators.get(target_op_id) + if op is not None: + schema = op.GetInputSchemaByPortID(target_port_id) + else: + schema = None + self.DAG.add_edge(source_op_id, + target_op_id, + srcPort=src_port_id, + targetPort=target_port_id, + schema=schema + ) + + + def GetWorkflowContent(self) -> str: + return self.workflow_content + + def GetWorkflowId(self) -> int: + return self.wid + + def GetOperators(self, types: List[str] = None) -> List['Operator']: + if types is None: + return list(self.operators.values()) + return [op for op in self.operators.values() if op.GetType() in types] + + def TopologicalSort(self) -> List['Operator']: + # Assuming a valid DAG and operators are sortable + # A complete topological sort algorithm should be implemented here + sorted_operators = list(self.operators.values()) # Placeholder for actual sorting logic + return sorted_operators + + def GetDAG(self): + return self.DAG + + def GetSchemaToNextOperatorDistributionMapping(self) -> Dict['DataSchema', Dict[str, int]]: + result: Dict['DataSchema', Dict[str, int]] = {} + + # Iterate over all edges in the DAG + for source_op_id, target_op_id, edge_data in self.DAG.edges(data=True): + schema = edge_data['schema'] + target_op = self.operators.get(target_op_id) + target_op_type = target_op.GetType() + + if schema not in result: + result[schema] = {} + + if target_op_type not in result[schema]: + result[schema][target_op_type] = 0 + + result[schema][target_op_type] += 1 + + return result + + def GetOperatorTypeToNextOperatorDistributionMapping(self) -> Dict[str, Dict[str, int]]: + result: Dict[str, Dict[str, int]] = {} + + # Iterate over all edges in the DAG + for source_op_id, target_op_id, edge_data in self.DAG.edges(data=True): + source_op = self.operators.get(source_op_id) + source_op_type = source_op.GetType() + target_op = self.operators.get(target_op_id) + target_op_type = target_op.GetType() + + if source_op_type not in result: + result[source_op_type] = {} + + if target_op_type not in result[source_op_type]: + result[source_op_type][target_op_type] = 0 + + result[source_op_type][target_op_type] += 1 + + return result + + def GetAdditionPairs(self) -> List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]]: + results: List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]] = [] + # Iterate over all edges in the DAG + for source_op_id, target_op_id, edge_data in self.DAG.edges(data=True): + source_op = self.operators.get(source_op_id) + target_op = self.operators.get(target_op_id) + srcPortId = edge_data.get('srcPort') + targetPortId = edge_data.get('targetPort') + if source_op is None or target_op is None: + continue + srcPort = next((port for port in source_op.GetOutputPorts() if port.GetId() == srcPortId), None) + targetPort = next((port for port in target_op.GetInputPorts() if port.GetId() == targetPortId), None) + + if srcPort is None or targetPort is None: + continue + results.append(((source_op, srcPort), (target_op, targetPort))) + return results + + def __str__(self) -> str: + # Create a string representation of the workflow + operators_str = '\n'.join([str(operator) for operator in self.GetOperators()]) + edges_str = '\n'.join([f"{source} -> {target}" for source, target in self.DAG.edges()]) + + return ( + f"TexeraWorkflow(\n" + f" WorkflowID={self.wid},\n" + f" Title={self.workflow_title},\n" + f" Operators=[\n{operators_str}\n ],\n" + f" DAG Edges=[\n {edges_str}\n ]\n" + f")" + ) \ No newline at end of file diff --git a/core/suggestion-service/model/texera/__init__.py b/core/suggestion-service/model/texera/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/core/suggestion-service/requirements.txt b/core/suggestion-service/requirements.txt new file mode 100644 index 00000000000..320ba234286 --- /dev/null +++ b/core/suggestion-service/requirements.txt @@ -0,0 +1,11 @@ +# Core dependencies +networkx==3.1.0 # Used for DAG operations +pyparsing==3.1.1 # Required by pyiceberg + +# API dependencies (for future REST API implementation) +fastapi==0.101.1 +uvicorn==0.23.2 +pydantic==2.1.1 + +# Utility libraries +typing-extensions==4.8.0 # Enhanced typing support \ No newline at end of file From 695ec24dc7663f5aa8a0256408f5ae2d68999b1d Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 14 Apr 2025 14:59:17 -0700 Subject: [PATCH 012/104] add tuple and frontend service calling --- .../workflow-suggestion.service.ts | 181 ++++++++++++++++++ core/suggestion-service/app.py | 119 ++++++++++++ core/suggestion-service/model/Tuple.py | 171 +++++++++++++++++ .../suggestion_engine/__init__.py | 1 + .../suggestion_engine/generator.py | 115 +++++++++++ core/suggestion-service/test_request.py | 123 ++++++++++++ 6 files changed, 710 insertions(+) create mode 100644 core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts create mode 100644 core/suggestion-service/app.py create mode 100644 core/suggestion-service/model/Tuple.py create mode 100644 core/suggestion-service/suggestion_engine/__init__.py create mode 100644 core/suggestion-service/suggestion_engine/generator.py create mode 100644 core/suggestion-service/test_request.py diff --git a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts new file mode 100644 index 00000000000..9731d010207 --- /dev/null +++ b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts @@ -0,0 +1,181 @@ +import { HttpClient, HttpHeaders } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { Observable, ReplaySubject, of } from "rxjs"; +import { catchError, map } from "rxjs/operators"; +import { AppSettings } from "../../../common/app-setting"; +import { WorkflowActionService } from "../workflow-graph/model/workflow-action.service"; +import { WorkflowCompilingService } from "../compile-workflow/workflow-compiling.service"; +import { Workflow } from "../../../common/type/workflow"; +import { WorkflowResultService } from "../workflow-result/workflow-result.service"; + +// Define the WorkflowSuggestion interface - this should match the interface in the component +export interface WorkflowSuggestion { + id: string; + description: string; + operatorsToAdd: { + operatorType: string; + position: { x: number, y: number }; + properties?: object; + }[]; + operatorPropertiesToChange: { + operatorId: string; + properties: object; + }[]; + operatorsToDelete: string[]; // IDs of operators to delete + linksToAdd: { + source: { operatorId: string, portId: string }; + target: { operatorId: string, portId: string }; + }[]; + isPreviewActive: boolean; +} + +// Define an interface for result panel data +interface ResultPanelData { + operatorID: string; + currentResult?: object[]; + columnKeys?: string[]; +} + +// endpoint for workflow suggestions +export const WORKFLOW_SUGGESTION_ENDPOINT = "api/suggest"; + +/** + * WorkflowSuggestionService is responsible for communicating with the backend suggestion service. + * It gathers the necessary data (workflow, compilation state, and result data) and sends it to + * the backend to generate workflow suggestions. + */ +@Injectable({ + providedIn: "root", +}) +export class WorkflowSuggestionService { + private suggestionStream = new ReplaySubject(1); + + constructor( + private httpClient: HttpClient, + private workflowActionService: WorkflowActionService, + private workflowCompilingService: WorkflowCompilingService, + private workflowResultService: WorkflowResultService + ) {} + + /** + * Requests workflow suggestions from the backend service. + * This method gathers the current workflow state, compilation information, + * and result data, then sends it to the backend to generate suggestions. + * + * @returns Observable of workflow suggestions + */ + public getSuggestions(): Observable { + // Get the current workflow + const workflow: Workflow = this.workflowActionService.getWorkflow(); + + // Get compilation state info + const compilationState = { + state: this.workflowCompilingService.getWorkflowCompilationState(), + physicalPlan: undefined, + operatorInputSchemaMap: {}, + operatorErrors: this.workflowCompilingService.getWorkflowCompilationErrors() + }; + + // Get result tables for all operators that have result data + const resultTables: Record = {}; + + // Get the results from all operators + const operators = this.workflowActionService.getTexeraGraph().getAllOperators(); + + operators.forEach(operator => { + const operatorId = operator.operatorID; + + // Check if this operator has paginated results + const paginatedResultService = this.workflowResultService.getPaginatedResultService(operatorId); + if (paginatedResultService) { + // Get schema attributes for column names + const schema = paginatedResultService.getSchema(); + const columnNames = schema.map(attr => attr.attributeName); + + // Select the first page of data (typically 10 rows) + // We're using a synchronous approach here to simplify things + let rows: object[] = []; + + // If there are results, try to get them + if (paginatedResultService.getCurrentTotalNumTuples() > 0) { + paginatedResultService.selectPage(1, 10).subscribe( + pageData => { + rows = pageData.table as object[]; + } + ); + } + + resultTables[operatorId] = { + rows: rows, + columnNames: columnNames + }; + } + + // Check if this operator has non-paginated results + const resultService = this.workflowResultService.getResultService(operatorId); + if (resultService) { + // Get the current result snapshot + const resultSnapshot = resultService.getCurrentResultSnapshot(); + if (resultSnapshot) { + // Since this is non-paginated data (likely visualization data), + // we might not have schema information, so we'll extract column names from the first object + const firstRow = resultSnapshot[0] || {}; + const columnNames = Object.keys(firstRow); + + resultTables[operatorId] = { + rows: Array.from(resultSnapshot), + columnNames: columnNames + }; + } + } + }); + + // Prepare the request body + const requestBody = { + workflow: JSON.stringify(workflow), + compilationState: compilationState, + resultTables: resultTables + }; + + // Send the request to the backend + return this.httpClient + .post( + `${AppSettings.getApiEndpoint()}/${WORKFLOW_SUGGESTION_ENDPOINT}`, + JSON.stringify(requestBody), + { + headers: new HttpHeaders({ + "Content-Type": "application/json", + }), + } + ) + .pipe( + map(suggestions => { + // Store the suggestions in the stream for other components to subscribe + this.suggestionStream.next(suggestions); + return suggestions; + }), + catchError(error => { + console.error("Error getting workflow suggestions:", error); + return of([]); + }) + ); + } + + /** + * Get an observable stream of workflow suggestions. + * Components can subscribe to this stream to get the latest suggestions. + * + * @returns Observable of workflow suggestions + */ + public getSuggestionStream(): Observable { + return this.suggestionStream.asObservable(); + } + + /** + * Refresh suggestions by requesting new ones from the backend. + * This will update the suggestion stream. + */ + public refreshSuggestions(): void { + this.getSuggestions().subscribe(); + } +} \ No newline at end of file diff --git a/core/suggestion-service/app.py b/core/suggestion-service/app.py new file mode 100644 index 00000000000..5de3f7f0922 --- /dev/null +++ b/core/suggestion-service/app.py @@ -0,0 +1,119 @@ +import json +from typing import Dict, List, Any, Optional + +from fastapi import FastAPI, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel, Field + +from suggestion_engine.generator import SuggestionGenerator + +app = FastAPI(title="Texera Workflow Suggestion Service") + +# Initialize the suggestion generator +suggestion_generator = SuggestionGenerator() + +# Enable CORS +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # In production, restrict this to your frontend domain + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Input models +class SchemaAttribute(BaseModel): + attributeName: str + attributeType: str + +class Position(BaseModel): + x: float + y: float + +class OperatorToAdd(BaseModel): + operatorType: str + position: Position + properties: Optional[Dict[str, Any]] = None + +class OperatorPropertyChange(BaseModel): + operatorId: str + properties: Dict[str, Any] + +class Link(BaseModel): + source: Dict[str, str] + target: Dict[str, str] + +class WorkflowSuggestion(BaseModel): + id: str + description: str + operatorsToAdd: List[OperatorToAdd] + operatorPropertiesToChange: List[OperatorPropertyChange] + operatorsToDelete: List[str] + linksToAdd: List[Link] + isPreviewActive: bool = False + +class PhysicalPlan(BaseModel): + # Simplified physical plan model + operators: Dict[str, Any] = {} + links: List[Dict[str, Any]] = [] + +class CompilationStateInfo(BaseModel): + state: str + physicalPlan: Optional[PhysicalPlan] = None + operatorInputSchemaMap: Optional[Dict[str, List[Optional[List[SchemaAttribute]]]]] = None + operatorErrors: Optional[Dict[str, Any]] = None + +class ResultTable(BaseModel): + rows: List[Dict[str, Any]] + columnNames: List[str] + +class SuggestionRequest(BaseModel): + workflow: str = Field(..., description="JSON string of the workflow") + compilationState: CompilationStateInfo + resultTables: Dict[str, ResultTable] + +@app.get("/") +async def root(): + return {"message": "Texera Workflow Suggestion Service is running"} + +@app.post("/api/suggest", response_model=List[WorkflowSuggestion]) +async def generate_suggestions(request: SuggestionRequest): + """ + Generate workflow suggestions based on the current workflow, compilation state, and result tables. + + Args: + request: Contains workflow as JSON string, compilation state info, and result tables by operator ID + + Returns: + A list of workflow suggestions + """ + try: + # Parse the workflow JSON + workflow_json = json.loads(request.workflow) + + # Convert Pydantic models to dictionaries for the suggestion generator + compilation_state_dict = request.compilationState.dict() + result_tables_dict = {op_id: table.dict() for op_id, table in request.resultTables.items()} + + # Generate suggestions using the suggestion engine + suggestions = suggestion_generator.generate_suggestions( + workflow_json, + compilation_state_dict, + result_tables_dict + ) + + # Convert the dictionaries to WorkflowSuggestion objects + workflow_suggestions = [] + for suggestion in suggestions: + workflow_suggestions.append(WorkflowSuggestion(**suggestion)) + + return workflow_suggestions + + except json.JSONDecodeError: + raise HTTPException(status_code=400, detail="Invalid workflow JSON format") + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error generating suggestions: {str(e)}") + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/core/suggestion-service/model/Tuple.py b/core/suggestion-service/model/Tuple.py new file mode 100644 index 00000000000..2bf562a4d49 --- /dev/null +++ b/core/suggestion-service/model/Tuple.py @@ -0,0 +1,171 @@ +from typing import Dict, List, Any, Optional, OrderedDict, Iterator, Union + +from model.DataSchema import DataSchema, Attribute + + +class Tuple: + """ + A simple implementation of a tuple for data storage. + """ + + def __init__( + self, + data: Optional[Dict[str, Any]] = None, + schema: Optional[DataSchema] = None + ): + """ + Initialize a Tuple with given data and optional schema. + + :param data: Dictionary mapping field names to values + :param schema: Optional schema that defines the structure of this tuple + """ + self._data: Dict[str, Any] = data or {} + self._schema = schema + + def __getitem__(self, key: Union[str, int]) -> Any: + """ + Get a field value by name or index. + + :param key: Field name (str) or index (int) + :return: Value of the field + """ + if isinstance(key, int): + key = list(self._data.keys())[key] + return self._data.get(key) + + def __setitem__(self, key: str, value: Any) -> None: + """ + Set a field value. + + :param key: Field name + :param value: Field value + """ + self._data[key] = value + + def __contains__(self, key: str) -> bool: + """ + Check if the tuple contains a field. + + :param key: Field name + :return: True if the field exists in the tuple + """ + return key in self._data + + def __len__(self) -> int: + """ + Get the number of fields in the tuple. + + :return: Number of fields + """ + return len(self._data) + + def __iter__(self) -> Iterator[Any]: + """ + Iterate over field values. + + :return: Iterator over field values + """ + return iter(self._data.values()) + + def __str__(self) -> str: + """ + String representation of the tuple. + + :return: String representation + """ + return f"Tuple({self._data})" + + def __repr__(self) -> str: + return self.__str__() + + def __eq__(self, other: object) -> bool: + """ + Check equality with another tuple. + + :param other: Another tuple + :return: True if tuples are equal + """ + if not isinstance(other, Tuple): + return False + return self._data == other._data + + def get_field_names(self) -> List[str]: + """ + Get list of field names in the tuple. + + :return: List of field names + """ + return list(self._data.keys()) + + def get_fields(self) -> Dict[str, Any]: + """ + Get dictionary representation of the tuple. + + :return: Dictionary with field names as keys and values as values + """ + return self._data.copy() + + def get_field(self, field_name: str) -> Any: + """ + Get value of a specific field. + + :param field_name: Name of the field + :return: Value of the field + """ + return self._data.get(field_name) + + def set_field(self, field_name: str, value: Any) -> None: + """ + Set value of a specific field. + + :param field_name: Name of the field + :param value: Value to set + """ + self._data[field_name] = value + + def as_dict(self) -> Dict[str, Any]: + """ + Convert tuple to dictionary. + + :return: Dictionary representation of the tuple + """ + return self._data.copy() + + def set_schema(self, schema: DataSchema) -> None: + """ + Set schema for the tuple. + + :param schema: Schema to set + """ + self._schema = schema + + def get_schema(self) -> Optional[DataSchema]: + """ + Get schema of the tuple. + + :return: Schema of the tuple + """ + return self._schema + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> 'Tuple': + """ + Create a tuple from a dictionary. + + :param data: Dictionary with field names and values + :return: New Tuple instance + """ + return cls(data) + + @classmethod + def from_list(cls, field_names: List[str], values: List[Any]) -> 'Tuple': + """ + Create a tuple from lists of field names and values. + + :param field_names: List of field names + :param values: List of values + :return: New Tuple instance + """ + if len(field_names) != len(values): + raise ValueError("Number of field names must match number of values") + return cls(dict(zip(field_names, values))) \ No newline at end of file diff --git a/core/suggestion-service/suggestion_engine/__init__.py b/core/suggestion-service/suggestion_engine/__init__.py new file mode 100644 index 00000000000..b5ef90d0344 --- /dev/null +++ b/core/suggestion-service/suggestion_engine/__init__.py @@ -0,0 +1 @@ +# Suggestion engine package \ No newline at end of file diff --git a/core/suggestion-service/suggestion_engine/generator.py b/core/suggestion-service/suggestion_engine/generator.py new file mode 100644 index 00000000000..06f33d42bde --- /dev/null +++ b/core/suggestion-service/suggestion_engine/generator.py @@ -0,0 +1,115 @@ +from typing import Dict, List, Any, Optional +import json +import uuid + +from model.Tuple import Tuple +from model.DataSchema import DataSchema, Attribute, AttributeType + + +class SuggestionGenerator: + """ + Class responsible for generating workflow suggestions + """ + + def __init__(self): + """Initialize the suggestion generator""" + pass + + def generate_suggestions( + self, + workflow_json: Dict[str, Any], + compilation_state: Dict[str, Any], + result_tables: Dict[str, Dict[str, Any]] + ) -> List[Dict[str, Any]]: + """ + Generate workflow suggestions based on the current workflow, + compilation state, and result tables. + + Args: + workflow_json: The workflow as a parsed JSON object + compilation_state: Compilation state information + result_tables: Result tables from operators + + Returns: + A list of workflow suggestions + """ + # Convert result tables to Tuple objects + tuples_by_operator = {} + for op_id, table in result_tables.items(): + tuples = [] + for row in table["rows"]: + tuples.append(Tuple(row)) + tuples_by_operator[op_id] = tuples + + # Here you would implement actual suggestion logic + # For now, return mock suggestions + + suggestions = [] + + # Add suggestions based on the workflow state + if compilation_state["state"] == "Succeeded": + # Add mock suggestion 1: Add a KeywordSearch operator + suggestion1 = { + "id": f"suggestion-{uuid.uuid4()}", + "description": "Add a KeywordSearch operator with sentiment analysis", + "operatorsToAdd": [ + { + "operatorType": "KeywordSearch", + "position": {"x": 400, "y": 300}, + "properties": {"keyword": "climate change", "attributes": ["content", "title"]} + }, + { + "operatorType": "SentimentAnalysis", + "position": {"x": 600, "y": 300}, + "properties": {"attribute": "content", "resultAttribute": "sentiment"} + } + ], + "operatorPropertiesToChange": [ + { + "operatorId": "View-Results-1", + "properties": {"limit": 20, "offset": 0} + } + ], + "operatorsToDelete": [], + "linksToAdd": [ + { + "source": {"operatorId": "Source-Scan-1", "portId": "output-0"}, + "target": {"operatorId": "KeywordSearch-1", "portId": "input-0"} + }, + { + "source": {"operatorId": "KeywordSearch-1", "portId": "output-0"}, + "target": {"operatorId": "SentimentAnalysis-1", "portId": "input-0"} + }, + { + "source": {"operatorId": "SentimentAnalysis-1", "portId": "output-0"}, + "target": {"operatorId": "View-Results-1", "portId": "input-0"} + } + ], + "isPreviewActive": False + } + suggestions.append(suggestion1) + + # Add mock suggestion 2: Replace ScanSource with CSVFileScan + suggestion2 = { + "id": f"suggestion-{uuid.uuid4()}", + "description": "Replace ScanSource with CSVFileScan for better performance", + "operatorsToAdd": [ + { + "operatorType": "CSVFileScan", + "position": {"x": 200, "y": 200}, + "properties": {"fileName": "data.csv", "limit": -1, "offset": 0, "schema": "auto"} + } + ], + "operatorPropertiesToChange": [], + "operatorsToDelete": ["Source-Scan-1"], + "linksToAdd": [ + { + "source": {"operatorId": "CSVFileScan-1", "portId": "output-0"}, + "target": {"operatorId": "View-Results-1", "portId": "input-0"} + } + ], + "isPreviewActive": False + } + suggestions.append(suggestion2) + + return suggestions \ No newline at end of file diff --git a/core/suggestion-service/test_request.py b/core/suggestion-service/test_request.py new file mode 100644 index 00000000000..27e1012786d --- /dev/null +++ b/core/suggestion-service/test_request.py @@ -0,0 +1,123 @@ +import json +import requests + +# Define the API endpoint URL +API_URL = "http://localhost:8000/api/suggest" + +# Sample workflow JSON +workflow_json = { + "operators": { + "Source-Scan-1": { + "operatorID": "Source-Scan-1", + "operatorType": "SourceScan", + "operatorProperties": { + "tableName": "users", + "limit": 100 + }, + "inputPorts": [], + "outputPorts": [ + {"portID": "output-0", "displayName": "output"} + ] + }, + "View-Results-1": { + "operatorID": "View-Results-1", + "operatorType": "ViewResults", + "operatorProperties": {}, + "inputPorts": [ + {"portID": "input-0", "displayName": "input"} + ], + "outputPorts": [] + } + }, + "links": [ + { + "linkID": "link-1", + "source": {"operatorID": "Source-Scan-1", "portID": "output-0"}, + "target": {"operatorID": "View-Results-1", "portID": "input-0"} + } + ], + "operatorPositions": { + "Source-Scan-1": {"x": 100, "y": 100}, + "View-Results-1": {"x": 400, "y": 100} + } +} + +# Sample compilation state +compilation_state = { + "state": "Succeeded", + "physicalPlan": { + "operators": { + "Source-Scan-1": { + "operatorID": "Source-Scan-1", + "operatorType": "SourceScan" + }, + "View-Results-1": { + "operatorID": "View-Results-1", + "operatorType": "ViewResults" + } + }, + "links": [ + { + "fromOpID": "Source-Scan-1", + "fromPortID": "output-0", + "toOpID": "View-Results-1", + "toPortID": "input-0" + } + ] + }, + "operatorInputSchemaMap": { + "View-Results-1": [ + [ + {"attributeName": "id", "attributeType": "long"}, + {"attributeName": "name", "attributeType": "string"}, + {"attributeName": "email", "attributeType": "string"}, + {"attributeName": "age", "attributeType": "integer"} + ] + ] + } +} + +# Sample result tables +result_tables = { + "View-Results-1": { + "rows": [ + {"id": 1, "name": "John Doe", "email": "john@example.com", "age": 30}, + {"id": 2, "name": "Jane Smith", "email": "jane@example.com", "age": 25}, + {"id": 3, "name": "Bob Johnson", "email": "bob@example.com", "age": 40} + ], + "columnNames": ["id", "name", "email", "age"] + } +} + +# Prepare the request payload +request_data = { + "workflow": json.dumps(workflow_json), + "compilationState": compilation_state, + "resultTables": result_tables +} + +# Send the request +try: + response = requests.post(API_URL, json=request_data) + + # Print the response + if response.status_code == 200: + print("Suggestions received:") + suggestions = response.json() + for i, suggestion in enumerate(suggestions, 1): + print(f"\nSuggestion {i}: {suggestion['description']}") + print(f" Operators to add: {len(suggestion['operatorsToAdd'])}") + print(f" Properties to change: {len(suggestion['operatorPropertiesToChange'])}") + print(f" Operators to delete: {len(suggestion['operatorsToDelete'])}") + print(f" Links to add: {len(suggestion['linksToAdd'])}") + else: + print(f"Error: {response.status_code}") + print(response.text) +except Exception as e: + print(f"Error sending request: {e}") + +""" +To run this test: +1. Start the suggestion service: python app.py +2. In another terminal, run this script: python test_request.py +""" \ No newline at end of file From 70805ff9ac8044bbd003bcbfd0c7e2a226fe896e Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 16 Apr 2025 11:07:01 -0700 Subject: [PATCH 013/104] 2nd version --- core/gui/proxy.config.json | 5 + core/gui/src/app/app.module.ts | 7 +- .../workflow-suggestion.component.html | 60 -- .../workflow-suggestion.component.ts | 618 ------------------ .../result-panel/result-panel.component.ts | 47 +- .../suggestion-frame.component.html | 86 +++ .../suggestion-frame.component.scss} | 43 +- .../suggestion-frame.component.ts | 581 ++++++++++++++++ .../workflow-suggestion.service.ts | 274 ++++++-- core/suggestion-service/app.py | 23 +- .../suggestion_engine/generator.py | 115 +++- 11 files changed, 1112 insertions(+), 747 deletions(-) delete mode 100644 core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.html delete mode 100644 core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.ts create mode 100644 core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.html rename core/gui/src/app/workspace/component/{left-panel/workflow-suggestion/workflow-suggestion.component.scss => result-panel/suggestion-frame/suggestion-frame.component.scss} (66%) create mode 100644 core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts diff --git a/core/gui/proxy.config.json b/core/gui/proxy.config.json index 2702a9892c4..83cef938648 100755 --- a/core/gui/proxy.config.json +++ b/core/gui/proxy.config.json @@ -14,6 +14,11 @@ "secure": false, "changeOrigin": true }, + "/api/workflow-suggestion": { + "target": "http://0.0.0.0:9094", + "secure": false, + "changeOrigin": true + }, "/api/config/**": { "target": "http://localhost:9094", "secure": false, diff --git a/core/gui/src/app/app.module.ts b/core/gui/src/app/app.module.ts index 0dac287d6b5..072da76d0c9 100644 --- a/core/gui/src/app/app.module.ts +++ b/core/gui/src/app/app.module.ts @@ -67,7 +67,6 @@ import { MiniMapComponent } from "./workspace/component/workflow-editor/mini-map import { MenuComponent } from "./workspace/component/menu/menu.component"; import { OperatorLabelComponent } from "./workspace/component/left-panel/operator-menu/operator-label/operator-label.component"; import { OperatorMenuComponent } from "./workspace/component/left-panel/operator-menu/operator-menu.component"; -import { WorkflowSuggestionComponent } from "./workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component"; import { SettingsComponent } from "./workspace/component/left-panel/settings/settings.component"; import { PropertyEditorComponent } from "./workspace/component/property-editor/property-editor.component"; import { TypeCastingDisplayComponent } from "./workspace/component/property-editor/typecasting-display/type-casting-display.component"; @@ -171,6 +170,10 @@ import { ComputingUnitSelectionComponent } from "./workspace/component/power-but import { NzSliderModule } from "ng-zorro-antd/slider"; import { AdminSettingsComponent } from "./dashboard/component/admin/settings/admin-settings.component"; import { catchError, of } from "rxjs"; +import { SuggestionFrameComponent } from "./workspace/component/result-panel/suggestion-frame/suggestion-frame.component"; +// Import providers for circular dependency resolution +import { WORKFLOW_SUGGESTION_PROVIDER } from "./workspace/service/workflow-suggestion/workflow-suggestion.provider"; +import { WORKFLOW_COMPILING_PROVIDER } from "./workspace/service/compile-workflow/workflow-compiling.provider"; registerLocaleData(en); @@ -182,7 +185,6 @@ registerLocaleData(en); WorkspaceComponent, MenuComponent, OperatorMenuComponent, - WorkflowSuggestionComponent, SettingsComponent, PropertyEditorComponent, VersionsListComponent, @@ -263,6 +265,7 @@ registerLocaleData(en); HubSearchResultComponent, ComputingUnitSelectionComponent, AdminSettingsComponent, + SuggestionFrameComponent, ], imports: [ BrowserModule, diff --git a/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.html b/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.html deleted file mode 100644 index fb6c9d94bb0..00000000000 --- a/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.html +++ /dev/null @@ -1,60 +0,0 @@ -
    -
    -

    Workflow Suggestions

    -

    Click on a suggestion to preview

    -
    - -
    -
    -
    - {{suggestion.description}} -
    - -
    - - - -
    -
    - -
    - No suggestions available -
    -
    -
    diff --git a/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.ts b/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.ts deleted file mode 100644 index 90b5469981c..00000000000 --- a/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.ts +++ /dev/null @@ -1,618 +0,0 @@ -import { Component, OnInit, HostListener } from "@angular/core"; -import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; -import { WorkflowActionService } from "../../../service/workflow-graph/model/workflow-action.service"; -import { WorkflowUtilService } from "../../../service/workflow-graph/util/workflow-util.service"; -import { JointUIService } from "../../../service/joint-ui/joint-ui.service"; -import { NzMessageService } from "ng-zorro-antd/message"; -import { WorkflowPersistService } from "../../../../common/service/workflow-persist/workflow-persist.service"; -import { Workflow, WorkflowContent } from "../../../../common/type/workflow"; -import { cloneDeep, isEqual } from "lodash"; -import { DynamicSchemaService } from "../../../service/dynamic-schema/dynamic-schema.service"; - -interface WorkflowSuggestion { - id: string; - description: string; - operatorsToAdd: { - operatorType: string; - position: { x: number; y: number }; - properties?: object; - }[]; - operatorPropertiesToChange: { - operatorId: string; - properties: object; - }[]; - operatorsToDelete: string[]; // IDs of operators to delete - linksToAdd: { - source: { operatorId: string; portId: string }; - target: { operatorId: string; portId: string }; - }[]; - isPreviewActive: boolean; -} - -@UntilDestroy() -@Component({ - selector: "texera-workflow-suggestion", - templateUrl: "workflow-suggestion.component.html", - styleUrls: ["workflow-suggestion.component.scss"], -}) -export class WorkflowSuggestionComponent implements OnInit { - public suggestions: WorkflowSuggestion[] = []; - public activePreviewId: string | null = null; - public canModify = true; - - // Store the workflow state before a preview is applied - private workflowBeforePreview: Workflow | null = null; - - // Store original operator properties to compare with changed properties - private originalOperatorProperties: Map = new Map(); - - // Store property style maps for highlighting changed properties - private propertyStyleMaps: Map> = new Map(); - - // Flag to prevent clicks inside the component from triggering the document click handler - private clickedInside = false; - - constructor( - private workflowActionService: WorkflowActionService, - private workflowUtilService: WorkflowUtilService, - private jointUIService: JointUIService, - private messageService: NzMessageService, - private workflowPersistService: WorkflowPersistService, - private dynamicSchemaService: DynamicSchemaService - ) {} - - ngOnInit(): void { - this.workflowActionService - .getWorkflowModificationEnabledStream() - .pipe(untilDestroyed(this)) - .subscribe(canModify => (this.canModify = canModify)); - - // Listen to operator highlight events to apply property style maps - this.workflowActionService - .getJointGraphWrapper() - .getJointOperatorHighlightStream() - .pipe(untilDestroyed(this)) - .subscribe(operatorIDArray => { - // Handle operatorID which comes as a readonly string array - // Extract the first operator ID from the array - const operatorID = operatorIDArray.length > 0 ? operatorIDArray[0] : ""; - - if (this.propertyStyleMaps.has(operatorID)) { - // Wait for the property editor to appear - setTimeout(() => { - // Apply property style maps to highlight changed properties - const operatorHighlightedEvent = new CustomEvent("operator-highlighted", { - detail: { - operatorID: operatorID, - propertyStyleMap: this.propertyStyleMaps.get(operatorID), - }, - }); - document.dispatchEvent(operatorHighlightedEvent); - - // Add event listener for the property editor component - this.setupPropertyEditorListener(); - }, 100); - } - }); - - // Load mock suggestions when component initializes - this.loadMockSuggestions(); - } - - @HostListener("click") - onClickInside() { - this.clickedInside = true; - } - - @HostListener("document:click") - onClickOutside() { - // Reset the flag for the next click - this.clickedInside = false; - // Remove this functionality - previews should only be cleared via the Cancel button - // No longer clearing preview when clicking outside - } - - private setupPropertyEditorListener(): void { - // Look for elements with specific CSS class that the property editor uses - const formlyFields = document.querySelectorAll(".formly-field"); - - // Try to find and target the property editor - const propertyEditor = document.querySelector(".property-editor-content"); - if (propertyEditor) { - // Find all the formly fields and apply styles - formlyFields.forEach(field => { - const key = field.getAttribute("data-field-key"); - // Apply highlighting for changed properties - const activeOperatorID = this.workflowActionService - .getJointGraphWrapper() - .getCurrentHighlightedOperatorIDs()[0]; - - if (activeOperatorID && this.propertyStyleMaps.has(activeOperatorID)) { - const styleMap = this.propertyStyleMaps.get(activeOperatorID)!; - if (key && styleMap.has(key)) { - const highlightStyle = styleMap.get(key)!; - // Apply the highlighting style to this field - const fieldElement = field.querySelector(".formly-field-content"); - if (fieldElement) { - fieldElement.setAttribute("style", highlightStyle.toString()); - } - } - } - }); - } - } - - private loadMockSuggestions(): void { - this.suggestions = [ - { - id: "suggestion1", - description: "Add a KeywordSearch operator with sentiment analysis", - operatorsToAdd: [ - { - operatorType: "KeywordSearch", - position: { x: 400, y: 300 }, - properties: { - keyword: "climate change", - attributes: ["content", "title"], - }, - }, - { - operatorType: "SentimentAnalysis", - position: { x: 600, y: 300 }, - properties: { - attribute: "content", - resultAttribute: "sentiment", - }, - }, - ], - operatorPropertiesToChange: [ - { - operatorId: "View-Results-1", - properties: { - limit: 20, - offset: 0, - }, - }, - ], - operatorsToDelete: [], - linksToAdd: [ - { - source: { operatorId: "Source-Scan-1", portId: "output-0" }, - target: { operatorId: "KeywordSearch-1", portId: "input-0" }, - }, - { - source: { operatorId: "KeywordSearch-1", portId: "output-0" }, - target: { operatorId: "SentimentAnalysis-1", portId: "input-0" }, - }, - { - source: { operatorId: "SentimentAnalysis-1", portId: "output-0" }, - target: { operatorId: "View-Results-1", portId: "input-0" }, - }, - ], - isPreviewActive: false, - }, - { - id: "suggestion2", - description: "Replace ScanSource with CSVFileScan for better performance", - operatorsToAdd: [ - { - operatorType: "CSVFileScan", - position: { x: 200, y: 200 }, - properties: { - fileName: "data.csv", - limit: -1, - offset: 0, - schema: "auto", - }, - }, - ], - operatorPropertiesToChange: [], - operatorsToDelete: ["Source-Scan-1"], - linksToAdd: [ - { - source: { operatorId: "CSVFileScan-1", portId: "output-0" }, - target: { operatorId: "View-Results-1", portId: "input-0" }, - }, - ], - isPreviewActive: false, - }, - { - id: "suggestion3", - description: "Enhance workflow with projection and sorting", - operatorsToAdd: [ - { - operatorType: "Projection", - position: { x: 400, y: 200 }, - properties: { - attributes: ["id", "name", "price", "category"], - }, - }, - { - operatorType: "Sort", - position: { x: 600, y: 200 }, - properties: { - sortAttributesList: [ - { - attributeName: "price", - order: "desc", - }, - ], - }, - }, - ], - operatorPropertiesToChange: [ - { - operatorId: "Source-Scan-1", - properties: { - tableName: "products", - limit: 1000, - }, - }, - ], - operatorsToDelete: [], - linksToAdd: [ - { - source: { operatorId: "Source-Scan-1", portId: "output-0" }, - target: { operatorId: "Projection-1", portId: "input-0" }, - }, - { - source: { operatorId: "Projection-1", portId: "output-0" }, - target: { operatorId: "Sort-1", portId: "input-0" }, - }, - { - source: { operatorId: "Sort-1", portId: "output-0" }, - target: { operatorId: "View-Results-1", portId: "input-0" }, - }, - ], - isPreviewActive: false, - }, - ]; - } - - public cancelPreview(): void { - if (this.activePreviewId) { - this.clearPreviewAndRestoreWorkflow(); - this.messageService.info("Preview cancelled"); - } - } - - public togglePreview(suggestion: WorkflowSuggestion): void { - // If there's an active preview, clear it and restore the previous workflow - if (this.activePreviewId) { - this.clearPreviewAndRestoreWorkflow(); - } - - if (this.activePreviewId === suggestion.id) { - // Deactivate preview if clicking the same suggestion (already handled by clearing above) - this.activePreviewId = null; - } else { - // Save the current workflow state before creating the preview - this.saveWorkflowState(); - - // Clear property style maps and original properties - this.propertyStyleMaps.clear(); - this.originalOperatorProperties.clear(); - - // Activate preview for this suggestion - this.activePreviewId = suggestion.id; - this.createPreview(suggestion); - } - } - - private saveWorkflowState(): void { - const currentWorkflow = this.workflowActionService.getWorkflow(); - // Create a deep copy of the workflow to ensure we don't have references to the original - this.workflowBeforePreview = cloneDeep(currentWorkflow); - } - - private restoreWorkflowState(): void { - if (!this.workflowBeforePreview) return; - - // Clear the current workflow - this.workflowActionService.clearWorkflow(); - - // Restore operators and links from the saved workflow state - if (this.workflowBeforePreview.content) { - const content = this.workflowBeforePreview.content; - - // Create array of operators with positions - const operatorsAndPositions = content.operators.map(op => { - const position = content.operatorPositions[op.operatorID]; - return { - op: op, - pos: position, - }; - }); - - // Add all operators and links back to the workflow - this.workflowActionService.addOperatorsAndLinks(operatorsAndPositions, content.links, content.commentBoxes); - } - - // Reset the saved workflow state - this.workflowBeforePreview = null; - - // Clear data structures - this.propertyStyleMaps.clear(); - this.originalOperatorProperties.clear(); - } - - /** - * Calculates which properties have changed between two objects - * @param original The original object - * @param modified The modified object - * @returns Array of property names that have changed - */ - private getChangedPropertyNames(original: object, modified: object): string[] { - const changedProperties: string[] = []; - - // Find all properties that exist in either original or modified - const allKeys = new Set([...Object.keys(original), ...Object.keys(modified)]); - - // Compare each property - allKeys.forEach(key => { - const originalValue = (original as any)[key]; - const modifiedValue = (modified as any)[key]; - - // Check if the property value is different - if (!isEqual(originalValue, modifiedValue)) { - changedProperties.push(key); - } - }); - - return changedProperties; - } - - private createPreview(suggestion: WorkflowSuggestion): void { - const texeraGraph = this.workflowActionService.getTexeraGraph(); - const jointGraph = this.workflowActionService.getJointGraph(); - - // Handle operators to delete first - suggestion.operatorsToDelete.forEach(operatorId => { - if (texeraGraph.hasOperator(operatorId)) { - // Highlight the operator in red before "deleting" it - const operatorCell = jointGraph.getCell(operatorId); - if (operatorCell) { - operatorCell.attr({ - rect: { - fill: "rgba(255, 200, 200, 0.6)", - stroke: "rgba(255, 0, 0, 0.6)", - "stroke-width": 2, - }, - }); - } - } - }); - - // Add preview operators - suggestion.operatorsToAdd.forEach(opToAdd => { - // Create a new operator predicate - const operatorPredicate = this.workflowUtilService.getNewOperatorPredicate(opToAdd.operatorType); - - // Set properties if provided - if (opToAdd.properties) { - Object.assign(operatorPredicate.operatorProperties, opToAdd.properties); - } - - // Add operator to graph - this.workflowActionService.addOperator(operatorPredicate, opToAdd.position); - - // Make the operator semi-transparent to indicate it's a preview - const operatorCell = jointGraph.getCell(operatorPredicate.operatorID); - if (operatorCell) { - operatorCell.attr({ - ".": { opacity: 0.6 }, - rect: { stroke: "#1890ff", "stroke-width": 2 }, - }); - - // Handle operator click to highlight it - operatorCell.on("cell:pointerclick", () => { - // Get the operator ID as a simple string - const opId: string = operatorPredicate.operatorID; - // Now highlight it - use spread syntax to convert single string to varargs - this.workflowActionService.getJointGraphWrapper().highlightOperators(opId); - }); - } - }); - - // Change operator properties for existing operators - suggestion.operatorPropertiesToChange.forEach(propChange => { - const operator = texeraGraph.getOperator(propChange.operatorId); - if (operator) { - // Store original properties for comparison - this.originalOperatorProperties.set(propChange.operatorId, cloneDeep(operator.operatorProperties)); - - // Apply property changes (these will be undone by restoring the workflow state) - const newProperties = { - ...operator.operatorProperties, - ...propChange.properties, - }; - - this.workflowActionService.setOperatorProperty(propChange.operatorId, newProperties); - - // Calculate which properties have changed - const changedPropertyNames = this.getChangedPropertyNames( - this.originalOperatorProperties.get(propChange.operatorId)!, - newProperties - ); - - // Create a map of property styles for highlighting changed properties - const propertyStyleMap = new Map(); - changedPropertyNames.forEach(propName => { - propertyStyleMap.set( - propName, - "background-color: rgba(82, 196, 26, 0.2); border: 1px solid #52c41a; border-radius: 4px;" - ); - }); - - // Store the property style map for this operator - this.propertyStyleMaps.set(propChange.operatorId, propertyStyleMap); - - // Make the operator light green to indicate property changes - const operatorCell = jointGraph.getCell(propChange.operatorId); - if (operatorCell) { - operatorCell.attr({ - rect: { - fill: "rgba(82, 196, 26, 0.2)", - stroke: "#52c41a", - "stroke-width": 2, - }, - }); - - // Handle operator click to highlight it - operatorCell.on("cell:pointerclick", () => { - // Get the operator ID as a simple string - const opId: string = propChange.operatorId; - // Now highlight it - use spread syntax to convert single string to varargs - this.workflowActionService.getJointGraphWrapper().highlightOperators(opId); - }); - } - } - }); - - // Add preview links - suggestion.linksToAdd.forEach(linkToAdd => { - let sourceOperatorId = linkToAdd.source.operatorId; - let targetOperatorId = linkToAdd.target.operatorId; - - if (texeraGraph.hasOperator(sourceOperatorId) && texeraGraph.hasOperator(targetOperatorId)) { - try { - const link = { - linkID: `link-preview-${Date.now()}`, - source: { - operatorID: sourceOperatorId, - portID: linkToAdd.source.portId, - }, - target: { - operatorID: targetOperatorId, - portID: linkToAdd.target.portId, - }, - }; - - this.workflowActionService.addLink(link); - - // Make the link semi-transparent - const linkCell = jointGraph.getCell(link.linkID); - if (linkCell) { - linkCell.attr({ - ".connection": { opacity: 0.6, stroke: "#1890ff" }, - ".marker-target": { opacity: 0.6, fill: "#1890ff" }, - }); - } - } catch (error) { - console.error("Error adding preview link:", error); - } - } - }); - } - - private clearPreviewAndRestoreWorkflow(): void { - // Reset active preview ID - this.activePreviewId = null; - - // Restore the workflow to its state before the preview - this.restoreWorkflowState(); - } - - public applySuggestion(suggestion: WorkflowSuggestion): void { - // First clear any previews and restore the original workflow - this.clearPreviewAndRestoreWorkflow(); - - // Then apply the changes for real - try { - // Delete operators first - if (suggestion.operatorsToDelete.length > 0) { - this.workflowActionService.deleteOperatorsAndLinks(suggestion.operatorsToDelete); - } - - // Add operators - const operatorsToAdd = suggestion.operatorsToAdd.map(opToAdd => { - const operatorPredicate = this.workflowUtilService.getNewOperatorPredicate(opToAdd.operatorType); - - // Set properties if provided - if (opToAdd.properties) { - Object.assign(operatorPredicate.operatorProperties, opToAdd.properties); - } - - return { - op: operatorPredicate, - pos: opToAdd.position, - }; - }); - - // Add all operators at once - this.workflowActionService.addOperatorsAndLinks(operatorsToAdd); - - // Add links - suggestion.linksToAdd.forEach(linkToAdd => { - let sourceOperatorId = linkToAdd.source.operatorId; - let targetOperatorId = linkToAdd.target.operatorId; - - // Map operator IDs if needed - if (!this.workflowActionService.getTexeraGraph().hasOperator(sourceOperatorId)) { - const newOperator = operatorsToAdd.find(op => op.op.operatorType === sourceOperatorId.split("-")[0]); - if (newOperator) sourceOperatorId = newOperator.op.operatorID; - } - - if (!this.workflowActionService.getTexeraGraph().hasOperator(targetOperatorId)) { - const newOperator = operatorsToAdd.find(op => op.op.operatorType === targetOperatorId.split("-")[0]); - if (newOperator) targetOperatorId = newOperator.op.operatorID; - } - - if ( - this.workflowActionService.getTexeraGraph().hasOperator(sourceOperatorId) && - this.workflowActionService.getTexeraGraph().hasOperator(targetOperatorId) - ) { - const link = { - linkID: `link-${Date.now()}`, - source: { - operatorID: sourceOperatorId, - portID: linkToAdd.source.portId, - }, - target: { - operatorID: targetOperatorId, - portID: linkToAdd.target.portId, - }, - }; - - this.workflowActionService.addLink(link); - } - }); - - // Change properties for existing operators - suggestion.operatorPropertiesToChange.forEach(propChange => { - if (this.workflowActionService.getTexeraGraph().hasOperator(propChange.operatorId)) { - const operator = this.workflowActionService.getTexeraGraph().getOperator(propChange.operatorId); - this.workflowActionService.setOperatorProperty(propChange.operatorId, { - ...operator.operatorProperties, - ...propChange.properties, - }); - } - }); - - // Save the workflow to materialize the changes - const workflow = this.workflowActionService.getWorkflow(); - this.workflowPersistService.persistWorkflow(workflow).subscribe(() => { - this.messageService.success("Successfully applied and saved the suggestion!"); - - // Remove the applied suggestion from the list - this.suggestions = this.suggestions.filter(s => s.id !== suggestion.id); - }); - } catch (error) { - console.error("Error applying suggestion:", error); - this.messageService.error("Failed to apply the suggestion."); - } - } - - public dislikeSuggestion(suggestion: WorkflowSuggestion): void { - // If this is the active suggestion, restore the workflow first - if (this.activePreviewId === suggestion.id) { - this.clearPreviewAndRestoreWorkflow(); - } - - // Remove the suggestion from the list - this.suggestions = this.suggestions.filter(s => s.id !== suggestion.id); - - // Show a message to confirm the action - this.messageService.info("Suggestion removed from the list."); - } -} diff --git a/core/gui/src/app/workspace/component/result-panel/result-panel.component.ts b/core/gui/src/app/workspace/component/result-panel/result-panel.component.ts index 98708cf0763..bc5a9760b4c 100644 --- a/core/gui/src/app/workspace/component/result-panel/result-panel.component.ts +++ b/core/gui/src/app/workspace/component/result-panel/result-panel.component.ts @@ -51,6 +51,7 @@ import { PanelService } from "../../service/panel/panel.service"; import { WorkflowCompilingService } from "../../service/compile-workflow/workflow-compiling.service"; import { CompilationState } from "../../types/workflow-compiling.interface"; import { WorkflowFatalError } from "../../types/workflow-websocket.interface"; +import { SuggestionFrameComponent } from "./suggestion-frame/suggestion-frame.component"; export const DEFAULT_WIDTH = 800; export const DEFAULT_HEIGHT = 300; @@ -99,6 +100,9 @@ export class ResultPanelComponent implements OnInit, OnDestroy { } ngOnInit(): void { + // Add suggestions tab to the result panel first + this.displaySuggestions(); + const style = localStorage.getItem("result-panel-style"); if (style) document.getElementById("result-container")!.style.cssText = style; const translates = document.getElementById("result-container")!.style.transform; @@ -218,8 +222,8 @@ export class ResultPanelComponent implements OnInit, OnDestroy { const currentHighlightedOperator = highlightedOperators.length === 1 ? highlightedOperators[0] : undefined; if (this.currentOperatorId !== currentHighlightedOperator) { - // clear everything, prepare for state change - this.clearResultPanel(); + // clear result-related panels, but keep the suggestions tab + this.clearResultPanelExceptSuggestions(); this.currentOperatorId = currentHighlightedOperator; if (!this.currentOperatorId) { @@ -227,6 +231,11 @@ export class ResultPanelComponent implements OnInit, OnDestroy { } } + // Make sure suggestions tab is always available + if (!this.frameComponentConfigs.has("Suggestions")) { + this.displaySuggestions(); + } + if ( this.executeWorkflowService.getExecutionState().state === ExecutionState.Failed || this.workflowCompilingService.getWorkflowCompilationState() === CompilationState.Failed @@ -258,6 +267,22 @@ export class ResultPanelComponent implements OnInit, OnDestroy { this.frameComponentConfigs.clear(); } + /** + * Clears result panel components except for the Suggestions tab + */ + clearResultPanelExceptSuggestions(): void { + // Temporarily store the suggestions component if it exists + const suggestionsComponent = this.frameComponentConfigs.get("Suggestions"); + + // Clear all components + this.frameComponentConfigs.clear(); + + // Restore the suggestions component if it existed + if (suggestionsComponent) { + this.frameComponentConfigs.set("Suggestions", suggestionsComponent); + } + } + displayConsole(operatorId: string, consoleInputEnabled: boolean) { this.frameComponentConfigs.set("Console", { component: ConsoleFrameComponent, @@ -339,11 +364,19 @@ export class ResultPanelComponent implements OnInit, OnDestroy { openPanel() { this.height = DEFAULT_HEIGHT; this.width = DEFAULT_WIDTH; + + // Ensure suggestions tab is available when panel is opened + if (!this.frameComponentConfigs.has("Suggestions")) { + this.displaySuggestions(); + } } closePanel() { this.height = 32.5; this.width = 0; + + // Don't clear suggestions when closing the panel + // Preserve the tab for when the panel is reopened } resetPanelPosition() { @@ -390,4 +423,14 @@ export class ResultPanelComponent implements OnInit, OnDestroy { if (!isDefined(newHeight)) return; this.returnPosition = { x: this.returnPosition.x, y: this.returnPosition.y + prevHeight - newHeight }; } + + /** + * Displays the workflow suggestions in the result panel + */ + displaySuggestions() { + this.frameComponentConfigs.set("Suggestions", { + component: SuggestionFrameComponent, + componentInputs: {}, + }); + } } diff --git a/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.html b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.html new file mode 100644 index 00000000000..97f5592aba6 --- /dev/null +++ b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.html @@ -0,0 +1,86 @@ +
    +
    +
    +

    Workflow Suggestions

    +

    Click on a suggestion to preview

    +
    + +
    +
    + +
    + +
    +
    + {{suggestion.description}} +
    + +
    + + + +
    +
    + +
    + No suggestions available +
    +
    + +
    + +
    +
    +
    diff --git a/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.scss b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.scss similarity index 66% rename from core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.scss rename to core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.scss index f40da3b5e34..9384b2d94b1 100644 --- a/core/gui/src/app/workspace/component/left-panel/workflow-suggestion/workflow-suggestion.component.scss +++ b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.scss @@ -1,4 +1,4 @@ -#suggestion-container { +.suggestion-container { display: flex; flex-direction: column; height: 100%; @@ -27,6 +27,13 @@ padding: 10px; } +.loading-container { + display: flex; + justify-content: center; + align-items: center; + min-height: 200px; +} + .suggestion-item { border: 1px solid #ddd; border-radius: 4px; @@ -63,16 +70,28 @@ display: flex; padding: 5px 10px 10px; flex-wrap: wrap; - gap: 5px; + gap: 8px; + justify-content: space-between; button { flex: 1; min-width: 80px; + margin: 0; + z-index: 100; /* Ensure buttons are on top */ + + /* Fix for specificity */ + &[nz-button] { + margin: 0; + padding: 0 8px; + height: 32px; + line-height: 30px; + } &:nth-child(1) { // Apply button background-color: #52c41a; border-color: #52c41a; + color: white; &:hover { background-color: #73d13d; @@ -108,3 +127,23 @@ color: #999; padding: 20px; } + +// Style for bottom refresh button +.suggestion-container > .suggestion-actions { + border-top: 1px solid #ddd; + padding: 10px; + justify-content: center; +} + +// Style for the container when preview is active +#suggestion-frame-container { + height: 100%; + width: 100%; + overflow: hidden; + position: relative; + + &.preview-active { + // Make sure the suggestion tab takes priority + z-index: 1000; + } +} diff --git a/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts new file mode 100644 index 00000000000..d0f445ca7a5 --- /dev/null +++ b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts @@ -0,0 +1,581 @@ +import { Component, OnInit, HostListener, OnDestroy } from "@angular/core"; +import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; +import { WorkflowActionService } from "../../../service/workflow-graph/model/workflow-action.service"; +import { WorkflowSuggestionService } from "../../../service/workflow-suggestion/workflow-suggestion.service"; +import { WorkflowUtilService } from "../../../service/workflow-graph/util/workflow-util.service"; +import { NzMessageService } from "ng-zorro-antd/message"; +import { WorkflowPersistService } from "../../../../common/service/workflow-persist/workflow-persist.service"; +import { Workflow } from "../../../../common/type/workflow"; +import { cloneDeep, isEqual } from "lodash"; +import { ExecuteWorkflowService } from "../../../service/execute-workflow/execute-workflow.service"; +import { ExecutionState } from "../../../types/execute-workflow.interface"; +import { filter, take } from "rxjs/operators"; +import { WorkflowCompilingService } from "../../../service/compile-workflow/workflow-compiling.service"; +import { Subject, Subscription, interval } from "rxjs"; +import { CompilationState } from "../../../types/workflow-compiling.interface"; + +/** + * SuggestionFrameComponent is a wrapper for the workflow suggestion functionality + * that allows it to be displayed in the result panel as a tab. + */ +@UntilDestroy() +@Component({ + selector: "texera-suggestion-frame", + templateUrl: "./suggestion-frame.component.html", + styleUrls: ["./suggestion-frame.component.scss"], +}) +export class SuggestionFrameComponent implements OnInit, OnDestroy { + // Variables needed for suggestion functionality + public suggestions: any[] = []; + public activePreviewId: string | null = null; + public canModify = true; + public loadingSuggestions = false; + + // Store the workflow state before a preview is applied + private workflowBeforePreview: Workflow | null = null; + + // Store original operator properties to compare with changed properties + private originalOperatorProperties: Map = new Map(); + + // Store property style maps for highlighting changed properties + private propertyStyleMaps: Map> = new Map(); + + // Track if we're in preview mode to prevent tab changes + public isInPreviewMode = false; + // Track the subscription to compilation state changed stream + private compilationStateSubscription: Subscription | null = null; + // Custom subject to handle compilation state changes during preview + private previewCompilationSubject = new Subject(); + // Track the tab focus interval + private tabFocusInterval: Subscription | null = null; + // Store click listeners to prevent unnecessary event binding + private boundHandleDocumentClick: any; + + constructor( + private workflowActionService: WorkflowActionService, + private workflowUtilService: WorkflowUtilService, + private messageService: NzMessageService, + private workflowPersistService: WorkflowPersistService, + private workflowSuggestionService: WorkflowSuggestionService, + private executeWorkflowService: ExecuteWorkflowService, + private workflowCompilingService: WorkflowCompilingService + ) { + this.boundHandleDocumentClick = this.handleDocumentClick.bind(this); + } + + ngOnInit(): void { + // Subscribe to suggestion service + this.workflowSuggestionService + .getSuggestionStream() + .pipe(untilDestroyed(this)) + .subscribe(suggestions => { + // Only update suggestions if not in preview mode + if (!this.isInPreviewMode) { + this.suggestions = suggestions; + this.loadingSuggestions = false; + } + }); + + // Get initial permission state + this.workflowActionService + .getWorkflowModificationEnabledStream() + .pipe(untilDestroyed(this)) + .subscribe(canModify => (this.canModify = canModify)); + + // Monitor execution state to refresh suggestions when workflow completes or fails + this.executeWorkflowService + .getExecutionStateStream() + .pipe( + filter( + event => event.current.state === ExecutionState.Completed || event.current.state === ExecutionState.Failed + ), + untilDestroyed(this) + ) + .subscribe(event => { + if (!this.isInPreviewMode) { + console.log(`SuggestionFrame: Execution state changed to ${event.current.state}, refreshing suggestions`); + this.refreshSuggestions(); + } else { + console.log( + `SuggestionFrame: Execution state changed to ${event.current.state}, but preview is active - skipping refresh` + ); + } + }); + + // Initial refresh of suggestions + this.refreshSuggestions(); + } + + ngOnDestroy(): void { + // Clean up event listener when component is destroyed + this.removeDocumentClickListener(); + this.restoreCompilationListeners(); + + // Clean up the tab focus interval if it exists + if (this.tabFocusInterval) { + this.tabFocusInterval.unsubscribe(); + this.tabFocusInterval = null; + } + } + + /** + * Add document click listener when entering preview mode + */ + private addDocumentClickListener(): void { + if (!document.hasOwnProperty("suggestionsClickListener")) { + document.addEventListener("click", this.boundHandleDocumentClick, true); + // @ts-ignore + document.suggestionsClickListener = true; + } + } + + /** + * Remove document click listener when exiting preview mode + */ + private removeDocumentClickListener(): void { + document.removeEventListener("click", this.boundHandleDocumentClick, true); + // @ts-ignore + delete document.suggestionsClickListener; + } + + /** + * Event handler to prevent tab changes during preview mode + */ + private handleDocumentClick(event: MouseEvent): void { + if (this.isInPreviewMode) { + // Get the closest button within our component to avoid blocking action buttons + const actionButton = (event.target as HTMLElement).closest(".suggestion-actions button"); + // Get the event target + const target = event.target as HTMLElement; + + // Allow clicks within our component actions area + if (actionButton) { + return; + } + + // Check if the click target is a tab or a link that would change focus + if (target && (target.closest(".ant-tabs-tab") || target.closest("a[href]"))) { + // Only prevent default if it's not within our suggestion component + if (!target.closest("texera-suggestion-frame")) { + event.preventDefault(); + event.stopPropagation(); + this.messageService.warning("Please cancel the preview first before changing tabs"); + + // Refocus the suggestion tab + this.focusSuggestionTab(); + } + } + } + } + + /** + * Refreshes the workflow suggestions + */ + public refreshSuggestions(): void { + // Only refresh if there are operators in the workflow and not in preview mode + if (this.isInPreviewMode) { + console.log("Preview mode is active, skipping suggestion refresh"); + return; + } + + const operators = this.workflowActionService.getTexeraGraph().getAllOperators(); + if (operators.length > 0) { + this.loadingSuggestions = true; + this.workflowSuggestionService.refreshSuggestions(); + } + } + + /** + * Toggles the preview of a suggestion + */ + public togglePreview(suggestion: any): void { + // If there's an active preview, clear it and restore the previous workflow + if (this.activePreviewId) { + this.clearPreviewAndRestoreWorkflow(); + } + + if (this.activePreviewId === suggestion.id) { + // Deactivate preview if clicking the same suggestion (already handled by clearing above) + this.activePreviewId = null; + this.isInPreviewMode = false; + + // Notify the suggestion service that preview is no longer active + this.workflowSuggestionService.setPreviewActive(false); + + // Remove the document click listener + this.removeDocumentClickListener(); + + // Clear the tab focus interval + if (this.tabFocusInterval) { + this.tabFocusInterval.unsubscribe(); + this.tabFocusInterval = null; + } + } else { + // Save the current workflow state before creating the preview + this.saveWorkflowState(); + + // Clear property style maps and original properties + this.propertyStyleMaps.clear(); + this.originalOperatorProperties.clear(); + + // Set preview mode to prevent tab changes and suggestion updates + this.isInPreviewMode = true; + + // Add the document click listener to prevent tab changes + this.addDocumentClickListener(); + + // Notify the suggestion service that preview is active + this.workflowSuggestionService.setPreviewActive(true); + + // Activate preview for this suggestion + this.activePreviewId = suggestion.id; + + // Create the preview without requesting new suggestions from backend + this.createPreview(suggestion); + + // Focus on this tab programmatically + this.focusSuggestionTab(); + + // Notify user that they're in preview mode + this.messageService.info("Preview mode active. Any compilation errors will be ignored."); + } + } + + /** + * Disables compilation listeners to prevent tab switching during preview + */ + private disableCompilationListeners(): void { + // We no longer need to intercept compilation changes since the service is now notified + // about preview mode and will handle this internally + } + + /** + * Restores original compilation listeners + */ + private restoreCompilationListeners(): void { + // We no longer need to restore compilation listeners since the service handles this + + // Just clear the tab focus interval if it exists + if (this.tabFocusInterval) { + this.tabFocusInterval.unsubscribe(); + this.tabFocusInterval = null; + } + } + + /** + * Focuses on the suggestion tab to prevent tab changes + */ + private focusSuggestionTab(): void { + // Find the suggestion tab in the result panel and focus on it initially + setTimeout(() => { + const suggestionTab = document.querySelector(".ant-tabs-tab[aria-controls*=\"Suggestions\"]") as HTMLElement; + if (suggestionTab) { + suggestionTab.click(); + console.log("Focused on suggestion tab"); + + // Set up an interval to keep the suggestion tab focused during preview mode + if (this.isInPreviewMode) { + // Clear any existing interval first + if (this.tabFocusInterval) { + this.tabFocusInterval.unsubscribe(); + } + + // Start a new interval that checks and refocuses the suggestion tab if needed + this.tabFocusInterval = interval(300) + .pipe(untilDestroyed(this)) + .subscribe(() => { + if (this.isInPreviewMode) { + const activeTab = document.querySelector(".ant-tabs-tab-active"); + const isSuggestionTabActive = + activeTab && + (activeTab.textContent?.includes("Suggestions") || + activeTab.getAttribute("aria-controls")?.includes("Suggestions")); + + if (!isSuggestionTabActive) { + console.log("Refocusing suggestion tab..."); + const suggestionTab = document.querySelector( + ".ant-tabs-tab[aria-controls*=\"Suggestions\"]" + ) as HTMLElement; + if (suggestionTab) { + suggestionTab.click(); + } + } + } else if (this.tabFocusInterval) { + // If we're no longer in preview mode, clear the interval + this.tabFocusInterval.unsubscribe(); + this.tabFocusInterval = null; + } + }); + } + } + }, 50); + } + + /** + * Applies a suggestion to the workflow + */ + public applySuggestion(suggestion: any): void { + // First clear any previews and restore the original workflow + this.clearPreviewAndRestoreWorkflow(); + + // Then apply the changes for real + try { + const texeraGraph = this.workflowActionService.getTexeraGraph(); + + // Delete operators first + if (suggestion.operatorsToDelete && suggestion.operatorsToDelete.length > 0) { + this.workflowActionService.deleteOperatorsAndLinks(suggestion.operatorsToDelete); + } + + // Add operators + const operatorsToAdd = suggestion.operatorsToAdd.map((opToAdd: any) => { + const operatorPredicate = this.workflowUtilService.getNewOperatorPredicate(opToAdd.operatorType); + + // Set properties if provided + if (opToAdd.properties) { + Object.assign(operatorPredicate.operatorProperties, opToAdd.properties); + } + + return { + op: operatorPredicate, + pos: opToAdd.position, + }; + }); + + // Add all operators at once + this.workflowActionService.addOperatorsAndLinks(operatorsToAdd); + + // Add links + suggestion.linksToAdd.forEach((linkToAdd: any) => { + let sourceOperatorId = linkToAdd.source.operatorId; + let targetOperatorId = linkToAdd.target.operatorId; + + // Map operator IDs if needed + if (!texeraGraph.hasOperator(sourceOperatorId)) { + const newOperator = operatorsToAdd.find((op: any) => op.op.operatorType === sourceOperatorId.split("-")[0]); + if (newOperator) sourceOperatorId = newOperator.op.operatorID; + } + + if (!texeraGraph.hasOperator(targetOperatorId)) { + const newOperator = operatorsToAdd.find((op: any) => op.op.operatorType === targetOperatorId.split("-")[0]); + if (newOperator) targetOperatorId = newOperator.op.operatorID; + } + + if (texeraGraph.hasOperator(sourceOperatorId) && texeraGraph.hasOperator(targetOperatorId)) { + const link = { + linkID: `link-${Date.now()}`, + source: { + operatorID: sourceOperatorId, + portID: linkToAdd.source.portId, + }, + target: { + operatorID: targetOperatorId, + portID: linkToAdd.target.portId, + }, + }; + + this.workflowActionService.addLink(link); + } + }); + + // Change properties for existing operators + if (suggestion.operatorPropertiesToChange) { + suggestion.operatorPropertiesToChange.forEach((propChange: any) => { + if (texeraGraph.hasOperator(propChange.operatorId)) { + const operator = texeraGraph.getOperator(propChange.operatorId); + this.workflowActionService.setOperatorProperty(propChange.operatorId, { + ...operator.operatorProperties, + ...propChange.properties, + }); + } + }); + } + + // Save the workflow to materialize the changes + const workflow = this.workflowActionService.getWorkflow(); + this.workflowPersistService.persistWorkflow(workflow).subscribe(() => { + this.messageService.success("Successfully applied and saved the suggestion!"); + + // Remove the applied suggestion from the list + this.suggestions = this.suggestions.filter(s => s.id !== suggestion.id); + }); + } catch (error) { + console.error("Error applying suggestion:", error); + this.messageService.error("Failed to apply the suggestion."); + } + } + + /** + * Removes a suggestion from the list + */ + public dislikeSuggestion(suggestion: any): void { + // If this is the active suggestion, restore the workflow first + if (this.activePreviewId === suggestion.id) { + this.clearPreviewAndRestoreWorkflow(); + } + + // Remove the suggestion from the list + this.suggestions = this.suggestions.filter(s => s.id !== suggestion.id); + + // Show a message to confirm the action + this.messageService.info("Suggestion removed from the list."); + } + + /** + * Cancels the preview of a suggestion + */ + public cancelPreview(): void { + if (this.activePreviewId) { + this.clearPreviewAndRestoreWorkflow(); + this.messageService.info("Preview cancelled"); + } + } + + /** + * Saves the current workflow state + */ + private saveWorkflowState(): void { + const currentWorkflow = this.workflowActionService.getWorkflow(); + // Create a deep copy of the workflow to ensure we don't have references to the original + this.workflowBeforePreview = cloneDeep(currentWorkflow); + } + + /** + * Clears the preview and restores the original workflow + */ + private clearPreviewAndRestoreWorkflow(): void { + // Reset active preview ID + this.activePreviewId = null; + + // Exit preview mode + this.isInPreviewMode = false; + + // Remove the document click listener + this.removeDocumentClickListener(); + + // Notify the suggestion service that preview is no longer active + this.workflowSuggestionService.setPreviewActive(false); + + // Restore the workflow to its state before the preview + this.restoreWorkflowState(); + } + + /** + * Restores the workflow to its state before the preview + */ + private restoreWorkflowState(): void { + if (!this.workflowBeforePreview) return; + + // Clear the current workflow + this.workflowActionService.clearWorkflow(); + + // Restore operators and links from the saved workflow state + if (this.workflowBeforePreview.content) { + const content = this.workflowBeforePreview.content; + + // Create array of operators with positions + const operatorsAndPositions = content.operators.map(op => { + const position = content.operatorPositions[op.operatorID]; + return { + op: op, + pos: position, + }; + }); + + // Add all operators and links back to the workflow + this.workflowActionService.addOperatorsAndLinks(operatorsAndPositions, content.links, content.commentBoxes); + } + + // Reset the saved workflow state + this.workflowBeforePreview = null; + + // Clear data structures + this.propertyStyleMaps.clear(); + this.originalOperatorProperties.clear(); + } + + /** + * Creates a preview of a suggestion in the workflow + */ + private createPreview(suggestion: any): void { + const texeraGraph = this.workflowActionService.getTexeraGraph(); + const jointGraph = this.workflowActionService.getJointGraph(); + + // Handle operators to delete first + if (suggestion.operatorsToDelete) { + suggestion.operatorsToDelete.forEach((operatorId: string) => { + if (texeraGraph.hasOperator(operatorId)) { + // Highlight the operator in red before "deleting" it + const operatorCell = jointGraph.getCell(operatorId); + if (operatorCell) { + operatorCell.attr({ + rect: { + fill: "rgba(255, 200, 200, 0.6)", + stroke: "rgba(255, 0, 0, 0.6)", + "stroke-width": 2, + }, + }); + } + } + }); + } + + // Add preview operators + suggestion.operatorsToAdd.forEach((opToAdd: any) => { + // Create a new operator predicate + const operatorPredicate = this.workflowUtilService.getNewOperatorPredicate(opToAdd.operatorType); + + // Set properties if provided + if (opToAdd.properties) { + Object.assign(operatorPredicate.operatorProperties, opToAdd.properties); + } + + // Add operator to graph + this.workflowActionService.addOperator(operatorPredicate, opToAdd.position); + + // Make the operator semi-transparent to indicate it's a preview + const operatorCell = jointGraph.getCell(operatorPredicate.operatorID); + if (operatorCell) { + operatorCell.attr({ + ".": { opacity: 0.6 }, + rect: { stroke: "#1890ff", "stroke-width": 2 }, + }); + } + }); + + // Add preview links + suggestion.linksToAdd.forEach((linkToAdd: any) => { + let sourceOperatorId = linkToAdd.source.operatorId; + let targetOperatorId = linkToAdd.target.operatorId; + + if (texeraGraph.hasOperator(sourceOperatorId) && texeraGraph.hasOperator(targetOperatorId)) { + try { + const link = { + linkID: `link-preview-${Date.now()}`, + source: { + operatorID: sourceOperatorId, + portID: linkToAdd.source.portId, + }, + target: { + operatorID: targetOperatorId, + portID: linkToAdd.target.portId, + }, + }; + + this.workflowActionService.addLink(link); + + // Make the link semi-transparent + const linkCell = jointGraph.getCell(link.linkID); + if (linkCell) { + linkCell.attr({ + ".connection": { opacity: 0.6, stroke: "#1890ff" }, + ".marker-target": { opacity: 0.6, fill: "#1890ff" }, + }); + } + } catch (error) { + console.error("Error adding preview link:", error); + } + } + }); + } +} diff --git a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts index 9731d010207..864a5f26f79 100644 --- a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts +++ b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts @@ -1,12 +1,17 @@ import { HttpClient, HttpHeaders } from "@angular/common/http"; -import { Injectable } from "@angular/core"; -import { Observable, ReplaySubject, of } from "rxjs"; -import { catchError, map } from "rxjs/operators"; +import { Injectable, Injector, Inject, Optional } from "@angular/core"; +import { Observable, ReplaySubject, of, merge, BehaviorSubject } from "rxjs"; +import { catchError, map, debounceTime, filter } from "rxjs/operators"; import { AppSettings } from "../../../common/app-setting"; import { WorkflowActionService } from "../workflow-graph/model/workflow-action.service"; -import { WorkflowCompilingService } from "../compile-workflow/workflow-compiling.service"; import { Workflow } from "../../../common/type/workflow"; import { WorkflowResultService } from "../workflow-result/workflow-result.service"; +import { ExecuteWorkflowService } from "../execute-workflow/execute-workflow.service"; +import { ExecutionState, ExecutionStateInfo } from "../../types/execute-workflow.interface"; +// Import the WorkflowCompilingService type for better type checking +import { WorkflowCompilingService } from "../compile-workflow/workflow-compiling.service"; +import { CompilationState } from "../../types/workflow-compiling.interface"; +import { WORKFLOW_COMPILING_SERVICE } from "../compile-workflow/workflow-compiling.provider"; // Define the WorkflowSuggestion interface - this should match the interface in the component export interface WorkflowSuggestion { @@ -14,7 +19,7 @@ export interface WorkflowSuggestion { description: string; operatorsToAdd: { operatorType: string; - position: { x: number, y: number }; + position: { x: number; y: number }; properties?: object; }[]; operatorPropertiesToChange: { @@ -23,8 +28,8 @@ export interface WorkflowSuggestion { }[]; operatorsToDelete: string[]; // IDs of operators to delete linksToAdd: { - source: { operatorId: string, portId: string }; - target: { operatorId: string, portId: string }; + source: { operatorId: string; portId: string }; + target: { operatorId: string; portId: string }; }[]; isPreviewActive: boolean; } @@ -37,7 +42,7 @@ interface ResultPanelData { } // endpoint for workflow suggestions -export const WORKFLOW_SUGGESTION_ENDPOINT = "api/suggest"; +export const WORKFLOW_SUGGESTION_ENDPOINT = "workflow-suggestion"; /** * WorkflowSuggestionService is responsible for communicating with the backend suggestion service. @@ -50,67 +55,207 @@ export const WORKFLOW_SUGGESTION_ENDPOINT = "api/suggest"; export class WorkflowSuggestionService { private suggestionStream = new ReplaySubject(1); + // Stream that indicates whether a preview is currently active + private previewActiveStream = new BehaviorSubject(false); + + // Track previous execution state to avoid duplicate requests + private previousExecutionState: ExecutionState = ExecutionState.Uninitialized; + + // Track when we've last requested suggestions to debounce multiple result updates + private lastResultUpdateTime = 0; + private resultUpdateDebounceMs = 2000; // 2 seconds debounce time for result updates + + private workflowCompilingService: WorkflowCompilingService | undefined; + constructor( private httpClient: HttpClient, private workflowActionService: WorkflowActionService, - private workflowCompilingService: WorkflowCompilingService, - private workflowResultService: WorkflowResultService - ) {} + private workflowResultService: WorkflowResultService, + private executeWorkflowService: ExecuteWorkflowService, + private injector: Injector, + @Optional() @Inject(WORKFLOW_COMPILING_SERVICE) private workflowCompilingServiceRef: WorkflowCompilingService + ) { + console.log("WorkflowSuggestionService initialized"); + + // If the workflow compiling service was injected, use it directly + if (workflowCompilingServiceRef) { + this.workflowCompilingService = workflowCompilingServiceRef; + } + + // Listen for workflow changes and refresh suggestions + // This follows the same pattern as WorkflowCompilingService + merge( + this.workflowActionService.getTexeraGraph().getLinkAddStream(), + this.workflowActionService.getTexeraGraph().getLinkDeleteStream(), + this.workflowActionService.getTexeraGraph().getOperatorAddStream(), + this.workflowActionService.getTexeraGraph().getOperatorDeleteStream(), + this.workflowActionService.getTexeraGraph().getOperatorPropertyChangeStream(), + this.workflowActionService.getTexeraGraph().getDisabledOperatorsChangedStream() + ) + .pipe( + // Debounce to avoid too many requests during rapid changes + debounceTime(1000), + // Skip refreshing if preview is active + filter(() => !this.previewActiveStream.getValue()) + ) + .subscribe(() => { + console.log("WorkflowSuggestionService: Workflow change detected"); + // Only refresh if there are operators in the workflow + const operators = this.workflowActionService.getTexeraGraph().getAllOperators(); + if (operators.length > 0) { + console.log(`WorkflowSuggestionService: Refreshing with ${operators.length} operators`); + this.refreshSuggestions(); + } + }); + + // Subscribe to execution state changes to refresh suggestions + this.executeWorkflowService + .getExecutionStateStream() + .pipe( + filter(event => { + const currentState = event.current.state; + const previousState = event.previous.state; + const targetStates = [ExecutionState.Completed, ExecutionState.Failed, ExecutionState.Paused]; + + // Only refresh when: + // 1. The state has changed (prev != current) + // 2. Current state is one of our target states + // 3. Current state is different from our tracked previous state (to avoid duplicate refreshes) + // 4. Preview is not active + const shouldRefresh = + previousState !== currentState && + targetStates.includes(currentState) && + this.previousExecutionState !== currentState && + !this.previewActiveStream.getValue(); + + // Update our tracked previous state + if (shouldRefresh) { + this.previousExecutionState = currentState; + } + + return shouldRefresh; + }) + ) + .subscribe(event => { + console.log( + `WorkflowSuggestionService: Execution state changed from ${event.previous.state} to ${event.current.state}` + ); + const operators = this.workflowActionService.getTexeraGraph().getAllOperators(); + if (operators.length > 0) { + console.log("WorkflowSuggestionService: Refreshing suggestions after execution state change"); + this.refreshSuggestions(); + } + }); + + // Subscribe to workflow result updates + this.workflowResultService + .getResultUpdateStream() + .pipe( + // Skip if preview is active + filter(() => !this.previewActiveStream.getValue()) + ) + .subscribe(resultUpdate => { + // Only process if there are new results and we're not in a preview + if (resultUpdate && Object.keys(resultUpdate).length > 0) { + console.log("WorkflowSuggestionService: Result update detected for operators:", Object.keys(resultUpdate)); + + // Debounce multiple result updates that come in quick succession + const now = Date.now(); + if (now - this.lastResultUpdateTime > this.resultUpdateDebounceMs) { + this.lastResultUpdateTime = now; + + // Refresh suggestions with the new results + const operators = this.workflowActionService.getTexeraGraph().getAllOperators(); + if (operators.length > 0) { + console.log("WorkflowSuggestionService: Refreshing suggestions after result update"); + this.refreshSuggestions(); + } + } else { + console.log("WorkflowSuggestionService: Skipping result update refresh (debounce)"); + } + } + }); + } + + private getWorkflowCompilingService(): WorkflowCompilingService | undefined { + if (!this.workflowCompilingService) { + try { + // First try to use the injected service + if (this.workflowCompilingServiceRef) { + this.workflowCompilingService = this.workflowCompilingServiceRef; + } else { + // Otherwise, try to get it from the injector + this.workflowCompilingService = this.injector.get(WorkflowCompilingService); + } + } catch (e) { + console.warn("WorkflowCompilingService not available yet:", e); + } + } + return this.workflowCompilingService; + } /** * Requests workflow suggestions from the backend service. * This method gathers the current workflow state, compilation information, * and result data, then sends it to the backend to generate suggestions. - * + * * @returns Observable of workflow suggestions */ public getSuggestions(): Observable { + // Skip if preview is active + if (this.previewActiveStream.getValue()) { + console.log("WorkflowSuggestionService: Preview active, skipping suggestion request"); + return of([]); + } + // Get the current workflow const workflow: Workflow = this.workflowActionService.getWorkflow(); - + // Get compilation state info - const compilationState = { - state: this.workflowCompilingService.getWorkflowCompilationState(), + const workflowCompilingService = this.getWorkflowCompilingService(); + let compilationState = { + state: workflowCompilingService ? workflowCompilingService.getWorkflowCompilationState() : undefined, physicalPlan: undefined, operatorInputSchemaMap: {}, - operatorErrors: this.workflowCompilingService.getWorkflowCompilationErrors() + operatorErrors: workflowCompilingService ? workflowCompilingService.getWorkflowCompilationErrors() : {}, }; + // Get execution state info + const executionState = this.executeWorkflowService.getExecutionState(); + // Get result tables for all operators that have result data - const resultTables: Record = {}; - + const resultTables: Record = {}; + // Get the results from all operators const operators = this.workflowActionService.getTexeraGraph().getAllOperators(); - + operators.forEach(operator => { const operatorId = operator.operatorID; - + // Check if this operator has paginated results const paginatedResultService = this.workflowResultService.getPaginatedResultService(operatorId); if (paginatedResultService) { // Get schema attributes for column names const schema = paginatedResultService.getSchema(); const columnNames = schema.map(attr => attr.attributeName); - + // Select the first page of data (typically 10 rows) // We're using a synchronous approach here to simplify things let rows: object[] = []; - + // If there are results, try to get them if (paginatedResultService.getCurrentTotalNumTuples() > 0) { - paginatedResultService.selectPage(1, 10).subscribe( - pageData => { - rows = pageData.table as object[]; - } - ); + paginatedResultService.selectPage(1, 10).subscribe(pageData => { + rows = pageData.table as object[]; + }); } - + resultTables[operatorId] = { rows: rows, - columnNames: columnNames + columnNames: columnNames, }; - } - + } + // Check if this operator has non-paginated results const resultService = this.workflowResultService.getResultService(operatorId); if (resultService) { @@ -121,10 +266,10 @@ export class WorkflowSuggestionService { // we might not have schema information, so we'll extract column names from the first object const firstRow = resultSnapshot[0] || {}; const columnNames = Object.keys(firstRow); - + resultTables[operatorId] = { rows: Array.from(resultSnapshot), - columnNames: columnNames + columnNames: columnNames, }; } } @@ -134,7 +279,8 @@ export class WorkflowSuggestionService { const requestBody = { workflow: JSON.stringify(workflow), compilationState: compilationState, - resultTables: resultTables + executionState: executionState, + resultTables: resultTables, }; // Send the request to the backend @@ -154,7 +300,7 @@ export class WorkflowSuggestionService { this.suggestionStream.next(suggestions); return suggestions; }), - catchError(error => { + catchError((error: unknown) => { console.error("Error getting workflow suggestions:", error); return of([]); }) @@ -164,18 +310,68 @@ export class WorkflowSuggestionService { /** * Get an observable stream of workflow suggestions. * Components can subscribe to this stream to get the latest suggestions. - * + * * @returns Observable of workflow suggestions */ public getSuggestionStream(): Observable { return this.suggestionStream.asObservable(); } + /** + * Get an observable stream indicating whether a preview is active. + * Components can subscribe to this to know when to skip certain operations. + * + * @returns Observable boolean indicating if a preview is active + */ + public getPreviewActiveStream(): Observable { + return this.previewActiveStream.asObservable(); + } + + /** + * Set whether a preview is currently active. + * This will notify all subscribers to the preview active stream. + * + * @param isActive Whether a preview is currently active + */ + public setPreviewActive(isActive: boolean): void { + console.log(`WorkflowSuggestionService: Setting preview active to ${isActive}`); + this.previewActiveStream.next(isActive); + + // Also update the compilation service if available + const workflowCompilingService = this.getWorkflowCompilingService(); + if (workflowCompilingService && workflowCompilingService.setPreviewActive) { + workflowCompilingService.setPreviewActive(isActive); + } + } + /** * Refresh suggestions by requesting new ones from the backend. * This will update the suggestion stream. + * + * @param isPreview If true, skip the actual request to the backend (used during preview mode) */ - public refreshSuggestions(): void { - this.getSuggestions().subscribe(); + public refreshSuggestions(isPreview: boolean = false): void { + console.log(`Refreshing suggestions in service (isPreview: ${isPreview})`); + + // Skip the request if in preview mode or if preview is active from elsewhere + if (isPreview || this.previewActiveStream.getValue()) { + console.log("Preview mode: skipping suggestion refresh request"); + return; + } + + // Log the operators in the workflow to debug + const operators = this.workflowActionService.getTexeraGraph().getAllOperators(); + console.log( + `Current operators (${operators.length}):`, + operators.map(op => op.operatorID) + ); + + // Log the current execution state + console.log(`Current execution state: ${this.executeWorkflowService.getExecutionState().state}`); + + this.getSuggestions().subscribe( + suggestions => console.log(`Received ${suggestions.length} suggestions from backend`), + (error: unknown) => console.error("Error refreshing suggestions:", error) + ); } -} \ No newline at end of file +} diff --git a/core/suggestion-service/app.py b/core/suggestion-service/app.py index 5de3f7f0922..7367abd8b66 100644 --- a/core/suggestion-service/app.py +++ b/core/suggestion-service/app.py @@ -63,6 +63,11 @@ class CompilationStateInfo(BaseModel): operatorInputSchemaMap: Optional[Dict[str, List[Optional[List[SchemaAttribute]]]]] = None operatorErrors: Optional[Dict[str, Any]] = None +class ExecutionStateInfo(BaseModel): + state: str + currentTuples: Optional[Dict[str, Any]] = None + errorMessages: Optional[List[Dict[str, Any]]] = None + class ResultTable(BaseModel): rows: List[Dict[str, Any]] columnNames: List[str] @@ -70,36 +75,42 @@ class ResultTable(BaseModel): class SuggestionRequest(BaseModel): workflow: str = Field(..., description="JSON string of the workflow") compilationState: CompilationStateInfo + executionState: Optional[ExecutionStateInfo] = None resultTables: Dict[str, ResultTable] @app.get("/") async def root(): return {"message": "Texera Workflow Suggestion Service is running"} -@app.post("/api/suggest", response_model=List[WorkflowSuggestion]) +@app.post("/api/workflow-suggestion", response_model=List[WorkflowSuggestion]) async def generate_suggestions(request: SuggestionRequest): """ Generate workflow suggestions based on the current workflow, compilation state, and result tables. Args: - request: Contains workflow as JSON string, compilation state info, and result tables by operator ID + request: Contains workflow as JSON string, compilation state info, execution state info, and result tables by operator ID Returns: A list of workflow suggestions """ try: # Parse the workflow JSON + print("received request", request) workflow_json = json.loads(request.workflow) # Convert Pydantic models to dictionaries for the suggestion generator - compilation_state_dict = request.compilationState.dict() - result_tables_dict = {op_id: table.dict() for op_id, table in request.resultTables.items()} + compilation_state_dict = request.compilationState.model_dump() + result_tables_dict = {op_id: table.model_dump() for op_id, table in request.resultTables.items()} + + # Include execution state if available + execution_state_dict = request.executionState.model_dump() if request.executionState else None # Generate suggestions using the suggestion engine suggestions = suggestion_generator.generate_suggestions( workflow_json, compilation_state_dict, - result_tables_dict + result_tables_dict, + execution_state_dict ) # Convert the dictionaries to WorkflowSuggestion objects @@ -116,4 +127,4 @@ async def generate_suggestions(request: SuggestionRequest): if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file + uvicorn.run(app, host="0.0.0.0", port=9094) \ No newline at end of file diff --git a/core/suggestion-service/suggestion_engine/generator.py b/core/suggestion-service/suggestion_engine/generator.py index 06f33d42bde..e852ad5017f 100644 --- a/core/suggestion-service/suggestion_engine/generator.py +++ b/core/suggestion-service/suggestion_engine/generator.py @@ -8,42 +8,46 @@ class SuggestionGenerator: """ - Class responsible for generating workflow suggestions + SuggestionGenerator is responsible for generating workflow suggestions + based on the current workflow state, compilation information, and result data. """ def __init__(self): - """Initialize the suggestion generator""" + """ + Initialize the suggestion generator. + """ pass def generate_suggestions( self, workflow_json: Dict[str, Any], compilation_state: Dict[str, Any], - result_tables: Dict[str, Dict[str, Any]] + result_tables: Dict[str, Dict[str, Any]], + execution_state: Optional[Dict[str, Any]] = None ) -> List[Dict[str, Any]]: """ - Generate workflow suggestions based on the current workflow, - compilation state, and result tables. + Generate workflow suggestions based on the current workflow, compilation state, execution state, and result tables. Args: - workflow_json: The workflow as a parsed JSON object - compilation_state: Compilation state information - result_tables: Result tables from operators + workflow_json: The current workflow configuration + compilation_state: Compilation information and errors + result_tables: Result data for each operator + execution_state: Current execution state of the workflow Returns: A list of workflow suggestions """ - # Convert result tables to Tuple objects - tuples_by_operator = {} - for op_id, table in result_tables.items(): - tuples = [] - for row in table["rows"]: - tuples.append(Tuple(row)) - tuples_by_operator[op_id] = tuples + # For debugging purposes + print(f"Generating suggestions for workflow with {len(workflow_json.get('content', {}).get('operators', []))} operators") + print(f"Compilation state: {compilation_state['state']}") + if execution_state: + print(f"Execution state: {execution_state['state']}") + print(f"Result tables available for {len(result_tables)} operators") - # Here you would implement actual suggestion logic - # For now, return mock suggestions + # Extract operators from the workflow + operators = workflow_json.get("content", {}).get("operators", []) + # Create a list to store suggestions suggestions = [] # Add suggestions based on the workflow state @@ -89,7 +93,82 @@ def generate_suggestions( } suggestions.append(suggestion1) - # Add mock suggestion 2: Replace ScanSource with CSVFileScan + # Add suggestions based on execution state + if execution_state and execution_state["state"] == "Completed": + # Add a suggestion for optimization if workflow completed successfully + suggestion3 = { + "id": f"suggestion-{uuid.uuid4()}", + "description": "Optimize workflow with projection and sorting", + "operatorsToAdd": [ + { + "operatorType": "Projection", + "position": {"x": 400, "y": 200}, + "properties": {"attributes": ["id", "name", "price", "category"]} + }, + { + "operatorType": "Sort", + "position": {"x": 600, "y": 200}, + "properties": { + "sortAttributesList": [ + { + "attributeName": "price", + "order": "desc" + } + ] + } + } + ], + "operatorPropertiesToChange": [ + { + "operatorId": "Source-Scan-1", + "properties": {"tableName": "products", "limit": 1000} + } + ], + "operatorsToDelete": [], + "linksToAdd": [ + { + "source": {"operatorId": "Source-Scan-1", "portId": "output-0"}, + "target": {"operatorId": "Projection-1", "portId": "input-0"} + }, + { + "source": {"operatorId": "Projection-1", "portId": "output-0"}, + "target": {"operatorId": "Sort-1", "portId": "input-0"} + }, + { + "source": {"operatorId": "Sort-1", "portId": "output-0"}, + "target": {"operatorId": "View-Results-1", "portId": "input-0"} + } + ], + "isPreviewActive": False + } + suggestions.append(suggestion3) + + # If execution state has errors, add suggestions for fixing them + if execution_state and execution_state["state"] == "Failed" and execution_state.get("errorMessages"): + suggestion_error_fix = { + "id": f"suggestion-{uuid.uuid4()}", + "description": "Fix data type issues in workflow", + "operatorsToAdd": [ + { + "operatorType": "TypeConversion", + "position": {"x": 300, "y": 250}, + "properties": {"targetType": "string", "attributeToConvert": "id"} + } + ], + "operatorPropertiesToChange": [], + "operatorsToDelete": [], + "linksToAdd": [ + { + "source": {"operatorId": "Source-Scan-1", "portId": "output-0"}, + "target": {"operatorId": "TypeConversion-1", "portId": "input-0"} + } + ], + "isPreviewActive": False + } + suggestions.append(suggestion_error_fix) + + # Add suggestion based on the operator types + if any(op.get("operatorType") == "CSVFileScan" for op in operators): suggestion2 = { "id": f"suggestion-{uuid.uuid4()}", "description": "Replace ScanSource with CSVFileScan for better performance", From 13e58587963b3a79eb19bc942ec089a5ffbff745 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 16 Apr 2025 11:20:40 -0700 Subject: [PATCH 014/104] remove redundant providers code --- core/gui/src/app/app.module.ts | 4 +--- .../workflow-suggestion/workflow-suggestion.service.ts | 3 +-- core/suggestion-service/suggestion_engine/generator.py | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/core/gui/src/app/app.module.ts b/core/gui/src/app/app.module.ts index 072da76d0c9..19039d2d3d2 100644 --- a/core/gui/src/app/app.module.ts +++ b/core/gui/src/app/app.module.ts @@ -171,9 +171,7 @@ import { NzSliderModule } from "ng-zorro-antd/slider"; import { AdminSettingsComponent } from "./dashboard/component/admin/settings/admin-settings.component"; import { catchError, of } from "rxjs"; import { SuggestionFrameComponent } from "./workspace/component/result-panel/suggestion-frame/suggestion-frame.component"; -// Import providers for circular dependency resolution -import { WORKFLOW_SUGGESTION_PROVIDER } from "./workspace/service/workflow-suggestion/workflow-suggestion.provider"; -import { WORKFLOW_COMPILING_PROVIDER } from "./workspace/service/compile-workflow/workflow-compiling.provider"; + registerLocaleData(en); diff --git a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts index 864a5f26f79..fa01c7d4f6e 100644 --- a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts +++ b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts @@ -11,7 +11,6 @@ import { ExecutionState, ExecutionStateInfo } from "../../types/execute-workflow // Import the WorkflowCompilingService type for better type checking import { WorkflowCompilingService } from "../compile-workflow/workflow-compiling.service"; import { CompilationState } from "../../types/workflow-compiling.interface"; -import { WORKFLOW_COMPILING_SERVICE } from "../compile-workflow/workflow-compiling.provider"; // Define the WorkflowSuggestion interface - this should match the interface in the component export interface WorkflowSuggestion { @@ -73,7 +72,7 @@ export class WorkflowSuggestionService { private workflowResultService: WorkflowResultService, private executeWorkflowService: ExecuteWorkflowService, private injector: Injector, - @Optional() @Inject(WORKFLOW_COMPILING_SERVICE) private workflowCompilingServiceRef: WorkflowCompilingService + private workflowCompilingServiceRef: WorkflowCompilingService ) { console.log("WorkflowSuggestionService initialized"); diff --git a/core/suggestion-service/suggestion_engine/generator.py b/core/suggestion-service/suggestion_engine/generator.py index e852ad5017f..b21f45fa4cb 100644 --- a/core/suggestion-service/suggestion_engine/generator.py +++ b/core/suggestion-service/suggestion_engine/generator.py @@ -43,6 +43,7 @@ def generate_suggestions( if execution_state: print(f"Execution state: {execution_state['state']}") print(f"Result tables available for {len(result_tables)} operators") + print("Result tables: ", result_tables) # Extract operators from the workflow operators = workflow_json.get("content", {}).get("operators", []) From 53431b49682a0025b4df8da03dd4bb27f2f9db73 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 16 Apr 2025 18:26:14 -0700 Subject: [PATCH 015/104] add test data --- .../workflow-compiling.service.ts | 7 + .../workflow-suggestion.service.ts | 43 +-- core/suggestion-service/README.md | 49 +++ core/suggestion-service/app.py | 18 + .../suggestion_engine/generator.py | 50 ++- .../test/data/workflow1/execution_state.json | 5 + .../test/data/workflow1/result_tables.json | 1 + .../test/data/workflow1/workflow.json | 162 +++++++++ .../workflow1/workflow_compilation_state.json | 64 ++++ .../test/data/workflow2/execution_state.json | 5 + .../test/data/workflow2/result_tables.json | 1 + .../test/data/workflow2/workflow.json | 267 +++++++++++++++ .../workflow2/workflow_compilation_state.json | 108 ++++++ .../test/data/workflow3/execution_state.json | 5 + .../test/data/workflow3/result_tables.json | 1 + .../test/data/workflow3/workflow.json | 313 ++++++++++++++++++ .../workflow3/workflow_compilation_state.json | 150 +++++++++ .../suggestion-service/test_interpretation.py | 172 ++++++++++ .../workflow_interpretation/__init__.py | 3 + .../workflow_interpretation/interpreter.py | 269 +++++++++++++++ 20 files changed, 1653 insertions(+), 40 deletions(-) create mode 100644 core/suggestion-service/README.md create mode 100644 core/suggestion-service/test/data/workflow1/execution_state.json create mode 100644 core/suggestion-service/test/data/workflow1/result_tables.json create mode 100644 core/suggestion-service/test/data/workflow1/workflow.json create mode 100644 core/suggestion-service/test/data/workflow1/workflow_compilation_state.json create mode 100644 core/suggestion-service/test/data/workflow2/execution_state.json create mode 100644 core/suggestion-service/test/data/workflow2/result_tables.json create mode 100644 core/suggestion-service/test/data/workflow2/workflow.json create mode 100644 core/suggestion-service/test/data/workflow2/workflow_compilation_state.json create mode 100644 core/suggestion-service/test/data/workflow3/execution_state.json create mode 100644 core/suggestion-service/test/data/workflow3/result_tables.json create mode 100644 core/suggestion-service/test/data/workflow3/workflow.json create mode 100644 core/suggestion-service/test/data/workflow3/workflow_compilation_state.json create mode 100644 core/suggestion-service/test_interpretation.py create mode 100644 core/suggestion-service/workflow_interpretation/__init__.py create mode 100644 core/suggestion-service/workflow_interpretation/interpreter.py diff --git a/core/gui/src/app/workspace/service/compile-workflow/workflow-compiling.service.ts b/core/gui/src/app/workspace/service/compile-workflow/workflow-compiling.service.ts index a82a8fdd597..bf2e58ba10f 100644 --- a/core/gui/src/app/workspace/service/compile-workflow/workflow-compiling.service.ts +++ b/core/gui/src/app/workspace/service/compile-workflow/workflow-compiling.service.ts @@ -123,6 +123,13 @@ export class WorkflowCompilingService { return this.currentCompilationStateInfo.state; } + public getOperatorInputSchemaMap(): Record { + if (this.currentCompilationStateInfo.state === CompilationState.Succeeded || this.currentCompilationStateInfo.state === CompilationState.Failed) { + return this.currentCompilationStateInfo.operatorInputSchemaMap; + } + return {}; + } + public getWorkflowCompilationErrors(): Readonly> { if ( this.currentCompilationStateInfo.state === CompilationState.Succeeded || diff --git a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts index fa01c7d4f6e..eaae893f08c 100644 --- a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts +++ b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts @@ -64,22 +64,14 @@ export class WorkflowSuggestionService { private lastResultUpdateTime = 0; private resultUpdateDebounceMs = 2000; // 2 seconds debounce time for result updates - private workflowCompilingService: WorkflowCompilingService | undefined; - constructor( private httpClient: HttpClient, private workflowActionService: WorkflowActionService, private workflowResultService: WorkflowResultService, private executeWorkflowService: ExecuteWorkflowService, private injector: Injector, - private workflowCompilingServiceRef: WorkflowCompilingService + private workflowCompilingService: WorkflowCompilingService ) { - console.log("WorkflowSuggestionService initialized"); - - // If the workflow compiling service was injected, use it directly - if (workflowCompilingServiceRef) { - this.workflowCompilingService = workflowCompilingServiceRef; - } // Listen for workflow changes and refresh suggestions // This follows the same pattern as WorkflowCompilingService @@ -176,23 +168,6 @@ export class WorkflowSuggestionService { }); } - private getWorkflowCompilingService(): WorkflowCompilingService | undefined { - if (!this.workflowCompilingService) { - try { - // First try to use the injected service - if (this.workflowCompilingServiceRef) { - this.workflowCompilingService = this.workflowCompilingServiceRef; - } else { - // Otherwise, try to get it from the injector - this.workflowCompilingService = this.injector.get(WorkflowCompilingService); - } - } catch (e) { - console.warn("WorkflowCompilingService not available yet:", e); - } - } - return this.workflowCompilingService; - } - /** * Requests workflow suggestions from the backend service. * This method gathers the current workflow state, compilation information, @@ -210,13 +185,12 @@ export class WorkflowSuggestionService { // Get the current workflow const workflow: Workflow = this.workflowActionService.getWorkflow(); - // Get compilation state info - const workflowCompilingService = this.getWorkflowCompilingService(); + let compilationState = { - state: workflowCompilingService ? workflowCompilingService.getWorkflowCompilationState() : undefined, + state: this.workflowCompilingService.getWorkflowCompilationState(), physicalPlan: undefined, - operatorInputSchemaMap: {}, - operatorErrors: workflowCompilingService ? workflowCompilingService.getWorkflowCompilationErrors() : {}, + operatorInputSchemaMap: this.workflowCompilingService.getOperatorInputSchemaMap(), + operatorErrors: this.workflowCompilingService.getWorkflowCompilationErrors() }; // Get execution state info @@ -335,12 +309,7 @@ export class WorkflowSuggestionService { public setPreviewActive(isActive: boolean): void { console.log(`WorkflowSuggestionService: Setting preview active to ${isActive}`); this.previewActiveStream.next(isActive); - - // Also update the compilation service if available - const workflowCompilingService = this.getWorkflowCompilingService(); - if (workflowCompilingService && workflowCompilingService.setPreviewActive) { - workflowCompilingService.setPreviewActive(isActive); - } + this.workflowCompilingService.setPreviewActive(isActive); } /** diff --git a/core/suggestion-service/README.md b/core/suggestion-service/README.md new file mode 100644 index 00000000000..7d4c8beeb69 --- /dev/null +++ b/core/suggestion-service/README.md @@ -0,0 +1,49 @@ +## Workflow Suggestion Generator + +### Prompt Generation Layer + +This layer is responsible for processing the given information regarding the workflow, compilation state, execution state and result, into the better natural language description. Once these natural language descriptions are generated, they can be passed to the LLM agent. + +Prompt generation can be divided into 2 major packages, each package provides a single endpoint: +#### 1. Workflow Static Interpretation +- Endpoint name: interpretWorkflow +- Endpoint intput parameters: + - workflow (dict) + - input schema for each operator (dict) + - static error for each operator (dict) + - interpretation method type + 1. RAW: simply use the below template + ``` + Here is the workflow dict: + ${string representation of workflow dict} + + Here is the input schema for each operator: + ${string representation of the input schema dict} + ``` + 2. BY_PATH + - use the `TexeraWorkflow` class to parse the workflow dict and input schema dict + - parse out all the paths from the DAG structure. A path is a sub workflow which is a single line, from source operator to the sink operator. This subworkflow should also carry the schema information extracted from the input schema dict, and the static error information, on each operator + + - use the below template to generate the description from the paths and schemas: + ``` + Here are the existing paths in this workflow and related schemas + + Path1: ${string representation of the path's sub-workflow} + + Path2: ${string representation of the path's sub-workflow} + ... + ``` +- Endpoint output parameters: + - natural language description (a string) + + + +#### 2. Workflow Interpretation with Execution information +TODO + +### LLM agent calling layers + +This layer is responsible for calling language models to get the response(the suggestions). + +1. Have a package to talk to different language models using the same unified function. +2. Have the configuration layer of the underlying language models, i.e. certain model, or multiple language model agents. diff --git a/core/suggestion-service/app.py b/core/suggestion-service/app.py index 7367abd8b66..87a72a1f661 100644 --- a/core/suggestion-service/app.py +++ b/core/suggestion-service/app.py @@ -1,4 +1,5 @@ import json +import os from typing import Dict, List, Any, Optional from fastapi import FastAPI, HTTPException @@ -105,6 +106,23 @@ async def generate_suggestions(request: SuggestionRequest): # Include execution state if available execution_state_dict = request.executionState.model_dump() if request.executionState else None + # Create a data directory if it doesn't exist + os.makedirs("test/data", exist_ok=True) + + # Save the workflow data as JSON files + with open("test/data/workflow.json", "w") as f: + json.dump(workflow_json, f, indent=2) + + with open("test/data/workflow_compilation_state.json", "w") as f: + json.dump(compilation_state_dict, f, indent=2) + + with open("test/data/result_tables.json", "w") as f: + json.dump(result_tables_dict, f, indent=2) + + if execution_state_dict: + with open("test/data/execution_state.json", "w") as f: + json.dump(execution_state_dict, f, indent=2) + # Generate suggestions using the suggestion engine suggestions = suggestion_generator.generate_suggestions( workflow_json, diff --git a/core/suggestion-service/suggestion_engine/generator.py b/core/suggestion-service/suggestion_engine/generator.py index b21f45fa4cb..47f151dd886 100644 --- a/core/suggestion-service/suggestion_engine/generator.py +++ b/core/suggestion-service/suggestion_engine/generator.py @@ -2,6 +2,7 @@ import json import uuid +from workflow_interpretation.interpreter import WorkflowInterpreter, InterpretationMethod from model.Tuple import Tuple from model.DataSchema import DataSchema, Attribute, AttributeType @@ -16,7 +17,7 @@ def __init__(self): """ Initialize the suggestion generator. """ - pass + self.workflow_interpreter = WorkflowInterpreter() def generate_suggestions( self, @@ -43,7 +44,16 @@ def generate_suggestions( if execution_state: print(f"Execution state: {execution_state['state']}") print(f"Result tables available for {len(result_tables)} operators") - print("Result tables: ", result_tables) + + # Generate natural language description of the workflow + workflow_description = self._generate_workflow_prompt( + workflow_json, + compilation_state.get("operatorInputSchemaMap"), + compilation_state.get("operatorErrors") + ) + + print("Generated workflow description:") + print(workflow_description) # Extract operators from the workflow operators = workflow_json.get("content", {}).get("operators", []) @@ -192,4 +202,38 @@ def generate_suggestions( } suggestions.append(suggestion2) - return suggestions \ No newline at end of file + return suggestions + + def _generate_workflow_prompt( + self, + workflow_json: Dict[str, Any], + input_schema: Optional[Dict[str, Any]] = None, + operator_errors: Optional[Dict[str, Any]] = None, + method: InterpretationMethod = InterpretationMethod.BY_PATH + ) -> str: + """ + Generate a natural language description of the workflow for use in prompts. + + Args: + workflow_json: The workflow dictionary + input_schema: The input schema dictionary for each operator + operator_errors: Dictionary of static errors for each operator + method: The interpretation method to use + + Returns: + A natural language description of the workflow + """ + try: + # Use the workflow interpreter to generate a description + description = self.workflow_interpreter.interpret_workflow( + workflow_json, + input_schema, + operator_errors, + method + ) + + return description + except Exception as e: + print(f"Error generating workflow prompt: {str(e)}") + # Fallback to a simple description if interpretation fails + return f"Workflow with {len(workflow_json.get('content', {}).get('operators', []))} operators" \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow1/execution_state.json b/core/suggestion-service/test/data/workflow1/execution_state.json new file mode 100644 index 00000000000..6f885c3d09f --- /dev/null +++ b/core/suggestion-service/test/data/workflow1/execution_state.json @@ -0,0 +1,5 @@ +{ + "state": "Uninitialized", + "currentTuples": null, + "errorMessages": null +} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow1/result_tables.json b/core/suggestion-service/test/data/workflow1/result_tables.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/core/suggestion-service/test/data/workflow1/result_tables.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow1/workflow.json b/core/suggestion-service/test/data/workflow1/workflow.json new file mode 100644 index 00000000000..1e5bb4fe2c5 --- /dev/null +++ b/core/suggestion-service/test/data/workflow1/workflow.json @@ -0,0 +1,162 @@ +{ + "wid": 2748, + "name": "Suggestion Test: 1-path-compilation-succeed", + "description": null, + "content": { + "operators": [ + { + "operatorID": "CSVFileScan-operator-02af511d-2912-4d48-a86e-8ac73a6e2b0e", + "operatorType": "CSVFileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "fileEncoding": "UTF_8", + "customDelimiter": ",", + "hasHeader": true, + "fileName": "/workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv" + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "CSV File Scan", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "tweet_id" + }, + { + "alias": "", + "originalAttribute": "create_at_month" + }, + { + "alias": "", + "originalAttribute": "favorite_count" + }, + { + "alias": "", + "originalAttribute": "retweet_count" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3", + "operatorType": "Sort", + "operatorVersion": "N/A", + "operatorProperties": { + "attributes": [ + { + "attribute": "favorite_count", + "sortPreference": "DESC" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sort", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + } + ], + "operatorPositions": { + "CSVFileScan-operator-02af511d-2912-4d48-a86e-8ac73a6e2b0e": { + "x": 472, + "y": 396 + }, + "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894": { + "x": 657, + "y": 395 + }, + "Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3": { + "x": 825, + "y": 396 + } + }, + "links": [ + { + "linkID": "link-4a444193-d4a7-4a2d-ac53-3425bc5bfda0", + "source": { + "operatorID": "CSVFileScan-operator-02af511d-2912-4d48-a86e-8ac73a6e2b0e", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894", + "portID": "input-0" + } + }, + { + "linkID": "link-1586b353-1954-4064-a38a-3c9539306c07", + "source": { + "operatorID": "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894", + "portID": "output-0" + }, + "target": { + "operatorID": "Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3", + "portID": "input-0" + } + } + ], + "commentBoxes": [], + "settings": { + "dataTransferBatchSize": 400 + } + }, + "creationTime": 1744844136205, + "lastModifiedTime": 1744844136205, + "isPublic": false +} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow1/workflow_compilation_state.json b/core/suggestion-service/test/data/workflow1/workflow_compilation_state.json new file mode 100644 index 00000000000..3356e0f70b0 --- /dev/null +++ b/core/suggestion-service/test/data/workflow1/workflow_compilation_state.json @@ -0,0 +1,64 @@ +{ + "state": "Succeeded", + "physicalPlan": null, + "operatorInputSchemaMap": { + "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ], + "Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] + ], + "CSVFileScan-operator-02af511d-2912-4d48-a86e-8ac73a6e2b0e": [] + }, + "operatorErrors": {} +} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow2/execution_state.json b/core/suggestion-service/test/data/workflow2/execution_state.json new file mode 100644 index 00000000000..6f885c3d09f --- /dev/null +++ b/core/suggestion-service/test/data/workflow2/execution_state.json @@ -0,0 +1,5 @@ +{ + "state": "Uninitialized", + "currentTuples": null, + "errorMessages": null +} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow2/result_tables.json b/core/suggestion-service/test/data/workflow2/result_tables.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/core/suggestion-service/test/data/workflow2/result_tables.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow2/workflow.json b/core/suggestion-service/test/data/workflow2/workflow.json new file mode 100644 index 00000000000..9ae537b5d5b --- /dev/null +++ b/core/suggestion-service/test/data/workflow2/workflow.json @@ -0,0 +1,267 @@ +{ + "name": "Suggestion Test: 2-path-compilation-succeed", + "description": null, + "wid": 2749, + "content": { + "operators": [ + { + "operatorID": "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef", + "operatorType": "CSVFileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "fileEncoding": "UTF_8", + "customDelimiter": ",", + "hasHeader": true, + "fileName": "/workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv" + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "CSV File Scan", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "tweet_id" + }, + { + "alias": "", + "originalAttribute": "create_at_month" + }, + { + "alias": "", + "originalAttribute": "favorite_count" + }, + { + "alias": "", + "originalAttribute": "retweet_count" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260", + "operatorType": "Sort", + "operatorVersion": "N/A", + "operatorProperties": { + "attributes": [ + { + "attribute": "favorite_count", + "sortPreference": "DESC" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sort", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "text" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289", + "operatorType": "PythonUDFV2", + "operatorVersion": "N/A", + "operatorProperties": { + "code": "# Choose from the following templates:\n# \nfrom pytexera import *\n\nclass ProcessTupleOperator(UDFOperatorV2):\n \n @overrides\n def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:\n tuple_['text_length'] = len(tuple_['text'])\n yield tuple_\n\n# class ProcessBatchOperator(UDFBatchOperator):\n# BATCH_SIZE = 10 # must be a positive integer\n# \n# @overrides\n# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]:\n# yield batch\n# \n# class ProcessTableOperator(UDFTableOperator):\n# \n# @overrides\n# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:\n# yield table\n", + "workers": 1, + "retainInputColumns": true, + "outputColumns": [ + { + "attributeName": "text_length", + "attributeType": "integer" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Python UDF", + "dynamicInputPorts": true, + "dynamicOutputPorts": true + } + ], + "operatorPositions": { + "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef": { + "x": 472, + "y": 396 + }, + "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f": { + "x": 657, + "y": 363 + }, + "Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260": { + "x": 816, + "y": 362 + }, + "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac": { + "x": 661, + "y": 501 + }, + "PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289": { + "x": 820, + "y": 497 + } + }, + "links": [ + { + "linkID": "link-4a444193-d4a7-4a2d-ac53-3425bc5bfda0", + "source": { + "operatorID": "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f", + "portID": "input-0" + } + }, + { + "linkID": "link-1586b353-1954-4064-a38a-3c9539306c07", + "source": { + "operatorID": "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f", + "portID": "output-0" + }, + "target": { + "operatorID": "Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260", + "portID": "input-0" + } + }, + { + "linkID": "63989c89-c9e7-470c-b107-bc6d154b2ed0", + "source": { + "operatorID": "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac", + "portID": "input-0" + } + }, + { + "linkID": "710b40d5-3142-4970-ae65-3ee210690f6f", + "source": { + "operatorID": "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac", + "portID": "output-0" + }, + "target": { + "operatorID": "PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289", + "portID": "input-0" + } + } + ], + "commentBoxes": [], + "settings": { + "dataTransferBatchSize": 400 + } + }, + "creationTime": 1744845032285, + "lastModifiedTime": 1744845032285, + "isPublished": false, + "readonly": false +} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow2/workflow_compilation_state.json b/core/suggestion-service/test/data/workflow2/workflow_compilation_state.json new file mode 100644 index 00000000000..6a42f30864d --- /dev/null +++ b/core/suggestion-service/test/data/workflow2/workflow_compilation_state.json @@ -0,0 +1,108 @@ +{ + "state": "Succeeded", + "physicalPlan": null, + "operatorInputSchemaMap": { + "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef": [], + "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ], + "PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289": [ + [ + { + "attributeName": "text", + "attributeType": "string" + } + ] + ], + "Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] + ], + "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ] + }, + "operatorErrors": {} +} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow3/execution_state.json b/core/suggestion-service/test/data/workflow3/execution_state.json new file mode 100644 index 00000000000..6f885c3d09f --- /dev/null +++ b/core/suggestion-service/test/data/workflow3/execution_state.json @@ -0,0 +1,5 @@ +{ + "state": "Uninitialized", + "currentTuples": null, + "errorMessages": null +} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow3/result_tables.json b/core/suggestion-service/test/data/workflow3/result_tables.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/core/suggestion-service/test/data/workflow3/result_tables.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow3/workflow.json b/core/suggestion-service/test/data/workflow3/workflow.json new file mode 100644 index 00000000000..fcbfa52184e --- /dev/null +++ b/core/suggestion-service/test/data/workflow3/workflow.json @@ -0,0 +1,313 @@ +{ + "name": "Suggestion Test: 2-path-compilation-failed", + "description": null, + "wid": 2750, + "content": { + "operators": [ + { + "operatorID": "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1", + "operatorType": "CSVFileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "fileEncoding": "UTF_8", + "customDelimiter": ",", + "hasHeader": true, + "fileName": "/workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv" + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "CSV File Scan", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "tweet_id" + }, + { + "alias": "", + "originalAttribute": "create_at_month" + }, + { + "alias": "", + "originalAttribute": "favorite_count" + }, + { + "alias": "", + "originalAttribute": "retweet_count" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2", + "operatorType": "Sort", + "operatorVersion": "N/A", + "operatorProperties": { + "attributes": [ + { + "attribute": "favorite_count", + "sortPreference": "DESC" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sort", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "text" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c", + "operatorType": "PythonUDFV2", + "operatorVersion": "N/A", + "operatorProperties": { + "code": "# Choose from the following templates:\n# \nfrom pytexera import *\n\nclass ProcessTupleOperator(UDFOperatorV2):\n \n @overrides\n def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:\n tuple_['text_length'] = len(tuple_['text'])\n yield tuple_\n\n# class ProcessBatchOperator(UDFBatchOperator):\n# BATCH_SIZE = 10 # must be a positive integer\n# \n# @overrides\n# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]:\n# yield batch\n# \n# class ProcessTableOperator(UDFTableOperator):\n# \n# @overrides\n# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:\n# yield table\n", + "workers": 1, + "retainInputColumns": true, + "outputColumns": [ + { + "attributeName": "text_length", + "attributeType": "integer" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Python UDF", + "dynamicInputPorts": true, + "dynamicOutputPorts": true + }, + { + "operatorID": "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865", + "operatorType": "LineChart", + "operatorVersion": "N/A", + "operatorProperties": { + "yLabel": "Y Axis", + "xLabel": "X Axis" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Line Chart", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + } + ], + "operatorPositions": { + "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1": { + "x": 472, + "y": 396 + }, + "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c": { + "x": 657, + "y": 363 + }, + "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2": { + "x": 816, + "y": 362 + }, + "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16": { + "x": 661, + "y": 501 + }, + "PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c": { + "x": 820, + "y": 497 + }, + "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865": { + "x": 966, + "y": 357 + } + }, + "links": [ + { + "linkID": "link-4a444193-d4a7-4a2d-ac53-3425bc5bfda0", + "source": { + "operatorID": "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c", + "portID": "input-0" + } + }, + { + "linkID": "link-1586b353-1954-4064-a38a-3c9539306c07", + "source": { + "operatorID": "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c", + "portID": "output-0" + }, + "target": { + "operatorID": "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2", + "portID": "input-0" + } + }, + { + "linkID": "63989c89-c9e7-470c-b107-bc6d154b2ed0", + "source": { + "operatorID": "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16", + "portID": "input-0" + } + }, + { + "linkID": "710b40d5-3142-4970-ae65-3ee210690f6f", + "source": { + "operatorID": "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16", + "portID": "output-0" + }, + "target": { + "operatorID": "PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c", + "portID": "input-0" + } + }, + { + "linkID": "link-2883d565-72d0-4d01-a210-fead4ad076a3", + "source": { + "operatorID": "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2", + "portID": "output-0" + }, + "target": { + "operatorID": "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865", + "portID": "input-0" + } + } + ], + "commentBoxes": [], + "settings": { + "dataTransferBatchSize": 400 + } + }, + "creationTime": 1744845299552, + "lastModifiedTime": 1744845299552, + "isPublished": false, + "readonly": false +} \ No newline at end of file diff --git a/core/suggestion-service/test/data/workflow3/workflow_compilation_state.json b/core/suggestion-service/test/data/workflow3/workflow_compilation_state.json new file mode 100644 index 00000000000..d849531024e --- /dev/null +++ b/core/suggestion-service/test/data/workflow3/workflow_compilation_state.json @@ -0,0 +1,150 @@ +{ + "state": "Failed", + "physicalPlan": null, + "operatorInputSchemaMap": { + "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ], + "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1": [], + "PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c": [ + [ + { + "attributeName": "text", + "attributeType": "string" + } + ] + ], + "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] + ], + "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] + ], + "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ] + }, + "operatorErrors": { + "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865": { + "type": { + "value": 0, + "index": 0, + "name": "COMPILATION_ERROR", + "compilationError": true, + "executionFailure": false, + "unrecognized": false + }, + "timestamp": { + "seconds": 1744853005, + "nanos": 636787000, + "unknownFields": { + "fields": {} + } + }, + "message": "java.lang.RuntimeException: Operator is not configured properly: null", + "details": "Stack trace for developers: \n\njava.lang.RuntimeException: Operator is not configured properly: null\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$9(WorkflowCompiler.scala:149)\nscala.Option.foreach(Option.scala:437)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3(WorkflowCompiler.scala:146)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3$adapted(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$1(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.expandLogicalPlan(WorkflowCompiler.scala:109)\nedu.uci.ics.amber.compiler.WorkflowCompiler.compile(WorkflowCompiler.scala:188)\nedu.uci.ics.texera.service.resource.WorkflowCompilationResource.compileWorkflow(WorkflowCompilationResource.scala:52)\njdk.internal.reflect.GeneratedMethodAccessor44.invoke(Unknown Source)\njava.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\njava.base/java.lang.reflect.Method.invoke(Method.java:566)\norg.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:146)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:189)\norg.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:93)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:478)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:400)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)\norg.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:256)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:248)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:244)\norg.glassfish.jersey.internal.Errors.process(Errors.java:292)\norg.glassfish.jersey.internal.Errors.process(Errors.java:274)\norg.glassfish.jersey.internal.Errors.process(Errors.java:244)\norg.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)\norg.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:235)\norg.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684)\norg.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)\norg.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:358)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:311)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)\norg.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)\norg.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)\nio.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:36)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.handle(AllowedMethodsFilter.java:46)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.doFilter(AllowedMethodsFilter.java:40)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\norg.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)\norg.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)\norg.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)\norg.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)\norg.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)\norg.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\nio.dropwizard.metrics.jetty11.InstrumentedHandler.handle(InstrumentedHandler.java:313)\nio.dropwizard.jetty.RoutingHandler.handle(RoutingHandler.java:52)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:822)\nio.dropwizard.jetty.ZipExceptionHandlingGzipHandler.handle(ZipExceptionHandlingGzipHandler.java:26)\norg.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:46)\norg.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:173)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.Server.handle(Server.java:563)\norg.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)\norg.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)\norg.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)\norg.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)\norg.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)\norg.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)\norg.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)\norg.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)\norg.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)\njava.base/java.lang.Thread.run(Thread.java:829)", + "operatorId": "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865", + "workerId": "" + } + } +} \ No newline at end of file diff --git a/core/suggestion-service/test_interpretation.py b/core/suggestion-service/test_interpretation.py new file mode 100644 index 00000000000..6b9f3303c7c --- /dev/null +++ b/core/suggestion-service/test_interpretation.py @@ -0,0 +1,172 @@ +""" +Test script for workflow interpretation. +""" +import json +from typing import Dict, Any + +from workflow_interpretation.interpreter import WorkflowInterpreter, InterpretationMethod + +# Sample workflow +SAMPLE_WORKFLOW = { + "id": "test-workflow", + "name": "Test Workflow", + "content": { + "operators": [ + { + "operatorID": "source-1", + "operatorType": "ScanSource", + "properties": { + "tableName": "employee", + "limit": 100 + } + }, + { + "operatorID": "filter-1", + "operatorType": "Filter", + "properties": { + "condition": { + "attributeName": "salary", + "comparisonType": "greaterThan", + "value": "50000" + } + } + }, + { + "operatorID": "sink-1", + "operatorType": "ViewResults", + "properties": { + "limit": 10, + "offset": 0 + } + } + ], + "links": [ + { + "linkID": "link-1", + "source": { + "operatorID": "source-1", + "portID": "output-0" + }, + "target": { + "operatorID": "filter-1", + "portID": "input-0" + } + }, + { + "linkID": "link-2", + "source": { + "operatorID": "filter-1", + "portID": "output-0" + }, + "target": { + "operatorID": "sink-1", + "portID": "input-0" + } + } + ] + } +} + +# Sample input schema +SAMPLE_INPUT_SCHEMA = { + "source-1": [], # No input schema for source operators + "filter-1": [ + [ + { + "attributeName": "id", + "attributeType": "integer" + }, + { + "attributeName": "name", + "attributeType": "string" + }, + { + "attributeName": "salary", + "attributeType": "double" + }, + { + "attributeName": "department", + "attributeType": "string" + } + ] + ], + "sink-1": [ + [ + { + "attributeName": "id", + "attributeType": "integer" + }, + { + "attributeName": "name", + "attributeType": "string" + }, + { + "attributeName": "salary", + "attributeType": "double" + }, + { + "attributeName": "department", + "attributeType": "string" + } + ] + ] +} + +# Sample operator errors +SAMPLE_OPERATOR_ERRORS = {} # No errors for this sample + +def test_raw_interpretation(): + """Test the RAW interpretation method.""" + interpreter = WorkflowInterpreter() + + try: + description = interpreter.interpret_workflow( + SAMPLE_WORKFLOW, + SAMPLE_INPUT_SCHEMA, + SAMPLE_OPERATOR_ERRORS, + InterpretationMethod.RAW + ) + + print("\n===== RAW INTERPRETATION =====") + print(description) + print("==============================\n") + + return True + except Exception as e: + print(f"Error testing RAW interpretation: {str(e)}") + return False + +def test_by_path_interpretation(): + """Test the BY_PATH interpretation method.""" + interpreter = WorkflowInterpreter() + + try: + description = interpreter.interpret_workflow( + SAMPLE_WORKFLOW, + SAMPLE_INPUT_SCHEMA, + SAMPLE_OPERATOR_ERRORS, + InterpretationMethod.BY_PATH + ) + + print("\n===== BY_PATH INTERPRETATION =====") + print(description) + print("==================================\n") + + return True + except Exception as e: + print(f"Error testing BY_PATH interpretation: {str(e)}") + return False + +if __name__ == "__main__": + print("Testing workflow interpretation...") + + # Test RAW interpretation + raw_result = test_raw_interpretation() + + # Test BY_PATH interpretation + by_path_result = test_by_path_interpretation() + + if raw_result and by_path_result: + print("All tests passed!") + else: + print("Some tests failed.") \ No newline at end of file diff --git a/core/suggestion-service/workflow_interpretation/__init__.py b/core/suggestion-service/workflow_interpretation/__init__.py new file mode 100644 index 00000000000..6f7ea98d2ec --- /dev/null +++ b/core/suggestion-service/workflow_interpretation/__init__.py @@ -0,0 +1,3 @@ +""" +Workflow Interpretation module for internal use in generating descriptions of workflows for suggestions. +""" \ No newline at end of file diff --git a/core/suggestion-service/workflow_interpretation/interpreter.py b/core/suggestion-service/workflow_interpretation/interpreter.py new file mode 100644 index 00000000000..bf8c0536176 --- /dev/null +++ b/core/suggestion-service/workflow_interpretation/interpreter.py @@ -0,0 +1,269 @@ +""" +Workflow Interpreter module for generating descriptions of workflows. +""" +from typing import Dict, List, Any, Optional, Tuple +import json +from enum import Enum + +from model.texera.TexeraWorkflow import TexeraWorkflow + + +class InterpretationMethod(Enum): + """Enum for different workflow interpretation methods.""" + RAW = "raw" + BY_PATH = "by_path" + + +class WorkflowInterpreter: + """ + WorkflowInterpreter is responsible for interpreting a workflow and its related information + (e.g., schema, errors) into a natural language description that can be used + by the LLM agent for generating suggestions. + """ + + def __init__(self): + """Initialize the interpreter.""" + pass + + def interpret_workflow( + self, + workflow: Dict[str, Any], + input_schema: Optional[Dict[str, Any]] = None, + operator_errors: Optional[Dict[str, Any]] = None, + method: InterpretationMethod = InterpretationMethod.BY_PATH + ) -> str: + """ + Interpret the workflow and generate a natural language description. + + Args: + workflow: The workflow dictionary + input_schema: The input schema dictionary for each operator + operator_errors: Dictionary of static errors for each operator + method: The interpretation method to use + + Returns: + A natural language description of the workflow + """ + if method == InterpretationMethod.RAW: + return self._interpret_raw(workflow, input_schema, operator_errors) + elif method == InterpretationMethod.BY_PATH: + return self._interpret_by_path(workflow, input_schema, operator_errors) + else: + raise ValueError(f"Unsupported interpretation method: {method}") + + def _interpret_raw( + self, + workflow: Dict[str, Any], + input_schema: Optional[Dict[str, Any]] = None, + operator_errors: Optional[Dict[str, Any]] = None + ) -> str: + """ + Generate a raw description of the workflow using a simple template. + + Args: + workflow: The workflow dictionary + input_schema: The input schema dictionary for each operator + operator_errors: Dictionary of static errors for each operator + + Returns: + A raw description of the workflow + """ + description = "Here is the workflow dict:\n" + description += json.dumps(workflow, indent=2) + + if input_schema: + description += "\n\nHere is the input schema for each operator:\n" + description += json.dumps(input_schema, indent=2) + + if operator_errors: + description += "\n\nHere are the static errors for each operator:\n" + description += json.dumps(operator_errors, indent=2) + + return description + + def _interpret_by_path( + self, + workflow: Dict[str, Any], + input_schema: Optional[Dict[str, Any]] = None, + operator_errors: Optional[Dict[str, Any]] = None + ) -> str: + """ + Generate a description of the workflow by extracting and describing paths from source to sink. + + Args: + workflow: The workflow dictionary + input_schema: The input schema dictionary for each operator + operator_errors: Dictionary of static errors for each operator + + Returns: + A description of the workflow organized by execution paths + """ + # Parse the workflow into a TexeraWorkflow object + try: + texera_workflow = TexeraWorkflow() + texera_workflow.initialize_from_dict(workflow) + + # Extract all paths from source to sink + paths = self._extract_paths(texera_workflow) + + # Generate description from paths + description = "Here are the existing paths in this workflow and related schemas:\n\n" + + for i, path in enumerate(paths, 1): + description += f"Path {i}:\n" + description += self._describe_path( + path, texera_workflow, input_schema, operator_errors) + description += "\n\n" + + return description + except Exception as e: + # If path extraction fails, fall back to raw interpretation + print(f"Error in path interpretation: {str(e)}. Falling back to raw interpretation.") + return self._interpret_raw(workflow, input_schema, operator_errors) + + def _extract_paths(self, workflow: TexeraWorkflow) -> List[List[str]]: + """ + Extract all paths from source operators to sink operators. + + Args: + workflow: The TexeraWorkflow object + + Returns: + A list of paths, where each path is a list of operator IDs + """ + # Find source operators (operators with no inputs) + source_operators = [] + for op_id, operator in workflow.operators.items(): + has_input = False + for link in workflow.links: + if link.target.operator_id == op_id: + has_input = True + break + if not has_input: + source_operators.append(op_id) + + # Find sink operators (operators with no outputs) + sink_operators = [] + for op_id, operator in workflow.operators.items(): + has_output = False + for link in workflow.links: + if link.source.operator_id == op_id: + has_output = True + break + if not has_output: + sink_operators.append(op_id) + + # For each source, find all paths to sinks + all_paths = [] + for source in source_operators: + paths = self._find_paths(source, sink_operators, workflow) + all_paths.extend(paths) + + return all_paths + + def _find_paths( + self, + current: str, + sinks: List[str], + workflow: TexeraWorkflow, + path: Optional[List[str]] = None, + visited: Optional[set] = None + ) -> List[List[str]]: + """ + Recursively find all paths from the current operator to any sink operator. + + Args: + current: The current operator ID + sinks: List of sink operator IDs + workflow: The TexeraWorkflow object + path: The current path being built + visited: Set of visited operator IDs to avoid cycles + + Returns: + A list of paths, where each path is a list of operator IDs + """ + if path is None: + path = [] + if visited is None: + visited = set() + + # Add current operator to path and visited + path = path + [current] + visited = visited.union({current}) + + # If current is a sink, we've found a path + if current in sinks: + return [path] + + # Find all next operators + next_operators = [] + for link in workflow.links: + if link.source.operator_id == current: + next_operators.append(link.target.operator_id) + + # Recursively find paths for each next operator + all_paths = [] + for next_op in next_operators: + # Avoid cycles + if next_op not in visited: + new_paths = self._find_paths(next_op, sinks, workflow, path, visited) + all_paths.extend(new_paths) + + return all_paths + + def _describe_path( + self, + path: List[str], + workflow: TexeraWorkflow, + input_schema: Optional[Dict[str, Any]] = None, + operator_errors: Optional[Dict[str, Any]] = None + ) -> str: + """ + Generate a description of a specific path in the workflow. + + Args: + path: List of operator IDs representing a path + workflow: The TexeraWorkflow object + input_schema: The input schema dictionary for each operator + operator_errors: Dictionary of static errors for each operator + + Returns: + A description of the path + """ + description = "" + + # Describe each operator in the path + for i, op_id in enumerate(path): + operator = workflow.operators[op_id] + description += f" {i+1}. {operator.operator_type}" + + # Add operator properties if available + if operator.properties: + description += " with properties:\n" + for prop_key, prop_value in operator.properties.items(): + description += f" - {prop_key}: {prop_value}\n" + else: + description += "\n" + + # Add schema information if available + if input_schema and op_id in input_schema: + schema_info = input_schema[op_id] + description += " Schema:\n" + description += f" {json.dumps(schema_info, indent=6)}\n" + + # Add error information if available + if operator_errors and op_id in operator_errors: + error_info = operator_errors[op_id] + description += " Errors:\n" + description += f" {json.dumps(error_info, indent=6)}\n" + + # Add connector information if not the last operator + if i < len(path) - 1: + # Find the link between this operator and the next + next_op_id = path[i + 1] + for link in workflow.links: + if link.source.operator_id == op_id and link.target.operator_id == next_op_id: + description += f" → Connected to {next_op_id} via ports {link.source.port_id} → {link.target.port_id}\n" + break + + return description \ No newline at end of file From 2a96b794729b3cc9e735c649aa94ea47bdf5dd7a Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 17 Apr 2025 16:05:34 -0700 Subject: [PATCH 016/104] fix Texera workflow --- .../model/texera/TexeraOperator.py | 12 +- .../model/texera/TexeraWorkflow.py | 186 +++++-- .../workflow1/by_path_interpretation.txt | 77 +++ .../results/workflow1/raw_interpretation.txt | 224 +++++++++ .../test/results/workflow1/suggestions.json | 112 +++++ .../workflow2/by_path_interpretation.txt | 166 +++++++ .../results/workflow2/raw_interpretation.txt | 373 ++++++++++++++ .../test/results/workflow2/suggestions.json | 112 +++++ .../workflow3/by_path_interpretation.txt | 213 ++++++++ .../results/workflow3/raw_interpretation.txt | 464 ++++++++++++++++++ .../test/results/workflow3/suggestions.json | 38 ++ .../suggestion-service/test_with_real_data.py | 177 +++++++ .../workflow_interpretation/interpreter.py | 5 +- 13 files changed, 2106 insertions(+), 53 deletions(-) create mode 100644 core/suggestion-service/test/results/workflow1/by_path_interpretation.txt create mode 100644 core/suggestion-service/test/results/workflow1/raw_interpretation.txt create mode 100644 core/suggestion-service/test/results/workflow1/suggestions.json create mode 100644 core/suggestion-service/test/results/workflow2/by_path_interpretation.txt create mode 100644 core/suggestion-service/test/results/workflow2/raw_interpretation.txt create mode 100644 core/suggestion-service/test/results/workflow2/suggestions.json create mode 100644 core/suggestion-service/test/results/workflow3/by_path_interpretation.txt create mode 100644 core/suggestion-service/test/results/workflow3/raw_interpretation.txt create mode 100644 core/suggestion-service/test/results/workflow3/suggestions.json create mode 100644 core/suggestion-service/test_with_real_data.py diff --git a/core/suggestion-service/model/texera/TexeraOperator.py b/core/suggestion-service/model/texera/TexeraOperator.py index a20423fdb28..fe1ddac0fe9 100644 --- a/core/suggestion-service/model/texera/TexeraOperator.py +++ b/core/suggestion-service/model/texera/TexeraOperator.py @@ -91,4 +91,14 @@ def __str__(self) -> str: f" OutputPorts=[\n {output_ports_str}\n ],\n" f" Error={self.error}\n" f")" - ) \ No newline at end of file + ) + + @property + def properties(self) -> Dict: + """ + Get the operator properties. + + Returns: + The operator properties dictionary + """ + return self.operator_properties \ No newline at end of file diff --git a/core/suggestion-service/model/texera/TexeraWorkflow.py b/core/suggestion-service/model/texera/TexeraWorkflow.py index bcf02897c07..cd174c7a827 100644 --- a/core/suggestion-service/model/texera/TexeraWorkflow.py +++ b/core/suggestion-service/model/texera/TexeraWorkflow.py @@ -1,6 +1,6 @@ import json -from typing import List, Dict, Tuple +from typing import List, Dict, Tuple, Any import networkx as nx from model.Operator import Operator @@ -14,7 +14,8 @@ class TexeraWorkflow(Workflow): def __init__( self, - workflow_content: str, + workflow_content: str = "", + workflow_dict: Dict[str, Any] = None, operator_id_to_port_indexed_input_schemas_mapping: Dict[str, List['DataSchema']]=None, operator_id_to_error_mapping: Dict[str, str]=None, wid: int = 0, @@ -25,53 +26,67 @@ def __init__( operator_id_to_port_indexed_input_schemas_mapping = {} if operator_id_to_error_mapping is None: operator_id_to_error_mapping = {} - - self.workflow_content = workflow_content - self.workflow_dict = json.loads(workflow_content) - operators_dict = self.workflow_dict.get("operators", {}) - links_dict = self.workflow_dict.get("links", {}) - - self.DAG = nx.DiGraph() - self.workflow_title = workflow_title - self.operators: Dict[str, 'TexeraOperator'] = { - op_dict['operatorID']: - TexeraOperator( - operator_dict = op_dict, - port_indexed_input_schemas=operator_id_to_port_indexed_input_schemas_mapping.get(op_dict['operatorID'], []), - error=operator_id_to_error_mapping.get(op_dict['operatorID'], "") - ) for op_dict in operators_dict - } - # self.links = [TexeraLink(link_dict, self.operators) for link_dict in links_dict] + self.wid = wid - - # start to build the DAG - for operator in self.GetOperators(): - self.DAG.add_node(operator.GetId(), - type=operator.GetType(), - inputPorts=[port.GetId() for port in operator.GetInputPorts()], - outputPorts=[port.GetId() for port in operator.GetOutputPorts()], - error=operator.GetError() - ) - - # then start to add link - for link in links_dict: - source_op_id = link.get('source').get('operatorID') - src_port_id = link.get('source').get('portID') - target_op_id = link.get('target').get('operatorID') - target_port_id = link.get('target').get('portID') - - op = self.operators.get(target_op_id) - if op is not None: - schema = op.GetInputSchemaByPortID(target_port_id) - else: - schema = None - self.DAG.add_edge(source_op_id, - target_op_id, - srcPort=src_port_id, - targetPort=target_port_id, - schema=schema - ) - + self.workflow_title = workflow_title + self.DAG = nx.DiGraph() + self.operators = {} + self.links = [] + + # Handle both string content and direct dict input + if workflow_dict is not None: + self.workflow_dict = workflow_dict + self.workflow_content = json.dumps(workflow_dict) + self.initialize_from_dict(workflow_dict) + elif workflow_content: + self.workflow_content = workflow_content + self.workflow_dict = json.loads(workflow_content) + operators_dict = self.workflow_dict.get("operators", {}) + links_dict = self.workflow_dict.get("links", {}) + + self.operators: Dict[str, 'TexeraOperator'] = { + op_dict['operatorID']: + TexeraOperator( + operator_dict = op_dict, + port_indexed_input_schemas=operator_id_to_port_indexed_input_schemas_mapping.get(op_dict['operatorID'], []), + error=operator_id_to_error_mapping.get(op_dict['operatorID'], "") + ) for op_dict in operators_dict + } + + # Build the DAG + self._build_dag() + + def initialize_from_dict(self, workflow_dict: Dict[str, Any]) -> None: + """ + Initialize the workflow from a dictionary (needed by the interpreter). + + Args: + workflow_dict: Dictionary representation of the workflow + """ + # Convert the dictionary to a string for internal storage + self.workflow_content = json.dumps(workflow_dict) + self.workflow_dict = workflow_dict + + # Extract operators and links + content = workflow_dict.get("content", {}) + operators_dict = content.get("operators", []) + + # Reset internal state + self.operators = {} + self.links = [] + + # Initialize operators + for op_dict in operators_dict: + op_id = op_dict.get("operatorID") + if op_id: + self.operators[op_id] = TexeraOperator( + operator_dict=op_dict, + port_indexed_input_schemas=[], + error="" + ) + + # Build the DAG structure (adds links and completes initialization) + self._build_dag() def GetWorkflowContent(self) -> str: return self.workflow_content @@ -162,4 +177,77 @@ def __str__(self) -> str: f" Operators=[\n{operators_str}\n ],\n" f" DAG Edges=[\n {edges_str}\n ]\n" f")" - ) \ No newline at end of file + ) + + def VisualizeDAG(self) -> str: + """ + Generate a visualization of the workflow DAG. + This is a placeholder implementation required for the abstract class. + + Returns: + A string representation of the workflow DAG visualization. + """ + # Create a simple text-based visualization of the DAG + visualization = "Workflow DAG Visualization:\n" + + # Add operator nodes + visualization += "\nOperators:\n" + for op_id, operator in self.operators.items(): + visualization += f" - {op_id} ({operator.GetType()})\n" + + # Add links (edges) + visualization += "\nLinks:\n" + for link in self.links: + source_op = link.source.operator_id + source_port = link.source.port_id + target_op = link.target.operator_id + target_port = link.target.port_id + visualization += f" - {source_op}:{source_port} → {target_op}:{target_port}\n" + + return visualization + + def _build_dag(self) -> None: + """Build the DAG from the workflow dictionary's operators and links.""" + # Clear existing DAG + self.DAG = nx.DiGraph() + + # Extract operators and links + operators_dict = self.workflow_dict.get("content", {}).get("operators", []) + links_dict = self.workflow_dict.get("content", {}).get("links", []) + + # Add nodes to DAG + for operator in self.GetOperators(): + self.DAG.add_node(operator.GetId(), + type=operator.GetType(), + inputPorts=[port.GetId() for port in operator.GetInputPorts()], + outputPorts=[port.GetId() for port in operator.GetOutputPorts()], + error=operator.GetError() + ) + + # Add links to DAG + for link in links_dict: + source_op_id = link.get('source', {}).get('operatorID') + src_port_id = link.get('source', {}).get('portID') + target_op_id = link.get('target', {}).get('operatorID') + target_port_id = link.get('target', {}).get('portID') + + if source_op_id and target_op_id: + # Add link to links list + class Link: + def __init__(self, src_id, src_port, target_id, target_port): + self.source = type('obj', (object,), {'operator_id': src_id, 'port_id': src_port}) + self.target = type('obj', (object,), {'operator_id': target_id, 'port_id': target_port}) + + self.links.append(Link(source_op_id, src_port_id, target_op_id, target_port_id)) + + # Add edge to DAG + op = self.operators.get(target_op_id) + schema = None + if op is not None: + schema = op.GetInputSchemaByPortID(target_port_id) + + self.DAG.add_edge(source_op_id, + target_op_id, + srcPort=src_port_id, + targetPort=target_port_id, + schema=schema) \ No newline at end of file diff --git a/core/suggestion-service/test/results/workflow1/by_path_interpretation.txt b/core/suggestion-service/test/results/workflow1/by_path_interpretation.txt new file mode 100644 index 00000000000..087b9193eb7 --- /dev/null +++ b/core/suggestion-service/test/results/workflow1/by_path_interpretation.txt @@ -0,0 +1,77 @@ +Here are the existing paths in this workflow and related schemas: + +Path 1: + 1. CSVFileScan with properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Schema: + [] + → Connected to Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894 via ports output-0 → input-0 + 2. Projection with properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'tweet_id'}, {'alias': '', 'originalAttribute': 'create_at_month'}, {'alias': '', 'originalAttribute': 'favorite_count'}, {'alias': '', 'originalAttribute': 'retweet_count'}] + Schema: + [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] +] + → Connected to Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3 via ports output-0 → input-0 + 3. Sort with properties: + - attributes: [{'attribute': 'favorite_count', 'sortPreference': 'DESC'}] + Schema: + [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] +] + + diff --git a/core/suggestion-service/test/results/workflow1/raw_interpretation.txt b/core/suggestion-service/test/results/workflow1/raw_interpretation.txt new file mode 100644 index 00000000000..d3ec4b06293 --- /dev/null +++ b/core/suggestion-service/test/results/workflow1/raw_interpretation.txt @@ -0,0 +1,224 @@ +Here is the workflow dict: +{ + "wid": 2748, + "name": "Suggestion Test: 1-path-compilation-succeed", + "description": null, + "content": { + "operators": [ + { + "operatorID": "CSVFileScan-operator-02af511d-2912-4d48-a86e-8ac73a6e2b0e", + "operatorType": "CSVFileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "fileEncoding": "UTF_8", + "customDelimiter": ",", + "hasHeader": true, + "fileName": "/workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv" + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "CSV File Scan", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "tweet_id" + }, + { + "alias": "", + "originalAttribute": "create_at_month" + }, + { + "alias": "", + "originalAttribute": "favorite_count" + }, + { + "alias": "", + "originalAttribute": "retweet_count" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3", + "operatorType": "Sort", + "operatorVersion": "N/A", + "operatorProperties": { + "attributes": [ + { + "attribute": "favorite_count", + "sortPreference": "DESC" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sort", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + } + ], + "operatorPositions": { + "CSVFileScan-operator-02af511d-2912-4d48-a86e-8ac73a6e2b0e": { + "x": 472, + "y": 396 + }, + "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894": { + "x": 657, + "y": 395 + }, + "Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3": { + "x": 825, + "y": 396 + } + }, + "links": [ + { + "linkID": "link-4a444193-d4a7-4a2d-ac53-3425bc5bfda0", + "source": { + "operatorID": "CSVFileScan-operator-02af511d-2912-4d48-a86e-8ac73a6e2b0e", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894", + "portID": "input-0" + } + }, + { + "linkID": "link-1586b353-1954-4064-a38a-3c9539306c07", + "source": { + "operatorID": "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894", + "portID": "output-0" + }, + "target": { + "operatorID": "Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3", + "portID": "input-0" + } + } + ], + "commentBoxes": [], + "settings": { + "dataTransferBatchSize": 400 + } + }, + "creationTime": 1744844136205, + "lastModifiedTime": 1744844136205, + "isPublic": false +} + +Here is the input schema for each operator: +{ + "Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ], + "Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] + ], + "CSVFileScan-operator-02af511d-2912-4d48-a86e-8ac73a6e2b0e": [] +} \ No newline at end of file diff --git a/core/suggestion-service/test/results/workflow1/suggestions.json b/core/suggestion-service/test/results/workflow1/suggestions.json new file mode 100644 index 00000000000..5abf9b84388 --- /dev/null +++ b/core/suggestion-service/test/results/workflow1/suggestions.json @@ -0,0 +1,112 @@ +[ + { + "id": "suggestion-b74b4daf-454f-4303-8fdf-b6d2dc4da305", + "description": "Add a KeywordSearch operator with sentiment analysis", + "operatorsToAdd": [ + { + "operatorType": "KeywordSearch", + "position": { + "x": 400, + "y": 300 + }, + "properties": { + "keyword": "climate change", + "attributes": [ + "content", + "title" + ] + } + }, + { + "operatorType": "SentimentAnalysis", + "position": { + "x": 600, + "y": 300 + }, + "properties": { + "attribute": "content", + "resultAttribute": "sentiment" + } + } + ], + "operatorPropertiesToChange": [ + { + "operatorId": "View-Results-1", + "properties": { + "limit": 20, + "offset": 0 + } + } + ], + "operatorsToDelete": [], + "linksToAdd": [ + { + "source": { + "operatorId": "Source-Scan-1", + "portId": "output-0" + }, + "target": { + "operatorId": "KeywordSearch-1", + "portId": "input-0" + } + }, + { + "source": { + "operatorId": "KeywordSearch-1", + "portId": "output-0" + }, + "target": { + "operatorId": "SentimentAnalysis-1", + "portId": "input-0" + } + }, + { + "source": { + "operatorId": "SentimentAnalysis-1", + "portId": "output-0" + }, + "target": { + "operatorId": "View-Results-1", + "portId": "input-0" + } + } + ], + "isPreviewActive": false + }, + { + "id": "suggestion-67ab18f9-7442-4556-801c-580afd2a0415", + "description": "Replace ScanSource with CSVFileScan for better performance", + "operatorsToAdd": [ + { + "operatorType": "CSVFileScan", + "position": { + "x": 200, + "y": 200 + }, + "properties": { + "fileName": "data.csv", + "limit": -1, + "offset": 0, + "schema": "auto" + } + } + ], + "operatorPropertiesToChange": [], + "operatorsToDelete": [ + "Source-Scan-1" + ], + "linksToAdd": [ + { + "source": { + "operatorId": "CSVFileScan-1", + "portId": "output-0" + }, + "target": { + "operatorId": "View-Results-1", + "portId": "input-0" + } + } + ], + "isPreviewActive": false + } +] \ No newline at end of file diff --git a/core/suggestion-service/test/results/workflow2/by_path_interpretation.txt b/core/suggestion-service/test/results/workflow2/by_path_interpretation.txt new file mode 100644 index 00000000000..80b5418843d --- /dev/null +++ b/core/suggestion-service/test/results/workflow2/by_path_interpretation.txt @@ -0,0 +1,166 @@ +Here are the existing paths in this workflow and related schemas: + +Path 1: + 1. CSVFileScan with properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Schema: + [] + → Connected to Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f via ports output-0 → input-0 + 2. Projection with properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'tweet_id'}, {'alias': '', 'originalAttribute': 'create_at_month'}, {'alias': '', 'originalAttribute': 'favorite_count'}, {'alias': '', 'originalAttribute': 'retweet_count'}] + Schema: + [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] +] + → Connected to Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260 via ports output-0 → input-0 + 3. Sort with properties: + - attributes: [{'attribute': 'favorite_count', 'sortPreference': 'DESC'}] + Schema: + [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] +] + + +Path 2: + 1. CSVFileScan with properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Schema: + [] + → Connected to Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac via ports output-0 → input-0 + 2. Projection with properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'text'}] + Schema: + [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] +] + → Connected to PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289 via ports output-0 → input-0 + 3. PythonUDFV2 with properties: + - code: # Choose from the following templates: +# +from pytexera import * + +class ProcessTupleOperator(UDFOperatorV2): + + @overrides + def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]: + tuple_['text_length'] = len(tuple_['text']) + yield tuple_ + +# class ProcessBatchOperator(UDFBatchOperator): +# BATCH_SIZE = 10 # must be a positive integer +# +# @overrides +# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]: +# yield batch +# +# class ProcessTableOperator(UDFTableOperator): +# +# @overrides +# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]: +# yield table + + - workers: 1 + - retainInputColumns: True + - outputColumns: [{'attributeName': 'text_length', 'attributeType': 'integer'}] + Schema: + [ + [ + { + "attributeName": "text", + "attributeType": "string" + } + ] +] + + diff --git a/core/suggestion-service/test/results/workflow2/raw_interpretation.txt b/core/suggestion-service/test/results/workflow2/raw_interpretation.txt new file mode 100644 index 00000000000..823cf27d418 --- /dev/null +++ b/core/suggestion-service/test/results/workflow2/raw_interpretation.txt @@ -0,0 +1,373 @@ +Here is the workflow dict: +{ + "name": "Suggestion Test: 2-path-compilation-succeed", + "description": null, + "wid": 2749, + "content": { + "operators": [ + { + "operatorID": "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef", + "operatorType": "CSVFileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "fileEncoding": "UTF_8", + "customDelimiter": ",", + "hasHeader": true, + "fileName": "/workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv" + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "CSV File Scan", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "tweet_id" + }, + { + "alias": "", + "originalAttribute": "create_at_month" + }, + { + "alias": "", + "originalAttribute": "favorite_count" + }, + { + "alias": "", + "originalAttribute": "retweet_count" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260", + "operatorType": "Sort", + "operatorVersion": "N/A", + "operatorProperties": { + "attributes": [ + { + "attribute": "favorite_count", + "sortPreference": "DESC" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sort", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "text" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289", + "operatorType": "PythonUDFV2", + "operatorVersion": "N/A", + "operatorProperties": { + "code": "# Choose from the following templates:\n# \nfrom pytexera import *\n\nclass ProcessTupleOperator(UDFOperatorV2):\n \n @overrides\n def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:\n tuple_['text_length'] = len(tuple_['text'])\n yield tuple_\n\n# class ProcessBatchOperator(UDFBatchOperator):\n# BATCH_SIZE = 10 # must be a positive integer\n# \n# @overrides\n# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]:\n# yield batch\n# \n# class ProcessTableOperator(UDFTableOperator):\n# \n# @overrides\n# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:\n# yield table\n", + "workers": 1, + "retainInputColumns": true, + "outputColumns": [ + { + "attributeName": "text_length", + "attributeType": "integer" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Python UDF", + "dynamicInputPorts": true, + "dynamicOutputPorts": true + } + ], + "operatorPositions": { + "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef": { + "x": 472, + "y": 396 + }, + "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f": { + "x": 657, + "y": 363 + }, + "Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260": { + "x": 816, + "y": 362 + }, + "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac": { + "x": 661, + "y": 501 + }, + "PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289": { + "x": 820, + "y": 497 + } + }, + "links": [ + { + "linkID": "link-4a444193-d4a7-4a2d-ac53-3425bc5bfda0", + "source": { + "operatorID": "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f", + "portID": "input-0" + } + }, + { + "linkID": "link-1586b353-1954-4064-a38a-3c9539306c07", + "source": { + "operatorID": "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f", + "portID": "output-0" + }, + "target": { + "operatorID": "Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260", + "portID": "input-0" + } + }, + { + "linkID": "63989c89-c9e7-470c-b107-bc6d154b2ed0", + "source": { + "operatorID": "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac", + "portID": "input-0" + } + }, + { + "linkID": "710b40d5-3142-4970-ae65-3ee210690f6f", + "source": { + "operatorID": "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac", + "portID": "output-0" + }, + "target": { + "operatorID": "PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289", + "portID": "input-0" + } + } + ], + "commentBoxes": [], + "settings": { + "dataTransferBatchSize": 400 + } + }, + "creationTime": 1744845032285, + "lastModifiedTime": 1744845032285, + "isPublished": false, + "readonly": false +} + +Here is the input schema for each operator: +{ + "CSVFileScan-operator-aa00f100-e3fa-431c-9f2c-a03c845d01ef": [], + "Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ], + "PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289": [ + [ + { + "attributeName": "text", + "attributeType": "string" + } + ] + ], + "Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] + ], + "Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ] +} \ No newline at end of file diff --git a/core/suggestion-service/test/results/workflow2/suggestions.json b/core/suggestion-service/test/results/workflow2/suggestions.json new file mode 100644 index 00000000000..d302a1fc9de --- /dev/null +++ b/core/suggestion-service/test/results/workflow2/suggestions.json @@ -0,0 +1,112 @@ +[ + { + "id": "suggestion-eb0a5d8f-505b-4e5c-86c5-172bd68415c2", + "description": "Add a KeywordSearch operator with sentiment analysis", + "operatorsToAdd": [ + { + "operatorType": "KeywordSearch", + "position": { + "x": 400, + "y": 300 + }, + "properties": { + "keyword": "climate change", + "attributes": [ + "content", + "title" + ] + } + }, + { + "operatorType": "SentimentAnalysis", + "position": { + "x": 600, + "y": 300 + }, + "properties": { + "attribute": "content", + "resultAttribute": "sentiment" + } + } + ], + "operatorPropertiesToChange": [ + { + "operatorId": "View-Results-1", + "properties": { + "limit": 20, + "offset": 0 + } + } + ], + "operatorsToDelete": [], + "linksToAdd": [ + { + "source": { + "operatorId": "Source-Scan-1", + "portId": "output-0" + }, + "target": { + "operatorId": "KeywordSearch-1", + "portId": "input-0" + } + }, + { + "source": { + "operatorId": "KeywordSearch-1", + "portId": "output-0" + }, + "target": { + "operatorId": "SentimentAnalysis-1", + "portId": "input-0" + } + }, + { + "source": { + "operatorId": "SentimentAnalysis-1", + "portId": "output-0" + }, + "target": { + "operatorId": "View-Results-1", + "portId": "input-0" + } + } + ], + "isPreviewActive": false + }, + { + "id": "suggestion-c6293a99-27fa-44cf-8726-fb90ca13cc82", + "description": "Replace ScanSource with CSVFileScan for better performance", + "operatorsToAdd": [ + { + "operatorType": "CSVFileScan", + "position": { + "x": 200, + "y": 200 + }, + "properties": { + "fileName": "data.csv", + "limit": -1, + "offset": 0, + "schema": "auto" + } + } + ], + "operatorPropertiesToChange": [], + "operatorsToDelete": [ + "Source-Scan-1" + ], + "linksToAdd": [ + { + "source": { + "operatorId": "CSVFileScan-1", + "portId": "output-0" + }, + "target": { + "operatorId": "View-Results-1", + "portId": "input-0" + } + } + ], + "isPreviewActive": false + } +] \ No newline at end of file diff --git a/core/suggestion-service/test/results/workflow3/by_path_interpretation.txt b/core/suggestion-service/test/results/workflow3/by_path_interpretation.txt new file mode 100644 index 00000000000..558f7d4a876 --- /dev/null +++ b/core/suggestion-service/test/results/workflow3/by_path_interpretation.txt @@ -0,0 +1,213 @@ +Here are the existing paths in this workflow and related schemas: + +Path 1: + 1. CSVFileScan with properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Schema: + [] + → Connected to Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c via ports output-0 → input-0 + 2. Projection with properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'tweet_id'}, {'alias': '', 'originalAttribute': 'create_at_month'}, {'alias': '', 'originalAttribute': 'favorite_count'}, {'alias': '', 'originalAttribute': 'retweet_count'}] + Schema: + [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] +] + → Connected to Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2 via ports output-0 → input-0 + 3. Sort with properties: + - attributes: [{'attribute': 'favorite_count', 'sortPreference': 'DESC'}] + Schema: + [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] +] + → Connected to LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865 via ports output-0 → input-0 + 4. LineChart with properties: + - yLabel: Y Axis + - xLabel: X Axis + Schema: + [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] +] + Errors: + { + "type": { + "value": 0, + "index": 0, + "name": "COMPILATION_ERROR", + "compilationError": true, + "executionFailure": false, + "unrecognized": false + }, + "timestamp": { + "seconds": 1744853005, + "nanos": 636787000, + "unknownFields": { + "fields": {} + } + }, + "message": "java.lang.RuntimeException: Operator is not configured properly: null", + "details": "Stack trace for developers: \n\njava.lang.RuntimeException: Operator is not configured properly: null\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$9(WorkflowCompiler.scala:149)\nscala.Option.foreach(Option.scala:437)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3(WorkflowCompiler.scala:146)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3$adapted(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$1(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.expandLogicalPlan(WorkflowCompiler.scala:109)\nedu.uci.ics.amber.compiler.WorkflowCompiler.compile(WorkflowCompiler.scala:188)\nedu.uci.ics.texera.service.resource.WorkflowCompilationResource.compileWorkflow(WorkflowCompilationResource.scala:52)\njdk.internal.reflect.GeneratedMethodAccessor44.invoke(Unknown Source)\njava.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\njava.base/java.lang.reflect.Method.invoke(Method.java:566)\norg.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:146)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:189)\norg.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:93)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:478)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:400)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)\norg.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:256)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:248)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:244)\norg.glassfish.jersey.internal.Errors.process(Errors.java:292)\norg.glassfish.jersey.internal.Errors.process(Errors.java:274)\norg.glassfish.jersey.internal.Errors.process(Errors.java:244)\norg.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)\norg.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:235)\norg.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684)\norg.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)\norg.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:358)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:311)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)\norg.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)\norg.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)\nio.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:36)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.handle(AllowedMethodsFilter.java:46)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.doFilter(AllowedMethodsFilter.java:40)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\norg.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)\norg.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)\norg.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)\norg.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)\norg.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)\norg.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\nio.dropwizard.metrics.jetty11.InstrumentedHandler.handle(InstrumentedHandler.java:313)\nio.dropwizard.jetty.RoutingHandler.handle(RoutingHandler.java:52)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:822)\nio.dropwizard.jetty.ZipExceptionHandlingGzipHandler.handle(ZipExceptionHandlingGzipHandler.java:26)\norg.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:46)\norg.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:173)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.Server.handle(Server.java:563)\norg.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)\norg.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)\norg.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)\norg.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)\norg.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)\norg.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)\norg.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)\norg.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)\norg.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)\njava.base/java.lang.Thread.run(Thread.java:829)", + "operatorId": "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865", + "workerId": "" +} + + +Path 2: + 1. CSVFileScan with properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Schema: + [] + → Connected to Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16 via ports output-0 → input-0 + 2. Projection with properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'text'}] + Schema: + [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] +] + → Connected to PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c via ports output-0 → input-0 + 3. PythonUDFV2 with properties: + - code: # Choose from the following templates: +# +from pytexera import * + +class ProcessTupleOperator(UDFOperatorV2): + + @overrides + def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]: + tuple_['text_length'] = len(tuple_['text']) + yield tuple_ + +# class ProcessBatchOperator(UDFBatchOperator): +# BATCH_SIZE = 10 # must be a positive integer +# +# @overrides +# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]: +# yield batch +# +# class ProcessTableOperator(UDFTableOperator): +# +# @overrides +# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]: +# yield table + + - workers: 1 + - retainInputColumns: True + - outputColumns: [{'attributeName': 'text_length', 'attributeType': 'integer'}] + Schema: + [ + [ + { + "attributeName": "text", + "attributeType": "string" + } + ] +] + + diff --git a/core/suggestion-service/test/results/workflow3/raw_interpretation.txt b/core/suggestion-service/test/results/workflow3/raw_interpretation.txt new file mode 100644 index 00000000000..c283095a83d --- /dev/null +++ b/core/suggestion-service/test/results/workflow3/raw_interpretation.txt @@ -0,0 +1,464 @@ +Here is the workflow dict: +{ + "name": "Suggestion Test: 2-path-compilation-failed", + "description": null, + "wid": 2750, + "content": { + "operators": [ + { + "operatorID": "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1", + "operatorType": "CSVFileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "fileEncoding": "UTF_8", + "customDelimiter": ",", + "hasHeader": true, + "fileName": "/workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv" + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "CSV File Scan", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "tweet_id" + }, + { + "alias": "", + "originalAttribute": "create_at_month" + }, + { + "alias": "", + "originalAttribute": "favorite_count" + }, + { + "alias": "", + "originalAttribute": "retweet_count" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2", + "operatorType": "Sort", + "operatorVersion": "N/A", + "operatorProperties": { + "attributes": [ + { + "attribute": "favorite_count", + "sortPreference": "DESC" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sort", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "isDrop": false, + "attributes": [ + { + "alias": "", + "originalAttribute": "text" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + }, + { + "operatorID": "PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c", + "operatorType": "PythonUDFV2", + "operatorVersion": "N/A", + "operatorProperties": { + "code": "# Choose from the following templates:\n# \nfrom pytexera import *\n\nclass ProcessTupleOperator(UDFOperatorV2):\n \n @overrides\n def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:\n tuple_['text_length'] = len(tuple_['text'])\n yield tuple_\n\n# class ProcessBatchOperator(UDFBatchOperator):\n# BATCH_SIZE = 10 # must be a positive integer\n# \n# @overrides\n# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]:\n# yield batch\n# \n# class ProcessTableOperator(UDFTableOperator):\n# \n# @overrides\n# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:\n# yield table\n", + "workers": 1, + "retainInputColumns": true, + "outputColumns": [ + { + "attributeName": "text_length", + "attributeType": "integer" + } + ] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Python UDF", + "dynamicInputPorts": true, + "dynamicOutputPorts": true + }, + { + "operatorID": "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865", + "operatorType": "LineChart", + "operatorVersion": "N/A", + "operatorProperties": { + "yLabel": "Y Axis", + "xLabel": "X Axis" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Line Chart", + "dynamicInputPorts": false, + "dynamicOutputPorts": false + } + ], + "operatorPositions": { + "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1": { + "x": 472, + "y": 396 + }, + "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c": { + "x": 657, + "y": 363 + }, + "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2": { + "x": 816, + "y": 362 + }, + "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16": { + "x": 661, + "y": 501 + }, + "PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c": { + "x": 820, + "y": 497 + }, + "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865": { + "x": 966, + "y": 357 + } + }, + "links": [ + { + "linkID": "link-4a444193-d4a7-4a2d-ac53-3425bc5bfda0", + "source": { + "operatorID": "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c", + "portID": "input-0" + } + }, + { + "linkID": "link-1586b353-1954-4064-a38a-3c9539306c07", + "source": { + "operatorID": "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c", + "portID": "output-0" + }, + "target": { + "operatorID": "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2", + "portID": "input-0" + } + }, + { + "linkID": "63989c89-c9e7-470c-b107-bc6d154b2ed0", + "source": { + "operatorID": "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1", + "portID": "output-0" + }, + "target": { + "operatorID": "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16", + "portID": "input-0" + } + }, + { + "linkID": "710b40d5-3142-4970-ae65-3ee210690f6f", + "source": { + "operatorID": "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16", + "portID": "output-0" + }, + "target": { + "operatorID": "PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c", + "portID": "input-0" + } + }, + { + "linkID": "link-2883d565-72d0-4d01-a210-fead4ad076a3", + "source": { + "operatorID": "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2", + "portID": "output-0" + }, + "target": { + "operatorID": "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865", + "portID": "input-0" + } + } + ], + "commentBoxes": [], + "settings": { + "dataTransferBatchSize": 400 + } + }, + "creationTime": 1744845299552, + "lastModifiedTime": 1744845299552, + "isPublished": false, + "readonly": false +} + +Here is the input schema for each operator: +{ + "Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ], + "CSVFileScan-operator-7249c7e8-6d05-4572-931e-495d47f7ccf1": [], + "PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c": [ + [ + { + "attributeName": "text", + "attributeType": "string" + } + ] + ], + "Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] + ], + "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + } + ] + ], + "Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16": [ + [ + { + "attributeName": "tweet_id", + "attributeType": "integer" + }, + { + "attributeName": "create_at_month", + "attributeType": "timestamp" + }, + { + "attributeName": "favorite_count", + "attributeType": "integer" + }, + { + "attributeName": "retweet_count", + "attributeType": "integer" + }, + { + "attributeName": "is_retweet", + "attributeType": "boolean" + }, + { + "attributeName": "lang", + "attributeType": "string" + }, + { + "attributeName": "text", + "attributeType": "string" + }, + { + "attributeName": "user_id", + "attributeType": "integer" + } + ] + ] +} + +Here are the static errors for each operator: +{ + "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865": { + "type": { + "value": 0, + "index": 0, + "name": "COMPILATION_ERROR", + "compilationError": true, + "executionFailure": false, + "unrecognized": false + }, + "timestamp": { + "seconds": 1744853005, + "nanos": 636787000, + "unknownFields": { + "fields": {} + } + }, + "message": "java.lang.RuntimeException: Operator is not configured properly: null", + "details": "Stack trace for developers: \n\njava.lang.RuntimeException: Operator is not configured properly: null\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$9(WorkflowCompiler.scala:149)\nscala.Option.foreach(Option.scala:437)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3(WorkflowCompiler.scala:146)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3$adapted(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$1(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.expandLogicalPlan(WorkflowCompiler.scala:109)\nedu.uci.ics.amber.compiler.WorkflowCompiler.compile(WorkflowCompiler.scala:188)\nedu.uci.ics.texera.service.resource.WorkflowCompilationResource.compileWorkflow(WorkflowCompilationResource.scala:52)\njdk.internal.reflect.GeneratedMethodAccessor44.invoke(Unknown Source)\njava.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\njava.base/java.lang.reflect.Method.invoke(Method.java:566)\norg.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:146)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:189)\norg.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:93)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:478)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:400)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)\norg.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:256)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:248)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:244)\norg.glassfish.jersey.internal.Errors.process(Errors.java:292)\norg.glassfish.jersey.internal.Errors.process(Errors.java:274)\norg.glassfish.jersey.internal.Errors.process(Errors.java:244)\norg.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)\norg.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:235)\norg.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684)\norg.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)\norg.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:358)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:311)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)\norg.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)\norg.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)\nio.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:36)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.handle(AllowedMethodsFilter.java:46)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.doFilter(AllowedMethodsFilter.java:40)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\norg.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)\norg.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)\norg.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)\norg.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)\norg.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)\norg.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\nio.dropwizard.metrics.jetty11.InstrumentedHandler.handle(InstrumentedHandler.java:313)\nio.dropwizard.jetty.RoutingHandler.handle(RoutingHandler.java:52)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:822)\nio.dropwizard.jetty.ZipExceptionHandlingGzipHandler.handle(ZipExceptionHandlingGzipHandler.java:26)\norg.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:46)\norg.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:173)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.Server.handle(Server.java:563)\norg.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)\norg.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)\norg.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)\norg.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)\norg.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)\norg.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)\norg.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)\norg.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)\norg.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)\njava.base/java.lang.Thread.run(Thread.java:829)", + "operatorId": "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865", + "workerId": "" + } +} \ No newline at end of file diff --git a/core/suggestion-service/test/results/workflow3/suggestions.json b/core/suggestion-service/test/results/workflow3/suggestions.json new file mode 100644 index 00000000000..39d4ed0b5ce --- /dev/null +++ b/core/suggestion-service/test/results/workflow3/suggestions.json @@ -0,0 +1,38 @@ +[ + { + "id": "suggestion-0bf71358-cd0a-474b-b5df-92abf6a28413", + "description": "Replace ScanSource with CSVFileScan for better performance", + "operatorsToAdd": [ + { + "operatorType": "CSVFileScan", + "position": { + "x": 200, + "y": 200 + }, + "properties": { + "fileName": "data.csv", + "limit": -1, + "offset": 0, + "schema": "auto" + } + } + ], + "operatorPropertiesToChange": [], + "operatorsToDelete": [ + "Source-Scan-1" + ], + "linksToAdd": [ + { + "source": { + "operatorId": "CSVFileScan-1", + "portId": "output-0" + }, + "target": { + "operatorId": "View-Results-1", + "portId": "input-0" + } + } + ], + "isPreviewActive": false + } +] \ No newline at end of file diff --git a/core/suggestion-service/test_with_real_data.py b/core/suggestion-service/test_with_real_data.py new file mode 100644 index 00000000000..fe5262528a1 --- /dev/null +++ b/core/suggestion-service/test_with_real_data.py @@ -0,0 +1,177 @@ +""" +Test script for workflow interpretation using real workflow data. +""" +import json +import os +from typing import Dict, Any, Optional +from pathlib import Path + +from workflow_interpretation.interpreter import WorkflowInterpreter, InterpretationMethod +from suggestion_engine.generator import SuggestionGenerator + +# Base directory for test data +TEST_DATA_DIR = Path("test/data") +TEST_RESULTS_DIR = Path("test/results") + +def load_test_data(workflow_dir: str) -> Dict[str, Any]: + """ + Load test data from the specified workflow directory. + + Args: + workflow_dir: Name of the workflow directory + + Returns: + Dictionary containing loaded test data + """ + base_path = TEST_DATA_DIR / workflow_dir + + # Load workflow data + with open(base_path / "workflow.json", "r") as f: + workflow_json = json.load(f) + + # Load compilation state + with open(base_path / "workflow_compilation_state.json", "r") as f: + compilation_state = json.load(f) + + # Load result tables (might be empty) + with open(base_path / "result_tables.json", "r") as f: + result_tables = json.load(f) + + # Load execution state + with open(base_path / "execution_state.json", "r") as f: + execution_state = json.load(f) + + return { + "workflow_json": workflow_json, + "compilation_state": compilation_state, + "result_tables": result_tables, + "execution_state": execution_state + } + +def test_workflow_interpretation(workflow_dir: str): + """ + Test workflow interpretation with data from the specified workflow directory. + + Args: + workflow_dir: Name of the workflow directory + """ + print(f"\n===== Testing interpretation for {workflow_dir} =====") + + # Load test data + test_data = load_test_data(workflow_dir) + + # Create interpreter + interpreter = WorkflowInterpreter() + + # Get operator input schema map and errors from compilation state + operator_input_schema_map = test_data["compilation_state"].get("operatorInputSchemaMap", {}) + operator_errors = test_data["compilation_state"].get("operatorErrors", {}) + + # Create results directory for this workflow + result_dir = TEST_RESULTS_DIR / workflow_dir + os.makedirs(result_dir, exist_ok=True) + + # Test raw interpretation + raw_description = interpreter.interpret_workflow( + test_data["workflow_json"], + operator_input_schema_map, + operator_errors, + InterpretationMethod.RAW + ) + + # Save raw interpretation to file + with open(result_dir / "raw_interpretation.txt", "w") as f: + f.write(raw_description) + + print(f"\n--- {workflow_dir} RAW Interpretation (truncated) ---") + # Print just the first few lines to avoid overwhelming output + print("\n".join(raw_description.split("\n")[:20]) + "\n... (truncated)") + + # Test by-path interpretation + by_path_description = interpreter.interpret_workflow( + test_data["workflow_json"], + operator_input_schema_map, + operator_errors, + InterpretationMethod.BY_PATH + ) + + # Save by-path interpretation to file + with open(result_dir / "by_path_interpretation.txt", "w") as f: + f.write(by_path_description) + + print(f"\n--- {workflow_dir} BY_PATH Interpretation ---") + print(by_path_description) + + print(f"\nInterpretation results saved to {result_dir}/") + + return True + +def test_suggestion_generation(workflow_dir: str): + """ + Test suggestion generation with data from the specified workflow directory. + + Args: + workflow_dir: Name of the workflow directory + """ + print(f"\n===== Testing suggestion generation for {workflow_dir} =====") + + # Load test data + test_data = load_test_data(workflow_dir) + + # Create suggestion generator + suggestion_generator = SuggestionGenerator() + + # Create results directory for this workflow + result_dir = TEST_RESULTS_DIR / workflow_dir + os.makedirs(result_dir, exist_ok=True) + + # Generate suggestions + suggestions = suggestion_generator.generate_suggestions( + test_data["workflow_json"], + test_data["compilation_state"], + test_data["result_tables"], + test_data["execution_state"] + ) + + # Save suggestions to file + with open(result_dir / "suggestions.json", "w") as f: + json.dump(suggestions, f, indent=2) + + print(f"\nGenerated {len(suggestions)} suggestions:") + for i, suggestion in enumerate(suggestions, 1): + print(f"\n{i}. {suggestion['description']}") + print(f" - Operators to add: {len(suggestion['operatorsToAdd'])}") + print(f" - Properties to change: {len(suggestion['operatorPropertiesToChange'])}") + print(f" - Operators to delete: {len(suggestion['operatorsToDelete'])}") + print(f" - Links to add: {len(suggestion['linksToAdd'])}") + + print(f"\nSuggestion results saved to {result_dir}/suggestions.json") + + return True + +def main(): + """Run tests with all workflow data.""" + workflow_dirs = ["workflow1", "workflow2", "workflow3"] + + # Ensure results directory exists + os.makedirs(TEST_RESULTS_DIR, exist_ok=True) + + for workflow_dir in workflow_dirs: + try: + print(f"\n{'='*50}") + print(f"Testing workflow: {workflow_dir}") + print(f"{'='*50}") + + # Test workflow interpretation + test_workflow_interpretation(workflow_dir) + + # Test suggestion generation + test_suggestion_generation(workflow_dir) + + except Exception as e: + print(f"Error testing {workflow_dir}: {str(e)}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/core/suggestion-service/workflow_interpretation/interpreter.py b/core/suggestion-service/workflow_interpretation/interpreter.py index bf8c0536176..ed271ece3be 100644 --- a/core/suggestion-service/workflow_interpretation/interpreter.py +++ b/core/suggestion-service/workflow_interpretation/interpreter.py @@ -98,10 +98,9 @@ def _interpret_by_path( Returns: A description of the workflow organized by execution paths """ - # Parse the workflow into a TexeraWorkflow object try: - texera_workflow = TexeraWorkflow() - texera_workflow.initialize_from_dict(workflow) + # Create a TexeraWorkflow with the dictionary input + texera_workflow = TexeraWorkflow(workflow_dict=workflow) # Extract all paths from source to sink paths = self._extract_paths(texera_workflow) From bf7482a391566733696e5d710b89bb2b1fccbe7f Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 17 Apr 2025 17:56:25 -0700 Subject: [PATCH 017/104] keep improving --- .../model/texera/TexeraWorkflow.py | 356 ++++++++++++------ .../suggestion-service/test_debug_workflow.py | 102 +++++ .../workflow_interpretation/interpreter.py | 338 +++++++++-------- 3 files changed, 523 insertions(+), 273 deletions(-) create mode 100644 core/suggestion-service/test_debug_workflow.py diff --git a/core/suggestion-service/model/texera/TexeraWorkflow.py b/core/suggestion-service/model/texera/TexeraWorkflow.py index cd174c7a827..f7002005455 100644 --- a/core/suggestion-service/model/texera/TexeraWorkflow.py +++ b/core/suggestion-service/model/texera/TexeraWorkflow.py @@ -1,6 +1,5 @@ import json - -from typing import List, Dict, Tuple, Any +from typing import List, Dict, Tuple, Any, Set, Optional import networkx as nx from model.Operator import Operator @@ -11,63 +10,85 @@ from model.texera.TexeraOperator import TexeraOperator +class Link: + """Simple class to represent a link between operators.""" + def __init__(self, src_id, src_port, target_id, target_port): + self.source = type('obj', (object,), {'operator_id': src_id, 'port_id': src_port}) + self.target = type('obj', (object,), {'operator_id': target_id, 'port_id': target_port}) + + class TexeraWorkflow(Workflow): def __init__( self, - workflow_content: str = "", workflow_dict: Dict[str, Any] = None, - operator_id_to_port_indexed_input_schemas_mapping: Dict[str, List['DataSchema']]=None, - operator_id_to_error_mapping: Dict[str, str]=None, + input_schema: Dict[str, Any] = None, + operator_errors: Dict[str, Any] = None, wid: int = 0, - workflow_title: str = "" + workflow_title: str = "", + operators: Dict[str, 'TexeraOperator'] = None, + links: List[Any] = None ): - - if operator_id_to_port_indexed_input_schemas_mapping is None: - operator_id_to_port_indexed_input_schemas_mapping = {} - if operator_id_to_error_mapping is None: - operator_id_to_error_mapping = {} - + """ + Initialize a TexeraWorkflow from either a workflow dictionary or directly from operators and links. + + Args: + workflow_dict: Dictionary representation of the workflow + input_schema: Dictionary mapping operator IDs to their input schemas + operator_errors: Dictionary mapping operator IDs to their errors + wid: Workflow ID + workflow_title: Title of the workflow + operators: Dictionary of operator IDs to TexeraOperator objects + links: List of link objects + """ + # Initialize with empty values self.wid = wid self.workflow_title = workflow_title self.DAG = nx.DiGraph() self.operators = {} self.links = [] - - # Handle both string content and direct dict input - if workflow_dict is not None: + self.input_schema = input_schema or {} + self.operator_errors = operator_errors or {} + self.workflow_dict = None + self.workflow_content = None + + # Initialize based on provided parameters + if operators is not None and links is not None: + # Direct operators and links provided + self.operators = operators + self.links = links + self.workflow_dict = self._create_workflow_dict_from_operators_and_links(operators, links) + self.workflow_content = json.dumps(self.workflow_dict) + self._build_dag_from_operators_and_links() + elif workflow_dict is not None: + # Initialize from workflow dictionary self.workflow_dict = workflow_dict self.workflow_content = json.dumps(workflow_dict) self.initialize_from_dict(workflow_dict) - elif workflow_content: - self.workflow_content = workflow_content - self.workflow_dict = json.loads(workflow_content) - operators_dict = self.workflow_dict.get("operators", {}) - links_dict = self.workflow_dict.get("links", {}) - - self.operators: Dict[str, 'TexeraOperator'] = { - op_dict['operatorID']: - TexeraOperator( - operator_dict = op_dict, - port_indexed_input_schemas=operator_id_to_port_indexed_input_schemas_mapping.get(op_dict['operatorID'], []), - error=operator_id_to_error_mapping.get(op_dict['operatorID'], "") - ) for op_dict in operators_dict + + def _create_workflow_dict_from_operators_and_links(self, operators: Dict[str, 'TexeraOperator'], links: List[Any]) -> Dict[str, Any]: + """Create a workflow dictionary from operators and links.""" + workflow_dict = {"content": {"operators": [], "links": []}} + + # Add operators to the workflow dict + for op_id, operator in operators.items(): + workflow_dict["content"]["operators"].append(operator.GetOperatorDict()) + + # Add links to the workflow dict + for link in links: + link_dict = { + "source": {"operatorID": link.source.operator_id, "portID": link.source.port_id}, + "target": {"operatorID": link.target.operator_id, "portID": link.target.port_id} } - - # Build the DAG - self._build_dag() + workflow_dict["content"]["links"].append(link_dict) + + return workflow_dict def initialize_from_dict(self, workflow_dict: Dict[str, Any]) -> None: - """ - Initialize the workflow from a dictionary (needed by the interpreter). - - Args: - workflow_dict: Dictionary representation of the workflow - """ - # Convert the dictionary to a string for internal storage + """Initialize the workflow from a dictionary.""" self.workflow_content = json.dumps(workflow_dict) self.workflow_dict = workflow_dict - # Extract operators and links + # Extract operators content = workflow_dict.get("content", {}) operators_dict = content.get("operators", []) @@ -81,13 +102,159 @@ def initialize_from_dict(self, workflow_dict: Dict[str, Any]) -> None: if op_id: self.operators[op_id] = TexeraOperator( operator_dict=op_dict, - port_indexed_input_schemas=[], - error="" + port_indexed_input_schemas=self._get_input_schemas_for_operator(op_id), + error=self.operator_errors.get(op_id, "") ) - # Build the DAG structure (adds links and completes initialization) + # Build the DAG (adds links and completes initialization) self._build_dag() + def _build_dag(self) -> None: + """Build the DAG from the workflow dictionary's operators and links.""" + self.DAG = nx.DiGraph() + + # Add nodes to DAG + for operator in self.GetOperators(): + self.DAG.add_node(operator.GetId(), + type=operator.GetType(), + inputPorts=[port.GetId() for port in operator.GetInputPorts()], + outputPorts=[port.GetId() for port in operator.GetOutputPorts()], + error=operator.GetError() + ) + + # Add links to DAG + links_dict = self.workflow_dict.get("content", {}).get("links", []) + for link in links_dict: + source_op_id = link.get('source', {}).get('operatorID') + src_port_id = link.get('source', {}).get('portID') + target_op_id = link.get('target', {}).get('operatorID') + target_port_id = link.get('target', {}).get('portID') + + if source_op_id and target_op_id: + # Add link to links list + self.links.append(Link(source_op_id, src_port_id, target_op_id, target_port_id)) + + # Add edge to DAG + op = self.operators.get(target_op_id) + schema = None + if op is not None: + schema = op.GetInputSchemaByPortID(target_port_id) + + self.DAG.add_edge(source_op_id, target_op_id, + srcPort=src_port_id, + targetPort=target_port_id, + schema=schema) + + def _build_dag_from_operators_and_links(self) -> None: + """Build the DAG from the operators and links provided directly.""" + self.DAG = nx.DiGraph() + + # Add nodes to DAG + for operator in self.GetOperators(): + self.DAG.add_node(operator.GetId(), + type=operator.GetType(), + inputPorts=[port.GetId() for port in operator.GetInputPorts()], + outputPorts=[port.GetId() for port in operator.GetOutputPorts()], + error=operator.GetError() + ) + + # Add links to DAG + for link in self.links: + source_op_id = link.source.operator_id + src_port_id = link.source.port_id + target_op_id = link.target.operator_id + target_port_id = link.target.port_id + + # Add edge to DAG + op = self.operators.get(target_op_id) + schema = None + if op is not None: + schema = op.GetInputSchemaByPortID(target_port_id) + + self.DAG.add_edge(source_op_id, target_op_id, + srcPort=src_port_id, + targetPort=target_port_id, + schema=schema) + + def get_all_paths(self) -> List[List[str]]: + """ + Find all possible paths through the workflow DAG. + A path is a sequence of operator IDs from a source node (no incoming edges) + to a sink node (no outgoing edges). + + Returns: + List of paths, where each path is a list of operator IDs + """ + # Find source and sink nodes + source_nodes = [node for node in self.DAG.nodes() if self.DAG.in_degree(node) == 0] + sink_nodes = [node for node in self.DAG.nodes() if self.DAG.out_degree(node) == 0] + + if not source_nodes or not sink_nodes: + return [] + + # Use DFS to find all paths + all_paths = [] + + def dfs_paths(current_node, path, visited): + visited.add(current_node) + + # If we've reached a sink node, add the path + if current_node in sink_nodes: + all_paths.append(path.copy()) + + # Explore neighbors + for neighbor in self.DAG.successors(current_node): + if neighbor not in visited: + path.append(neighbor) + dfs_paths(neighbor, path, visited.copy()) + path.pop() + + # Start DFS from each source node + for source in source_nodes: + dfs_paths(source, [source], set()) + + return all_paths + + def extract_path_workflow(self, path: List[str]) -> 'TexeraWorkflow': + """Extract a subworkflow containing only the operators and links in the given path.""" + # Create a new workflow dict with only the operators in the path + subworkflow_dict = {"content": {"operators": [], "links": []}} + + # Add operators from the path + for op_id in path: + if op_id in self.operators: + # Find the original operator dict + for op_dict in self.workflow_dict.get("content", {}).get("operators", []): + if op_dict.get("operatorID") == op_id: + subworkflow_dict["content"]["operators"].append(op_dict.copy()) + break + + # Add links between operators in the path + for i in range(len(path) - 1): + source_op_id, target_op_id = path[i], path[i + 1] + + # Find links between these operators + for link_dict in self.workflow_dict.get("content", {}).get("links", []): + source = link_dict.get("source", {}) + target = link_dict.get("target", {}) + + if (source.get("operatorID") == source_op_id and + target.get("operatorID") == target_op_id): + subworkflow_dict["content"]["links"].append(link_dict.copy()) + + # Create a new TexeraWorkflow with the same input schema and errors but only for this path + path_input_schema = {op_id: self.input_schema.get(op_id, []) for op_id in path if op_id in self.input_schema} + path_operator_errors = {op_id: self.operator_errors.get(op_id, "") for op_id in path if op_id in self.operator_errors} + + return TexeraWorkflow( + workflow_dict=subworkflow_dict, + input_schema=path_input_schema, + operator_errors=path_operator_errors, + wid=self.wid, + workflow_title=f"Path from {path[0]} to {path[-1]}" + ) + + # Interface methods def GetWorkflowContent(self) -> str: return self.workflow_content @@ -99,19 +266,23 @@ def GetOperators(self, types: List[str] = None) -> List['Operator']: return list(self.operators.values()) return [op for op in self.operators.values() if op.GetType() in types] + def get_operators(self) -> List[Dict[str, Any]]: + """Returns the operator dictionaries from the workflow.""" + return self.workflow_dict.get("content", {}).get("operators", []) + + def get_links(self) -> List[Dict[str, Any]]: + """Returns the link dictionaries from the workflow.""" + return self.workflow_dict.get("content", {}).get("links", []) + def TopologicalSort(self) -> List['Operator']: - # Assuming a valid DAG and operators are sortable - # A complete topological sort algorithm should be implemented here - sorted_operators = list(self.operators.values()) # Placeholder for actual sorting logic - return sorted_operators + # This is a placeholder for actual topological sort logic + return list(self.operators.values()) def GetDAG(self): return self.DAG def GetSchemaToNextOperatorDistributionMapping(self) -> Dict['DataSchema', Dict[str, int]]: - result: Dict['DataSchema', Dict[str, int]] = {} - - # Iterate over all edges in the DAG + result = {} for source_op_id, target_op_id, edge_data in self.DAG.edges(data=True): schema = edge_data['schema'] target_op = self.operators.get(target_op_id) @@ -119,18 +290,13 @@ def GetSchemaToNextOperatorDistributionMapping(self) -> Dict['DataSchema', Dict[ if schema not in result: result[schema] = {} - if target_op_type not in result[schema]: result[schema][target_op_type] = 0 - result[schema][target_op_type] += 1 - return result def GetOperatorTypeToNextOperatorDistributionMapping(self) -> Dict[str, Dict[str, int]]: - result: Dict[str, Dict[str, int]] = {} - - # Iterate over all edges in the DAG + result = {} for source_op_id, target_op_id, edge_data in self.DAG.edges(data=True): source_op = self.operators.get(source_op_id) source_op_type = source_op.GetType() @@ -139,37 +305,40 @@ def GetOperatorTypeToNextOperatorDistributionMapping(self) -> Dict[str, Dict[str if source_op_type not in result: result[source_op_type] = {} - if target_op_type not in result[source_op_type]: result[source_op_type][target_op_type] = 0 - result[source_op_type][target_op_type] += 1 - return result def GetAdditionPairs(self) -> List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]]: - results: List[Tuple[Tuple[Operator, Port], Tuple[Operator, Port]]] = [] - # Iterate over all edges in the DAG + results = [] for source_op_id, target_op_id, edge_data in self.DAG.edges(data=True): source_op = self.operators.get(source_op_id) target_op = self.operators.get(target_op_id) srcPortId = edge_data.get('srcPort') targetPortId = edge_data.get('targetPort') + if source_op is None or target_op is None: continue + srcPort = next((port for port in source_op.GetOutputPorts() if port.GetId() == srcPortId), None) targetPort = next((port for port in target_op.GetInputPorts() if port.GetId() == targetPortId), None) if srcPort is None or targetPort is None: continue + results.append(((source_op, srcPort), (target_op, targetPort))) return results + def _get_input_schemas_for_operator(self, operator_id: str) -> List['DataSchema']: + """Extract input schemas for a given operator from the input_schema dictionary.""" + if not self.input_schema or operator_id not in self.input_schema: + return [] + return self.input_schema.get(operator_id, []) + def __str__(self) -> str: - # Create a string representation of the workflow operators_str = '\n'.join([str(operator) for operator in self.GetOperators()]) edges_str = '\n'.join([f"{source} -> {target}" for source, target in self.DAG.edges()]) - return ( f"TexeraWorkflow(\n" f" WorkflowID={self.wid},\n" @@ -180,22 +349,11 @@ def __str__(self) -> str: ) def VisualizeDAG(self) -> str: - """ - Generate a visualization of the workflow DAG. - This is a placeholder implementation required for the abstract class. - - Returns: - A string representation of the workflow DAG visualization. - """ - # Create a simple text-based visualization of the DAG - visualization = "Workflow DAG Visualization:\n" - - # Add operator nodes - visualization += "\nOperators:\n" + """Generate a text-based visualization of the workflow DAG.""" + visualization = "Workflow DAG Visualization:\n\nOperators:\n" for op_id, operator in self.operators.items(): visualization += f" - {op_id} ({operator.GetType()})\n" - # Add links (edges) visualization += "\nLinks:\n" for link in self.links: source_op = link.source.operator_id @@ -204,50 +362,4 @@ def VisualizeDAG(self) -> str: target_port = link.target.port_id visualization += f" - {source_op}:{source_port} → {target_op}:{target_port}\n" - return visualization - - def _build_dag(self) -> None: - """Build the DAG from the workflow dictionary's operators and links.""" - # Clear existing DAG - self.DAG = nx.DiGraph() - - # Extract operators and links - operators_dict = self.workflow_dict.get("content", {}).get("operators", []) - links_dict = self.workflow_dict.get("content", {}).get("links", []) - - # Add nodes to DAG - for operator in self.GetOperators(): - self.DAG.add_node(operator.GetId(), - type=operator.GetType(), - inputPorts=[port.GetId() for port in operator.GetInputPorts()], - outputPorts=[port.GetId() for port in operator.GetOutputPorts()], - error=operator.GetError() - ) - - # Add links to DAG - for link in links_dict: - source_op_id = link.get('source', {}).get('operatorID') - src_port_id = link.get('source', {}).get('portID') - target_op_id = link.get('target', {}).get('operatorID') - target_port_id = link.get('target', {}).get('portID') - - if source_op_id and target_op_id: - # Add link to links list - class Link: - def __init__(self, src_id, src_port, target_id, target_port): - self.source = type('obj', (object,), {'operator_id': src_id, 'port_id': src_port}) - self.target = type('obj', (object,), {'operator_id': target_id, 'port_id': target_port}) - - self.links.append(Link(source_op_id, src_port_id, target_op_id, target_port_id)) - - # Add edge to DAG - op = self.operators.get(target_op_id) - schema = None - if op is not None: - schema = op.GetInputSchemaByPortID(target_port_id) - - self.DAG.add_edge(source_op_id, - target_op_id, - srcPort=src_port_id, - targetPort=target_port_id, - schema=schema) \ No newline at end of file + return visualization \ No newline at end of file diff --git a/core/suggestion-service/test_debug_workflow.py b/core/suggestion-service/test_debug_workflow.py new file mode 100644 index 00000000000..48e3323e10d --- /dev/null +++ b/core/suggestion-service/test_debug_workflow.py @@ -0,0 +1,102 @@ +import json +import os +import sys +from typing import Dict, Any + +from model.texera.TexeraWorkflow import TexeraWorkflow + +def load_test_workflow(workflow_name: str) -> Dict[str, Any]: + """ + Load a test workflow from the test/data/{workflow_name} directory. + + Args: + workflow_name: Name of the workflow folder + + Returns: + Dictionary containing the workflow + """ + # Construct the path to the workflow file + workflow_file = os.path.join("test", "data", workflow_name, "workflow.json") + + # Load the workflow from the file + with open(workflow_file, "r") as f: + return json.load(f) + +def debug_workflow(workflow_name: str) -> None: + """ + Debug a workflow's structure. + + Args: + workflow_name: Name of the workflow folder + """ + print(f"Debugging workflow: {workflow_name}") + + # Load the workflow + workflow_dict = load_test_workflow(workflow_name) + + # Create a TexeraWorkflow object + texera_workflow = TexeraWorkflow(workflow_dict=workflow_dict) + + # Debug the workflow structure + print("\n1. Examining workflow dictionary structure:") + print(f" - keys: {list(workflow_dict.keys())}") + + if "content" in workflow_dict: + print(f" - content keys: {list(workflow_dict['content'].keys())}") + + if "operators" in workflow_dict["content"]: + operators = workflow_dict["content"]["operators"] + print(f" - number of operators: {len(operators)}") + if operators: + print(f" - first operator keys: {list(operators[0].keys())}") + + if "links" in workflow_dict["content"]: + links = workflow_dict["content"]["links"] + print(f" - number of links: {len(links)}") + if links: + print(f" - first link keys: {list(links[0].keys())}") + + # Debug the DAG + print("\n2. Examining DAG structure:") + print(f" - number of nodes: {len(texera_workflow.DAG.nodes())}") + print(f" - number of edges: {len(texera_workflow.DAG.edges())}") + + # Print all paths + print("\n3. All paths through the workflow:") + paths = texera_workflow.get_all_paths() + print(f" - number of paths: {len(paths)}") + for i, path in enumerate(paths): + print(f" Path {i+1}: {path}") + + # Check a path workflow if paths exist + if paths: + print("\n4. Examining a path workflow:") + path_workflow = texera_workflow.extract_path_workflow(paths[0]) + + print(f" - path workflow dict keys: {list(path_workflow.workflow_dict.keys())}") + if "content" in path_workflow.workflow_dict: + content = path_workflow.workflow_dict["content"] + print(f" - content keys: {list(content.keys())}") + + if "operators" in content: + operators = content["operators"] + print(f" - number of operators: {len(operators)}") + if operators: + print(f" - first operator type: {type(operators[0])}") + print(f" - first operator keys: {list(operators[0].keys() if isinstance(operators[0], dict) else [])}") + + if "links" in content: + links = content["links"] + print(f" - number of links: {len(links)}") + if links: + print(f" - first link type: {type(links[0])}") + print(f" - first link keys: {list(links[0].keys() if isinstance(links[0], dict) else [])}") + +if __name__ == "__main__": + # Get the workflow name from command line arguments + if len(sys.argv) < 2: + print("Usage: python test_debug_workflow.py ") + sys.exit(1) + + workflow_name = sys.argv[1] + debug_workflow(workflow_name) \ No newline at end of file diff --git a/core/suggestion-service/workflow_interpretation/interpreter.py b/core/suggestion-service/workflow_interpretation/interpreter.py index ed271ece3be..cfef4b887f1 100644 --- a/core/suggestion-service/workflow_interpretation/interpreter.py +++ b/core/suggestion-service/workflow_interpretation/interpreter.py @@ -3,7 +3,9 @@ """ from typing import Dict, List, Any, Optional, Tuple import json +import traceback from enum import Enum +from collections import defaultdict from model.texera.TexeraWorkflow import TexeraWorkflow @@ -44,12 +46,16 @@ def interpret_workflow( Returns: A natural language description of the workflow """ - if method == InterpretationMethod.RAW: - return self._interpret_raw(workflow, input_schema, operator_errors) - elif method == InterpretationMethod.BY_PATH: - return self._interpret_by_path(workflow, input_schema, operator_errors) - else: - raise ValueError(f"Unsupported interpretation method: {method}") + try: + if method == InterpretationMethod.RAW: + return self._interpret_raw(workflow, input_schema, operator_errors) + elif method == InterpretationMethod.BY_PATH: + return self._interpret_by_path(workflow, input_schema, operator_errors) + else: + raise ValueError(f"Unsupported interpretation method: {method}") + except Exception as e: + stack_trace = traceback.format_exc() + return f"Error interpreting workflow: {str(e)}\n\nStacktrace:\n{stack_trace}" def _interpret_raw( self, @@ -88,181 +94,211 @@ def _interpret_by_path( operator_errors: Optional[Dict[str, Any]] = None ) -> str: """ - Generate a description of the workflow by extracting and describing paths from source to sink. + Interprets a workflow by analyzing execution paths. Args: - workflow: The workflow dictionary - input_schema: The input schema dictionary for each operator - operator_errors: Dictionary of static errors for each operator - + workflow: JSON representation of the workflow + input_schema: Optional dictionary mapping operator IDs to their input schemas + operator_errors: Optional dictionary mapping operator IDs to their errors + Returns: - A description of the workflow organized by execution paths + A natural language interpretation of the workflow """ try: - # Create a TexeraWorkflow with the dictionary input - texera_workflow = TexeraWorkflow(workflow_dict=workflow) + # Create a TexeraWorkflow object from the workflow dictionary + texera_workflow = TexeraWorkflow( + workflow_dict=workflow, + input_schema=input_schema or {}, + operator_errors=operator_errors or {} + ) - # Extract all paths from source to sink - paths = self._extract_paths(texera_workflow) + # Get all paths through the workflow + paths = texera_workflow.get_all_paths() - # Generate description from paths - description = "Here are the existing paths in this workflow and related schemas:\n\n" + if not paths: + return "This workflow doesn't contain any complete execution paths." - for i, path in enumerate(paths, 1): - description += f"Path {i}:\n" - description += self._describe_path( - path, texera_workflow, input_schema, operator_errors) - description += "\n\n" + interpretation = "This workflow contains the following execution paths:\n\n" - return description + # Generate descriptions for each path + for i, path in enumerate(paths): + try: + interpretation += f"Path {i+1}:\n" + + # Extract the subworkflow for this path + path_workflow = texera_workflow.extract_path_workflow(path) + + # Generate description for this path workflow + path_description = self._describe_path_workflow(path_workflow) + interpretation += path_description + "\n" + except Exception as e: + interpretation += f"Error processing path {i+1}: {str(e)}\n\n" + + return interpretation + except Exception as e: - # If path extraction fails, fall back to raw interpretation - print(f"Error in path interpretation: {str(e)}. Falling back to raw interpretation.") - return self._interpret_raw(workflow, input_schema, operator_errors) + stack_trace = traceback.format_exc() + return f"Error interpreting workflow by path: {str(e)}\n\nStacktrace:\n{stack_trace}" - def _extract_paths(self, workflow: TexeraWorkflow) -> List[List[str]]: + def _describe_path_workflow(self, path_workflow: TexeraWorkflow) -> str: """ - Extract all paths from source operators to sink operators. + Generate a description for a path workflow. Args: - workflow: The TexeraWorkflow object + path_workflow: A TexeraWorkflow object representing a path Returns: - A list of paths, where each path is a list of operator IDs + A description of the path """ - # Find source operators (operators with no inputs) - source_operators = [] - for op_id, operator in workflow.operators.items(): - has_input = False - for link in workflow.links: - if link.target.operator_id == op_id: - has_input = True - break - if not has_input: - source_operators.append(op_id) - - # Find sink operators (operators with no outputs) - sink_operators = [] - for op_id, operator in workflow.operators.items(): - has_output = False - for link in workflow.links: - if link.source.operator_id == op_id: - has_output = True - break - if not has_output: - sink_operators.append(op_id) - - # For each source, find all paths to sinks - all_paths = [] - for source in source_operators: - paths = self._find_paths(source, sink_operators, workflow) - all_paths.extend(paths) + try: + description = "" + + # Get operator dictionaries directly from the workflow content + operators = path_workflow.workflow_dict.get("content", {}).get("operators", []) + links = path_workflow.workflow_dict.get("content", {}).get("links", []) + + # Sort operators based on their position in the path + ordered_operators = self._sort_operators_by_links(operators, links) + + # Describe each operator and its connections + for i, operator in enumerate(ordered_operators): + try: + operator_id = operator["operatorID"] # This should exist in every operator + operator_type = operator.get("operatorType", "Unknown") + # Use customDisplayName if available, otherwise operatorType as fallback + operator_name = operator.get("customDisplayName", operator_type) + + # Get input schema for this operator if available + input_schema_list = path_workflow._get_input_schemas_for_operator(operator_id) + + description += f"- {operator_name} ({operator_type})" + + # Add operator properties if available + if "operatorProperties" in operator: + props = operator["operatorProperties"] + description += "\n Properties:" + for prop_name, prop_value in props.items(): + # Display full property values without truncation + description += f"\n - {prop_name}: {prop_value}" + + # Add any static errors if they exist + if path_workflow.operator_errors and operator_id in path_workflow.operator_errors: + operator_error = path_workflow.operator_errors[operator_id] + if operator_error: # Only add if there's an actual error + description += f"\n ERROR: {operator_error}" + + # Add schema information if available + if input_schema_list and len(input_schema_list) > 0: + description += "\n Input Schema:" + for schema_item in input_schema_list: + # Check if schema_item is a dictionary + if isinstance(schema_item, dict): + port = schema_item.get('port', 'unknown') + attributes = schema_item.get('attributes', []) + description += f"\n - Port: {port}" + description += f"\n Attributes: {attributes}" + # For list schema items or other types, display differently + elif isinstance(schema_item, list): + description += f"\n - Schema items: {len(schema_item)}" + # Show all items without truncation + for idx, attr in enumerate(schema_item): + description += f"\n Item {idx}: {attr}" + else: + description += f"\n - Schema: {str(schema_item)}" + + # For operators other than the last one, show where it connects to + if i < len(ordered_operators) - 1: + next_operator = ordered_operators[i + 1] + next_op_id = next_operator["operatorID"] + connection = self._find_connection(operator_id, next_op_id, links) + + if connection: + source_port = connection["source"]["portID"] + target_port = connection["target"]["portID"] + next_op_name = next_operator.get("customDisplayName", next_operator.get("operatorType", "Unknown")) + description += f"\n Connects from port '{source_port}' to '{next_op_name}' port '{target_port}'" + + description += "\n" + except Exception as e: + description += f"Error describing operator: {str(e)}\n" + + return description + except Exception as e: + stack_trace = traceback.format_exc() + return f"Error describing path workflow: {str(e)}\n\nStacktrace:\n{stack_trace}" - return all_paths - - def _find_paths( - self, - current: str, - sinks: List[str], - workflow: TexeraWorkflow, - path: Optional[List[str]] = None, - visited: Optional[set] = None - ) -> List[List[str]]: + def _sort_operators_by_links(self, operators: List[Dict[str, Any]], links: List[Dict[str, Any]]) -> List[Dict[str, Any]]: """ - Recursively find all paths from the current operator to any sink operator. + Sort operators based on their connections in the links. Args: - current: The current operator ID - sinks: List of sink operator IDs - workflow: The TexeraWorkflow object - path: The current path being built - visited: Set of visited operator IDs to avoid cycles + operators: List of operator dictionaries + links: List of link dictionaries Returns: - A list of paths, where each path is a list of operator IDs + List of sorted operator dictionaries """ - if path is None: - path = [] - if visited is None: + try: + # Create a map of operator IDs to operators + operator_map = {op["operatorID"]: op for op in operators} + + # Create a directed graph representing operator connections + graph = defaultdict(list) + for link in links: + source_id = link["source"]["operatorID"] + target_id = link["target"]["operatorID"] + graph[source_id].append(target_id) + + # Find source operators (operators with no incoming links) + incoming_links = defaultdict(int) + for link in links: + target_id = link["target"]["operatorID"] + incoming_links[target_id] += 1 + + source_operators = [op for op in operators if incoming_links.get(op["operatorID"], 0) == 0] + + # Perform topological sort visited = set() + ordered_operators = [] + + def dfs(op_id): + if op_id in visited: + return + visited.add(op_id) + for neighbor in graph.get(op_id, []): + dfs(neighbor) + if op_id in operator_map: + ordered_operators.append(operator_map[op_id]) + + for op in source_operators: + dfs(op["operatorID"]) + + # Reverse to get correct order from source to sink + return ordered_operators[::-1] - # Add current operator to path and visited - path = path + [current] - visited = visited.union({current}) - - # If current is a sink, we've found a path - if current in sinks: - return [path] - - # Find all next operators - next_operators = [] - for link in workflow.links: - if link.source.operator_id == current: - next_operators.append(link.target.operator_id) - - # Recursively find paths for each next operator - all_paths = [] - for next_op in next_operators: - # Avoid cycles - if next_op not in visited: - new_paths = self._find_paths(next_op, sinks, workflow, path, visited) - all_paths.extend(new_paths) - - return all_paths + except Exception as e: + print(f"Error in _sort_operators_by_links: {str(e)}") + # If sorting fails, return operators as is + return operators - def _describe_path( - self, - path: List[str], - workflow: TexeraWorkflow, - input_schema: Optional[Dict[str, Any]] = None, - operator_errors: Optional[Dict[str, Any]] = None - ) -> str: + def _find_connection(self, source_id: str, target_id: str, links: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]: """ - Generate a description of a specific path in the workflow. + Find the link connecting two operators. Args: - path: List of operator IDs representing a path - workflow: The TexeraWorkflow object - input_schema: The input schema dictionary for each operator - operator_errors: Dictionary of static errors for each operator + source_id: ID of the source operator + target_id: ID of the target operator + links: List of link dictionaries Returns: - A description of the path + The link dictionary if found, None otherwise """ - description = "" - - # Describe each operator in the path - for i, op_id in enumerate(path): - operator = workflow.operators[op_id] - description += f" {i+1}. {operator.operator_type}" - - # Add operator properties if available - if operator.properties: - description += " with properties:\n" - for prop_key, prop_value in operator.properties.items(): - description += f" - {prop_key}: {prop_value}\n" - else: - description += "\n" - - # Add schema information if available - if input_schema and op_id in input_schema: - schema_info = input_schema[op_id] - description += " Schema:\n" - description += f" {json.dumps(schema_info, indent=6)}\n" - - # Add error information if available - if operator_errors and op_id in operator_errors: - error_info = operator_errors[op_id] - description += " Errors:\n" - description += f" {json.dumps(error_info, indent=6)}\n" - - # Add connector information if not the last operator - if i < len(path) - 1: - # Find the link between this operator and the next - next_op_id = path[i + 1] - for link in workflow.links: - if link.source.operator_id == op_id and link.target.operator_id == next_op_id: - description += f" → Connected to {next_op_id} via ports {link.source.port_id} → {link.target.port_id}\n" - break - - return description \ No newline at end of file + try: + for link in links: + if (link["source"]["operatorID"] == source_id and + link["target"]["operatorID"] == target_id): + return link + return None + except Exception as e: + print(f"Error in _find_connection: {str(e)}") + return None \ No newline at end of file From 6303b8e121828251d7ce71d916cc7903b39ea6d7 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Fri, 18 Apr 2025 11:29:42 -0700 Subject: [PATCH 018/104] add initial --- core/suggestion-service/.env.example | 14 + core/suggestion-service/README.md | 146 +++++++++- core/suggestion-service/app.py | 46 ++- core/suggestion-service/llm_agent/__init__.py | 7 + .../llm_agent/anthropic_agent.py | 137 +++++++++ core/suggestion-service/llm_agent/base.py | 75 +++++ .../llm_agent/openai_agent.py | 135 +++++++++ .../output_formatter/__init__.py | 15 + .../output_formatter/formatter.py | 156 ++++++++++ core/suggestion-service/requirements.txt | 7 +- .../suggestion_engine/generator.py | 273 +++++++++++++++--- .../workflow1/by_path_interpretation.txt | 106 ++----- .../test/results/workflow1/suggestions.json | 4 +- .../workflow2/by_path_interpretation.txt | 201 ++++--------- .../test/results/workflow2/suggestions.json | 4 +- .../workflow3/by_path_interpretation.txt | 260 +++++------------ .../test/results/workflow3/suggestions.json | 2 +- core/suggestion-service/test_llm_agent.py | 138 +++++++++ 18 files changed, 1276 insertions(+), 450 deletions(-) create mode 100644 core/suggestion-service/.env.example create mode 100644 core/suggestion-service/llm_agent/__init__.py create mode 100644 core/suggestion-service/llm_agent/anthropic_agent.py create mode 100644 core/suggestion-service/llm_agent/base.py create mode 100644 core/suggestion-service/llm_agent/openai_agent.py create mode 100644 core/suggestion-service/output_formatter/__init__.py create mode 100644 core/suggestion-service/output_formatter/formatter.py create mode 100644 core/suggestion-service/test_llm_agent.py diff --git a/core/suggestion-service/.env.example b/core/suggestion-service/.env.example new file mode 100644 index 00000000000..f7fc40d5ff1 --- /dev/null +++ b/core/suggestion-service/.env.example @@ -0,0 +1,14 @@ +# LLM provider configuration +# Options: "openai" or "anthropic" +LLM_PROVIDER=openai + +# OpenAI configuration +OPENAI_API_KEY=your_openai_api_key_here +OPENAI_MODEL=gpt-4-turbo-preview + +# Anthropic configuration +ANTHROPIC_API_KEY=your_anthropic_api_key_here +ANTHROPIC_MODEL=claude-3-opus-20240229 + +# Suggestion configuration +MAX_SUGGESTIONS=3 \ No newline at end of file diff --git a/core/suggestion-service/README.md b/core/suggestion-service/README.md index 7d4c8beeb69..ce4c9af7338 100644 --- a/core/suggestion-service/README.md +++ b/core/suggestion-service/README.md @@ -41,9 +41,147 @@ Prompt generation can be divided into 2 major packages, each package provides a #### 2. Workflow Interpretation with Execution information TODO -### LLM agent calling layers +### LLM Agent Layer -This layer is responsible for calling language models to get the response(the suggestions). +This layer is responsible for calling language models to get the response (the suggestions). -1. Have a package to talk to different language models using the same unified function. -2. Have the configuration layer of the underlying language models, i.e. certain model, or multiple language model agents. +The LLM Agent layer is organized as follows: + +1. `llm_agent/base.py` - Defines the abstract base class for all LLM agents and a factory for creating agents +2. `llm_agent/openai_agent.py` - Implementation for OpenAI models (GPT-4, GPT-3.5, etc.) +3. `llm_agent/anthropic_agent.py` - Implementation for Anthropic Claude models + +#### Configuration + +LLM agents are configured using environment variables: + +``` +# LLM provider configuration (openai or anthropic) +LLM_PROVIDER=openai + +# OpenAI configuration +OPENAI_API_KEY=your_openai_api_key_here +OPENAI_MODEL=gpt-4-turbo-preview + +# Anthropic configuration +ANTHROPIC_API_KEY=your_anthropic_api_key_here +ANTHROPIC_MODEL=claude-3-opus-20240229 +``` + +#### Usage + +The SuggestionGenerator automatically creates and uses an LLM agent based on the configuration. +You can also create and use an LLM agent directly: + +```python +from llm_agent.base import LLMAgentFactory + +# Create an LLM agent +agent = LLMAgentFactory.create("openai", model="gpt-4-turbo-preview") + +# Generate suggestions +suggestions = agent.generate_suggestions( + prompt="Description of the workflow", + max_suggestions=3, + temperature=0.7 +) +``` + +#### Adding New LLM Providers + +To add a new LLM provider: + +1. Create a new file `llm_agent/your_provider_agent.py` +2. Implement the LLMAgent interface +3. Register your implementation with the LLMAgentFactory + +Example: +```python +@LLMAgentFactory.register("your_provider") +class YourProviderAgent(LLMAgent): + def __init__(self, model="default-model", api_key=None): + # Initialize client + pass + + def generate_suggestions(self, prompt, max_suggestions=3, temperature=0.7, **kwargs): + # Generate suggestions using your provider's API + pass +``` + +### Output Formatting Layer + +This layer ensures that LLM outputs match the expected format for workflow suggestions. + +The output formatter is responsible for: + +1. Extracting JSON from raw LLM output +2. Validating suggestion structure +3. Filling in missing values (UUIDs, empty fields, etc.) +4. Converting to the correct format expected by the frontend + +#### Usage + +The output formatter is automatically used by the LLM agents, but you can also use it directly: + +```python +from output_formatter.formatter import format_raw_suggestions + +# Format raw LLM output +raw_output = """ +[ + { + "suggestion": "Add a keyword search", + "changes": { + "operatorsToAdd": [ + { + "operatorType": "KeywordSearch", + "operatorProperties": { + "keyword": "example" + } + } + ], + "linksToAdd": [] + } + } +] +""" + +formatted_suggestions = format_raw_suggestions(raw_output) +``` + +### Testing + +You can test the LLM agent and formatter using the test script: + +```bash +# Test an LLM agent +python test_llm_agent.py agent --provider openai --prompt "Generate a workflow suggestion" --output output.json + +# Test the formatter with a raw JSON file +python test_llm_agent.py formatter --input raw_output.json --output formatted_output.json +``` + +### API Endpoints + +The service exposes the following endpoints: + +- `GET /` - Health check endpoint +- `GET /api/config` - Get the current configuration +- `POST /api/workflow-suggestion` - Generate workflow suggestions + +Required request format: +```json +{ + "workflow": "{JSON string of the workflow}", + "compilationState": { + "state": "Succeeded", + "operatorInputSchemaMap": {}, + "operatorErrors": {} + }, + "executionState": { + "state": "Completed" + }, + "resultTables": {}, + "maxSuggestions": 3 +} +``` diff --git a/core/suggestion-service/app.py b/core/suggestion-service/app.py index 87a72a1f661..6ac39a7c5c4 100644 --- a/core/suggestion-service/app.py +++ b/core/suggestion-service/app.py @@ -1,17 +1,18 @@ import json import os from typing import Dict, List, Any, Optional +from dotenv import load_dotenv -from fastapi import FastAPI, HTTPException +from fastapi import FastAPI, HTTPException, Depends from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, Field from suggestion_engine.generator import SuggestionGenerator -app = FastAPI(title="Texera Workflow Suggestion Service") +# Load environment variables +load_dotenv() -# Initialize the suggestion generator -suggestion_generator = SuggestionGenerator() +app = FastAPI(title="Texera Workflow Suggestion Service") # Enable CORS app.add_middleware( @@ -22,6 +23,17 @@ allow_headers=["*"], ) +# Get LLM configuration from environment variables +LLM_PROVIDER = os.environ.get("LLM_PROVIDER", "openai") +LLM_MODEL = os.environ.get("OPENAI_MODEL" if LLM_PROVIDER == "openai" else "ANTHROPIC_MODEL", None) +MAX_SUGGESTIONS = int(os.environ.get("MAX_SUGGESTIONS", "3")) + +# Initialize the suggestion generator with LLM support +suggestion_generator = SuggestionGenerator( + llm_provider=LLM_PROVIDER, + llm_model=LLM_MODEL +) + # Input models class SchemaAttribute(BaseModel): attributeName: str @@ -35,6 +47,7 @@ class OperatorToAdd(BaseModel): operatorType: str position: Position properties: Optional[Dict[str, Any]] = None + customDisplayName: Optional[str] = None class OperatorPropertyChange(BaseModel): operatorId: str @@ -78,11 +91,30 @@ class SuggestionRequest(BaseModel): compilationState: CompilationStateInfo executionState: Optional[ExecutionStateInfo] = None resultTables: Dict[str, ResultTable] + maxSuggestions: Optional[int] = MAX_SUGGESTIONS + +class LLMConfig(BaseModel): + provider: str = LLM_PROVIDER + model: Optional[str] = LLM_MODEL + @app.get("/") async def root(): return {"message": "Texera Workflow Suggestion Service is running"} + +@app.get("/api/config") +async def get_config(): + """Get the current configuration.""" + return { + "llm": { + "provider": LLM_PROVIDER, + "model": LLM_MODEL + }, + "maxSuggestions": MAX_SUGGESTIONS + } + + @app.post("/api/workflow-suggestion", response_model=List[WorkflowSuggestion]) async def generate_suggestions(request: SuggestionRequest): """ @@ -96,7 +128,7 @@ async def generate_suggestions(request: SuggestionRequest): """ try: # Parse the workflow JSON - print("received request", request) + print("Received suggestion request") workflow_json = json.loads(request.workflow) # Convert Pydantic models to dictionaries for the suggestion generator @@ -128,7 +160,8 @@ async def generate_suggestions(request: SuggestionRequest): workflow_json, compilation_state_dict, result_tables_dict, - execution_state_dict + execution_state_dict, + max_suggestions=request.maxSuggestions or MAX_SUGGESTIONS ) # Convert the dictionaries to WorkflowSuggestion objects @@ -143,6 +176,7 @@ async def generate_suggestions(request: SuggestionRequest): except Exception as e: raise HTTPException(status_code=500, detail=f"Error generating suggestions: {str(e)}") + if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=9094) \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/__init__.py b/core/suggestion-service/llm_agent/__init__.py new file mode 100644 index 00000000000..18bb141eb78 --- /dev/null +++ b/core/suggestion-service/llm_agent/__init__.py @@ -0,0 +1,7 @@ +"""LLM Agent module for calling language models.""" + +from llm_agent.base import LLMAgent, LLMAgentFactory +from llm_agent.openai_agent import OpenAIAgent +from llm_agent.anthropic_agent import AnthropicAgent + +__all__ = ['LLMAgent', 'LLMAgentFactory', 'OpenAIAgent', 'AnthropicAgent'] \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/anthropic_agent.py b/core/suggestion-service/llm_agent/anthropic_agent.py new file mode 100644 index 00000000000..a6ad3eeabbe --- /dev/null +++ b/core/suggestion-service/llm_agent/anthropic_agent.py @@ -0,0 +1,137 @@ +"""Anthropic Claude LLM agent.""" +import os +import json +from typing import Dict, Any, List, Optional + +import anthropic +from anthropic import Anthropic + +from llm_agent.base import LLMAgent, LLMAgentFactory +from output_formatter.formatter import format_raw_suggestions + + +@LLMAgentFactory.register("anthropic") +class AnthropicAgent(LLMAgent): + """ + Implementation of the LLM agent interface using Anthropic's Claude API. + + This agent supports different Claude models, such as: + - claude-3-opus-20240229 + - claude-3-sonnet-20240229 + - claude-3-haiku-20240307 + - claude-2.1 + - claude-2.0 + """ + + def __init__(self, + model: str = "claude-3-opus-20240229", + api_key: Optional[str] = None): + """ + Initialize the Anthropic Claude agent. + + Args: + model: The Claude model to use + api_key: The Anthropic API key (if None, uses ANTHROPIC_API_KEY environment variable) + """ + self.model = model + self.client = Anthropic(api_key=api_key or os.environ.get("ANTHROPIC_API_KEY")) + + def generate_suggestions(self, + prompt: str, + max_suggestions: int = 3, + temperature: float = 0.7, + max_tokens: Optional[int] = None, + **kwargs) -> List[Dict[str, Any]]: + """ + Generate workflow suggestions using Anthropic's Claude API. + + Args: + prompt: The natural language prompt describing the workflow + max_suggestions: Maximum number of suggestions to generate + temperature: Sampling temperature (0.0-1.0) where lower is more deterministic + max_tokens: Maximum number of tokens to generate (if None, defaults to 4096) + **kwargs: Additional Claude-specific parameters + + Returns: + A list of suggestion dictionaries formatted according to the interface + """ + # Enhance the prompt with instruction about the output format + system_prompt = self._create_system_prompt(max_suggestions) + + try: + response = self.client.messages.create( + model=self.model, + system=system_prompt, + messages=[ + {"role": "user", "content": prompt} + ], + temperature=temperature, + max_tokens=max_tokens or 4096, + **kwargs + ) + + # Extract content from the response + raw_content = response.content[0].text + + # Parse and format the suggestions + suggestions = format_raw_suggestions(raw_content) + + return suggestions[:max_suggestions] + + except Exception as e: + print(f"Error generating suggestions with Anthropic Claude: {str(e)}") + return [] + + def _create_system_prompt(self, max_suggestions: int) -> str: + """Create the system prompt for the Claude model.""" + return f"""You are an AI assistant that helps users improve their Texera workflows by suggesting useful modifications. + +Analyze the provided workflow description and generate {max_suggestions} suggestions to improve it. +Your suggestions should address common issues, optimizations, or additional useful features. + +For each suggestion, provide: +1. A brief natural language description of the recommendation +2. Detailed changes required to implement the suggestion + +Structure each suggestion as a valid JSON object with the following format: +{{ + "suggestion": "A clear description of the improvement", + "changes": {{ + "operatorsToAdd": [ + {{ + "operatorType": "TypeOfOperator", + "operatorID": "OperatorType-operator-UUID", + "operatorProperties": {{ + "property1": "value1", + "property2": "value2" + }}, + "customDisplayName": "Logical name describing function" + }} + ], + "linksToAdd": [ + {{ + "linkID": "link-UUID", + "source": {{ + "operatorID": "SourceOperatorID", + "portID": "output-N" + }}, + "target": {{ + "operatorID": "TargetOperatorID", + "portID": "input-N" + }} + }} + ], + "operatorsToDelete": ["OperatorID1", "OperatorID2"], + "operatorPropertiesToChange": [ + {{ + "operatorID": "ExistingOperatorID", + "properties": {{ + "propertyToChange": "newValue" + }} + }} + ] + }} +}} + +Output all suggestions as a valid JSON array and ensure each suggestion follows the exact format above. +Be sure to only generate valid JSON in your response.""" \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/base.py b/core/suggestion-service/llm_agent/base.py new file mode 100644 index 00000000000..b44c9f3d494 --- /dev/null +++ b/core/suggestion-service/llm_agent/base.py @@ -0,0 +1,75 @@ +"""Base LLM agent interface""" +from abc import ABC, abstractmethod +from typing import Dict, Any, List, Optional + + +class LLMAgent(ABC): + """ + Abstract base class for LLM agents that generate workflow suggestions. + All LLM providers (OpenAI, Anthropic, etc.) should implement this interface. + """ + + @abstractmethod + def generate_suggestions(self, + prompt: str, + max_suggestions: int = 3, + temperature: float = 0.7, + max_tokens: Optional[int] = None, + **kwargs) -> List[Dict[str, Any]]: + """ + Generate workflow suggestions using the LLM provider. + + Args: + prompt: The natural language prompt describing the workflow + max_suggestions: Maximum number of suggestions to generate + temperature: Sampling temperature (0.0-1.0) where lower is more deterministic + max_tokens: Maximum number of tokens to generate + **kwargs: Additional provider-specific parameters + + Returns: + A list of suggestion dictionaries with the following format: + { + "suggestion": str, # Natural language description of the suggestion + "changes": { + "operatorsToAdd": List[Dict], # Operators to add to the workflow + "linksToAdd": List[Dict], # Links to add between operators + "operatorsToDelete": List[str], # Operator IDs to delete + "operatorPropertiesToChange": List[Dict], # Properties to change on existing operators + } + } + """ + pass + + +class LLMAgentFactory: + """Factory for creating LLM agents based on provider name.""" + + _registry = {} + + @classmethod + def register(cls, provider_name: str): + """Register an LLM agent class with the factory.""" + def inner_wrapper(wrapped_class): + cls._registry[provider_name] = wrapped_class + return wrapped_class + return inner_wrapper + + @classmethod + def create(cls, provider_name: str, **kwargs) -> LLMAgent: + """ + Create an instance of the requested LLM agent. + + Args: + provider_name: Name of the LLM provider + **kwargs: Parameters to pass to the LLM agent constructor + + Returns: + An instance of the requested LLM agent + + Raises: + ValueError: If the provider is not registered + """ + if provider_name not in cls._registry: + raise ValueError(f"Unknown LLM provider: {provider_name}") + + return cls._registry[provider_name](**kwargs) \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/openai_agent.py b/core/suggestion-service/llm_agent/openai_agent.py new file mode 100644 index 00000000000..17c485eaa08 --- /dev/null +++ b/core/suggestion-service/llm_agent/openai_agent.py @@ -0,0 +1,135 @@ +"""OpenAI LLM agent.""" +import os +import json +from typing import Dict, Any, List, Optional + +import openai +from openai import OpenAI + +from llm_agent.base import LLMAgent, LLMAgentFactory +from output_formatter.formatter import format_raw_suggestions + + +@LLMAgentFactory.register("openai") +class OpenAIAgent(LLMAgent): + """ + Implementation of the LLM agent interface using OpenAI's API. + + This agent supports different model types from OpenAI, such as: + - gpt-4-turbo-preview + - gpt-4 + - gpt-3.5-turbo + """ + + def __init__(self, + model: str = "gpt-4-turbo-preview", + api_key: Optional[str] = None): + """ + Initialize the OpenAI agent. + + Args: + model: The OpenAI model to use + api_key: The OpenAI API key (if None, uses OPENAI_API_KEY environment variable) + """ + self.model = model + self.client = OpenAI(api_key=api_key or os.environ.get("OPENAI_API_KEY")) + + def generate_suggestions(self, + prompt: str, + max_suggestions: int = 3, + temperature: float = 0.7, + max_tokens: Optional[int] = None, + **kwargs) -> List[Dict[str, Any]]: + """ + Generate workflow suggestions using OpenAI's API. + + Args: + prompt: The natural language prompt describing the workflow + max_suggestions: Maximum number of suggestions to generate + temperature: Sampling temperature (0.0-1.0) where lower is more deterministic + max_tokens: Maximum number of tokens to generate + **kwargs: Additional OpenAI-specific parameters + + Returns: + A list of suggestion dictionaries formatted according to the interface + """ + # Enhance the prompt with instruction about the output format + system_prompt = self._create_system_prompt(max_suggestions) + + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": prompt} + ], + temperature=temperature, + max_tokens=max_tokens, + n=1, # Generate a single completion + **kwargs + ) + + # Extract content from the response + raw_content = response.choices[0].message.content + + # Parse and format the suggestions + suggestions = format_raw_suggestions(raw_content) + + return suggestions[:max_suggestions] + + except Exception as e: + print(f"Error generating suggestions with OpenAI: {str(e)}") + return [] + + def _create_system_prompt(self, max_suggestions: int) -> str: + """Create the system prompt for the OpenAI model.""" + return f"""You are an AI assistant that helps users improve their Texera workflows by suggesting useful modifications. + +Analyze the provided workflow description and generate {max_suggestions} suggestions to improve it. +Your suggestions should address common issues, optimizations, or additional useful features. + +For each suggestion, provide: +1. A brief natural language description of the recommendation +2. Detailed changes required to implement the suggestion + +Structure each suggestion as a valid JSON object with the following format: +{{ + "suggestion": "A clear description of the improvement", + "changes": {{ + "operatorsToAdd": [ + {{ + "operatorType": "TypeOfOperator", + "operatorID": "OperatorType-operator-UUID", + "operatorProperties": {{ + "property1": "value1", + "property2": "value2" + }}, + "customDisplayName": "Logical name describing function" + }} + ], + "linksToAdd": [ + {{ + "linkID": "link-UUID", + "source": {{ + "operatorID": "SourceOperatorID", + "portID": "output-N" + }}, + "target": {{ + "operatorID": "TargetOperatorID", + "portID": "input-N" + }} + }} + ], + "operatorsToDelete": ["OperatorID1", "OperatorID2"], + "operatorPropertiesToChange": [ + {{ + "operatorID": "ExistingOperatorID", + "properties": {{ + "propertyToChange": "newValue" + }} + }} + ] + }} +}} + +Output all suggestions as a valid JSON array and ensure each suggestion follows the exact format above.""" \ No newline at end of file diff --git a/core/suggestion-service/output_formatter/__init__.py b/core/suggestion-service/output_formatter/__init__.py new file mode 100644 index 00000000000..5e6b1fc27c1 --- /dev/null +++ b/core/suggestion-service/output_formatter/__init__.py @@ -0,0 +1,15 @@ +"""Output formatter for ensuring LLM responses match the expected format.""" + +from output_formatter.formatter import ( + format_raw_suggestions, + extract_json_from_llm_response, + validate_and_format_suggestion, + create_placeholder_suggestion +) + +__all__ = [ + 'format_raw_suggestions', + 'extract_json_from_llm_response', + 'validate_and_format_suggestion', + 'create_placeholder_suggestion' +] \ No newline at end of file diff --git a/core/suggestion-service/output_formatter/formatter.py b/core/suggestion-service/output_formatter/formatter.py new file mode 100644 index 00000000000..ef49af9b3f4 --- /dev/null +++ b/core/suggestion-service/output_formatter/formatter.py @@ -0,0 +1,156 @@ +"""Formatter for LLM outputs to ensure they match the expected format.""" +import json +import re +import uuid +from typing import List, Dict, Any, Optional, Tuple + + +def format_raw_suggestions(raw_content: str) -> List[Dict[str, Any]]: + """ + Format raw LLM output into structured suggestions. + + Args: + raw_content: Raw string output from an LLM + + Returns: + List of formatted suggestion dictionaries + """ + try: + # Try to extract JSON from the response + suggestions = extract_json_from_llm_response(raw_content) + + # Validate and format each suggestion + formatted_suggestions = [] + for suggestion in suggestions: + formatted = validate_and_format_suggestion(suggestion) + if formatted: + formatted_suggestions.append(formatted) + + return formatted_suggestions + + except Exception as e: + print(f"Error formatting suggestions: {str(e)}") + return [] + + +def extract_json_from_llm_response(response: str) -> List[Dict[str, Any]]: + """ + Extract JSON from an LLM response. + + Args: + response: Raw string output from an LLM + + Returns: + List of suggestion dictionaries + + Raises: + ValueError: If no valid JSON can be extracted + """ + # Try to find JSON in the response using regex + json_matches = re.findall(r'```json\n([\s\S]*?)\n```|(? Optional[Dict[str, Any]]: + """ + Validate and format a single suggestion. + + Args: + suggestion: Dictionary containing a suggestion + + Returns: + Formatted suggestion or None if validation fails + """ + # Check required fields + if "suggestion" not in suggestion: + suggestion["suggestion"] = "Unnamed suggestion" + + # Ensure changes field exists + if "changes" not in suggestion: + suggestion["changes"] = {} + + # Initialize changes fields if missing + changes = suggestion["changes"] + for field in ["operatorsToAdd", "linksToAdd", "operatorsToDelete", "operatorPropertiesToChange"]: + if field not in changes: + changes[field] = [] + + # Generate UUIDs for operators and links if missing + for operator in changes["operatorsToAdd"]: + if "operatorID" not in operator or not operator["operatorID"]: + op_type = operator.get("operatorType", "Unknown") + operator["operatorID"] = f"{op_type}-operator-{str(uuid.uuid4())}" + + # Ensure operator properties exists + if "operatorProperties" not in operator: + operator["operatorProperties"] = {} + + # Generate UUIDs for links if missing + for link in changes["linksToAdd"]: + if "linkID" not in link or not link["linkID"]: + link["linkID"] = f"link-{str(uuid.uuid4())}" + + # Validate operatorsToDelete is a list of strings + if not isinstance(changes["operatorsToDelete"], list): + changes["operatorsToDelete"] = [] + + return suggestion + + +def create_placeholder_suggestion() -> Dict[str, Any]: + """ + Create a placeholder suggestion when LLM generation fails. + + Returns: + A placeholder suggestion + """ + return { + "suggestion": "Could not generate a valid suggestion", + "changes": { + "operatorsToAdd": [], + "linksToAdd": [], + "operatorsToDelete": [], + "operatorPropertiesToChange": [] + } + } \ No newline at end of file diff --git a/core/suggestion-service/requirements.txt b/core/suggestion-service/requirements.txt index 320ba234286..ceec8860c1d 100644 --- a/core/suggestion-service/requirements.txt +++ b/core/suggestion-service/requirements.txt @@ -8,4 +8,9 @@ uvicorn==0.23.2 pydantic==2.1.1 # Utility libraries -typing-extensions==4.8.0 # Enhanced typing support \ No newline at end of file +typing-extensions==4.8.0 # Enhanced typing support +python-dotenv==1.0.0 # For loading environment variables + +# LLM API clients +openai==1.5.0 # OpenAI API client +anthropic==0.6.0 # Anthropic API client \ No newline at end of file diff --git a/core/suggestion-service/suggestion_engine/generator.py b/core/suggestion-service/suggestion_engine/generator.py index 47f151dd886..22550e059af 100644 --- a/core/suggestion-service/suggestion_engine/generator.py +++ b/core/suggestion-service/suggestion_engine/generator.py @@ -1,10 +1,17 @@ from typing import Dict, List, Any, Optional import json import uuid +import os +from dotenv import load_dotenv from workflow_interpretation.interpreter import WorkflowInterpreter, InterpretationMethod from model.Tuple import Tuple from model.DataSchema import DataSchema, Attribute, AttributeType +from llm_agent.base import LLMAgentFactory + + +# Load environment variables from .env file if present +load_dotenv() class SuggestionGenerator: @@ -13,18 +20,54 @@ class SuggestionGenerator: based on the current workflow state, compilation information, and result data. """ - def __init__(self): + def __init__(self, + llm_provider: str = None, + llm_model: str = None, + llm_api_key: str = None): """ Initialize the suggestion generator. + + Args: + llm_provider: The LLM provider to use (defaults to environment variable LLM_PROVIDER) + llm_model: The LLM model to use (defaults to environment variable LLM_MODEL) + llm_api_key: The API key for the LLM provider (defaults to environment variable based on provider) """ self.workflow_interpreter = WorkflowInterpreter() + # Set default LLM provider and model from environment variables + default_provider = os.environ.get("LLM_PROVIDER", "openai") + self.llm_provider = llm_provider or default_provider + + # Set default model based on provider + if self.llm_provider == "openai": + default_model = os.environ.get("OPENAI_MODEL", "gpt-4-turbo-preview") + elif self.llm_provider == "anthropic": + default_model = os.environ.get("ANTHROPIC_MODEL", "claude-3-opus-20240229") + else: + default_model = "" + + self.llm_model = llm_model or default_model + self.llm_api_key = llm_api_key + + # Create the LLM agent + try: + self.llm_agent = LLMAgentFactory.create( + self.llm_provider, + model=self.llm_model, + api_key=self.llm_api_key + ) + except ValueError as e: + print(f"Error creating LLM agent: {str(e)}") + # Fall back to mock suggestions if agent creation fails + self.llm_agent = None + def generate_suggestions( self, workflow_json: Dict[str, Any], compilation_state: Dict[str, Any], result_tables: Dict[str, Dict[str, Any]], - execution_state: Optional[Dict[str, Any]] = None + execution_state: Optional[Dict[str, Any]] = None, + max_suggestions: int = 3 ) -> List[Dict[str, Any]]: """ Generate workflow suggestions based on the current workflow, compilation state, execution state, and result tables. @@ -34,6 +77,7 @@ def generate_suggestions( compilation_state: Compilation information and errors result_tables: Result data for each operator execution_state: Current execution state of the workflow + max_suggestions: Maximum number of suggestions to generate Returns: A list of workflow suggestions @@ -55,6 +99,195 @@ def generate_suggestions( print("Generated workflow description:") print(workflow_description) + # If we have a valid LLM agent, use it to generate suggestions + if self.llm_agent: + try: + # Add context to the workflow description about compilation and execution state + enriched_prompt = self._enhance_prompt_with_state_info( + workflow_description, + compilation_state, + execution_state + ) + + # Get suggestions from the LLM agent + suggestions = self.llm_agent.generate_suggestions( + prompt=enriched_prompt, + max_suggestions=max_suggestions, + temperature=0.7 # Lower temperature for more focused suggestions + ) + + # Convert suggestions to the expected format for the frontend + formatted_suggestions = self._convert_to_frontend_format(suggestions) + + # Return generated suggestions (if any were generated) + if formatted_suggestions: + return formatted_suggestions + except Exception as e: + print(f"Error generating suggestions with LLM: {str(e)}") + # Fall back to mock suggestions on error + + # If LLM generation failed or agent is not available, return mock suggestions + return self._generate_mock_suggestions(workflow_json, compilation_state, execution_state) + + def _generate_workflow_prompt( + self, + workflow_json: Dict[str, Any], + input_schema: Optional[Dict[str, Any]] = None, + operator_errors: Optional[Dict[str, Any]] = None, + method: InterpretationMethod = InterpretationMethod.BY_PATH + ) -> str: + """ + Generate a natural language description of the workflow for use in prompts. + + Args: + workflow_json: The workflow dictionary + input_schema: The input schema dictionary for each operator + operator_errors: Dictionary of static errors for each operator + method: The interpretation method to use + + Returns: + A natural language description of the workflow + """ + try: + # Use the workflow interpreter to generate a description + description = self.workflow_interpreter.interpret_workflow( + workflow_json, + input_schema, + operator_errors, + method + ) + + return description + except Exception as e: + print(f"Error generating workflow prompt: {str(e)}") + # Fallback to a simple description if interpretation fails + return f"Workflow with {len(workflow_json.get('content', {}).get('operators', []))} operators" + + def _enhance_prompt_with_state_info( + self, + workflow_description: str, + compilation_state: Dict[str, Any], + execution_state: Optional[Dict[str, Any]] + ) -> str: + """ + Enhance the workflow description with compilation and execution state information. + + Args: + workflow_description: Natural language description of the workflow + compilation_state: Compilation information and errors + execution_state: Current execution state of the workflow + + Returns: + Enhanced workflow description + """ + prompt = workflow_description + "\n\n" + + # Add compilation state info + prompt += f"Compilation State: {compilation_state['state']}\n" + + # Add compilation errors if any + if compilation_state['state'] == "Failed" and compilation_state.get('operatorErrors'): + prompt += "Compilation Errors:\n" + for op_id, error in compilation_state['operatorErrors'].items(): + if error: + prompt += f"- Operator {op_id}: {error}\n" + + # Add execution state info if available + if execution_state: + prompt += f"\nExecution State: {execution_state['state']}\n" + + # Add execution errors if any + if execution_state['state'] == "Failed" and execution_state.get('errorMessages'): + prompt += "Execution Errors:\n" + for error in execution_state['errorMessages']: + prompt += f"- {error}\n" + + # Add final instruction + prompt += "\nBased on this workflow description and state information, suggest improvements or fixes." + + return prompt + + def _convert_to_frontend_format(self, suggestions: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + """ + Convert LLM-generated suggestions to the format expected by the frontend. + + Args: + suggestions: List of suggestions from the LLM agent + + Returns: + List of suggestions formatted for the frontend + """ + formatted_suggestions = [] + + for suggestion in suggestions: + # Create a new suggestion with the required format + formatted = { + "id": f"suggestion-{uuid.uuid4()}", + "description": suggestion["suggestion"], + "operatorsToAdd": [], + "operatorPropertiesToChange": [], + "operatorsToDelete": suggestion["changes"].get("operatorsToDelete", []), + "linksToAdd": [], + "isPreviewActive": False + } + + # Format operators to add + for operator in suggestion["changes"].get("operatorsToAdd", []): + formatted_operator = { + "operatorType": operator["operatorType"], + "position": {"x": 400, "y": 300}, # Default position + "properties": operator.get("operatorProperties", {}) + } + + # Add custom display name if provided + if operator.get("customDisplayName"): + formatted_operator["customDisplayName"] = operator["customDisplayName"] + + formatted["operatorsToAdd"].append(formatted_operator) + + # Format operator properties to change + for prop_change in suggestion["changes"].get("operatorPropertiesToChange", []): + formatted_prop_change = { + "operatorId": prop_change["operatorID"], + "properties": prop_change["properties"] + } + formatted["operatorPropertiesToChange"].append(formatted_prop_change) + + # Format links to add + for link in suggestion["changes"].get("linksToAdd", []): + formatted_link = { + "source": { + "operatorId": link["source"]["operatorID"], + "portId": link["source"]["portID"] + }, + "target": { + "operatorId": link["target"]["operatorID"], + "portId": link["target"]["portID"] + } + } + formatted["linksToAdd"].append(formatted_link) + + formatted_suggestions.append(formatted) + + return formatted_suggestions + + def _generate_mock_suggestions( + self, + workflow_json: Dict[str, Any], + compilation_state: Dict[str, Any], + execution_state: Optional[Dict[str, Any]] + ) -> List[Dict[str, Any]]: + """ + Generate mock suggestions for testing or when LLM generation fails. + + Args: + workflow_json: The current workflow configuration + compilation_state: Compilation information and errors + execution_state: Current execution state of the workflow + + Returns: + A list of mock workflow suggestions + """ # Extract operators from the workflow operators = workflow_json.get("content", {}).get("operators", []) @@ -202,38 +435,4 @@ def generate_suggestions( } suggestions.append(suggestion2) - return suggestions - - def _generate_workflow_prompt( - self, - workflow_json: Dict[str, Any], - input_schema: Optional[Dict[str, Any]] = None, - operator_errors: Optional[Dict[str, Any]] = None, - method: InterpretationMethod = InterpretationMethod.BY_PATH - ) -> str: - """ - Generate a natural language description of the workflow for use in prompts. - - Args: - workflow_json: The workflow dictionary - input_schema: The input schema dictionary for each operator - operator_errors: Dictionary of static errors for each operator - method: The interpretation method to use - - Returns: - A natural language description of the workflow - """ - try: - # Use the workflow interpreter to generate a description - description = self.workflow_interpreter.interpret_workflow( - workflow_json, - input_schema, - operator_errors, - method - ) - - return description - except Exception as e: - print(f"Error generating workflow prompt: {str(e)}") - # Fallback to a simple description if interpretation fails - return f"Workflow with {len(workflow_json.get('content', {}).get('operators', []))} operators" \ No newline at end of file + return suggestions \ No newline at end of file diff --git a/core/suggestion-service/test/results/workflow1/by_path_interpretation.txt b/core/suggestion-service/test/results/workflow1/by_path_interpretation.txt index 087b9193eb7..bdeb0ab3cfa 100644 --- a/core/suggestion-service/test/results/workflow1/by_path_interpretation.txt +++ b/core/suggestion-service/test/results/workflow1/by_path_interpretation.txt @@ -1,77 +1,35 @@ -Here are the existing paths in this workflow and related schemas: +This workflow contains the following execution paths: Path 1: - 1. CSVFileScan with properties: - - fileEncoding: UTF_8 - - customDelimiter: , - - hasHeader: True - - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv - Schema: - [] - → Connected to Projection-operator-9d955369-5e4d-44f9-a6f3-ec66b1192894 via ports output-0 → input-0 - 2. Projection with properties: - - isDrop: False - - attributes: [{'alias': '', 'originalAttribute': 'tweet_id'}, {'alias': '', 'originalAttribute': 'create_at_month'}, {'alias': '', 'originalAttribute': 'favorite_count'}, {'alias': '', 'originalAttribute': 'retweet_count'}] - Schema: - [ - [ - { - "attributeName": "tweet_id", - "attributeType": "integer" - }, - { - "attributeName": "create_at_month", - "attributeType": "timestamp" - }, - { - "attributeName": "favorite_count", - "attributeType": "integer" - }, - { - "attributeName": "retweet_count", - "attributeType": "integer" - }, - { - "attributeName": "is_retweet", - "attributeType": "boolean" - }, - { - "attributeName": "lang", - "attributeType": "string" - }, - { - "attributeName": "text", - "attributeType": "string" - }, - { - "attributeName": "user_id", - "attributeType": "integer" - } - ] -] - → Connected to Sort-operator-860e9211-556a-4789-ab06-fa79bc0b36c3 via ports output-0 → input-0 - 3. Sort with properties: - - attributes: [{'attribute': 'favorite_count', 'sortPreference': 'DESC'}] - Schema: - [ - [ - { - "attributeName": "tweet_id", - "attributeType": "integer" - }, - { - "attributeName": "create_at_month", - "attributeType": "timestamp" - }, - { - "attributeName": "favorite_count", - "attributeType": "integer" - }, - { - "attributeName": "retweet_count", - "attributeType": "integer" - } - ] -] - +- CSV File Scan (CSVFileScan) + Properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Connects from port 'output-0' to 'Projection' port 'input-0' +- Projection (Projection) + Properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'tweet_id'}, {'alias': '', 'originalAttribute': 'create_at_month'}, {'alias': '', 'originalAttribute': 'favorite_count'}, {'alias': '', 'originalAttribute': 'retweet_count'}] + Input Schema: + - Schema items: 8 + Item 0: {'attributeName': 'tweet_id', 'attributeType': 'integer'} + Item 1: {'attributeName': 'create_at_month', 'attributeType': 'timestamp'} + Item 2: {'attributeName': 'favorite_count', 'attributeType': 'integer'} + Item 3: {'attributeName': 'retweet_count', 'attributeType': 'integer'} + Item 4: {'attributeName': 'is_retweet', 'attributeType': 'boolean'} + Item 5: {'attributeName': 'lang', 'attributeType': 'string'} + Item 6: {'attributeName': 'text', 'attributeType': 'string'} + Item 7: {'attributeName': 'user_id', 'attributeType': 'integer'} + Connects from port 'output-0' to 'Sort' port 'input-0' +- Sort (Sort) + Properties: + - attributes: [{'attribute': 'favorite_count', 'sortPreference': 'DESC'}] + Input Schema: + - Schema items: 4 + Item 0: {'attributeName': 'tweet_id', 'attributeType': 'integer'} + Item 1: {'attributeName': 'create_at_month', 'attributeType': 'timestamp'} + Item 2: {'attributeName': 'favorite_count', 'attributeType': 'integer'} + Item 3: {'attributeName': 'retweet_count', 'attributeType': 'integer'} diff --git a/core/suggestion-service/test/results/workflow1/suggestions.json b/core/suggestion-service/test/results/workflow1/suggestions.json index 5abf9b84388..3275738f478 100644 --- a/core/suggestion-service/test/results/workflow1/suggestions.json +++ b/core/suggestion-service/test/results/workflow1/suggestions.json @@ -1,6 +1,6 @@ [ { - "id": "suggestion-b74b4daf-454f-4303-8fdf-b6d2dc4da305", + "id": "suggestion-f9690f43-4055-4181-8356-2fa09ec191b6", "description": "Add a KeywordSearch operator with sentiment analysis", "operatorsToAdd": [ { @@ -74,7 +74,7 @@ "isPreviewActive": false }, { - "id": "suggestion-67ab18f9-7442-4556-801c-580afd2a0415", + "id": "suggestion-57e154a4-4403-44b2-9794-a601b384e7d0", "description": "Replace ScanSource with CSVFileScan for better performance", "operatorsToAdd": [ { diff --git a/core/suggestion-service/test/results/workflow2/by_path_interpretation.txt b/core/suggestion-service/test/results/workflow2/by_path_interpretation.txt index 80b5418843d..044dc68cbf3 100644 --- a/core/suggestion-service/test/results/workflow2/by_path_interpretation.txt +++ b/core/suggestion-service/test/results/workflow2/by_path_interpretation.txt @@ -1,132 +1,64 @@ -Here are the existing paths in this workflow and related schemas: +This workflow contains the following execution paths: Path 1: - 1. CSVFileScan with properties: - - fileEncoding: UTF_8 - - customDelimiter: , - - hasHeader: True - - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv - Schema: - [] - → Connected to Projection-operator-b6799195-ecc4-426a-8e29-57e9248aa71f via ports output-0 → input-0 - 2. Projection with properties: - - isDrop: False - - attributes: [{'alias': '', 'originalAttribute': 'tweet_id'}, {'alias': '', 'originalAttribute': 'create_at_month'}, {'alias': '', 'originalAttribute': 'favorite_count'}, {'alias': '', 'originalAttribute': 'retweet_count'}] - Schema: - [ - [ - { - "attributeName": "tweet_id", - "attributeType": "integer" - }, - { - "attributeName": "create_at_month", - "attributeType": "timestamp" - }, - { - "attributeName": "favorite_count", - "attributeType": "integer" - }, - { - "attributeName": "retweet_count", - "attributeType": "integer" - }, - { - "attributeName": "is_retweet", - "attributeType": "boolean" - }, - { - "attributeName": "lang", - "attributeType": "string" - }, - { - "attributeName": "text", - "attributeType": "string" - }, - { - "attributeName": "user_id", - "attributeType": "integer" - } - ] -] - → Connected to Sort-operator-82fa7c7a-d1e1-45a6-8510-1f57c223e260 via ports output-0 → input-0 - 3. Sort with properties: - - attributes: [{'attribute': 'favorite_count', 'sortPreference': 'DESC'}] - Schema: - [ - [ - { - "attributeName": "tweet_id", - "attributeType": "integer" - }, - { - "attributeName": "create_at_month", - "attributeType": "timestamp" - }, - { - "attributeName": "favorite_count", - "attributeType": "integer" - }, - { - "attributeName": "retweet_count", - "attributeType": "integer" - } - ] -] - +- CSV File Scan (CSVFileScan) + Properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Connects from port 'output-0' to 'Projection' port 'input-0' +- Projection (Projection) + Properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'tweet_id'}, {'alias': '', 'originalAttribute': 'create_at_month'}, {'alias': '', 'originalAttribute': 'favorite_count'}, {'alias': '', 'originalAttribute': 'retweet_count'}] + Input Schema: + - Schema items: 8 + Item 0: {'attributeName': 'tweet_id', 'attributeType': 'integer'} + Item 1: {'attributeName': 'create_at_month', 'attributeType': 'timestamp'} + Item 2: {'attributeName': 'favorite_count', 'attributeType': 'integer'} + Item 3: {'attributeName': 'retweet_count', 'attributeType': 'integer'} + Item 4: {'attributeName': 'is_retweet', 'attributeType': 'boolean'} + Item 5: {'attributeName': 'lang', 'attributeType': 'string'} + Item 6: {'attributeName': 'text', 'attributeType': 'string'} + Item 7: {'attributeName': 'user_id', 'attributeType': 'integer'} + Connects from port 'output-0' to 'Sort' port 'input-0' +- Sort (Sort) + Properties: + - attributes: [{'attribute': 'favorite_count', 'sortPreference': 'DESC'}] + Input Schema: + - Schema items: 4 + Item 0: {'attributeName': 'tweet_id', 'attributeType': 'integer'} + Item 1: {'attributeName': 'create_at_month', 'attributeType': 'timestamp'} + Item 2: {'attributeName': 'favorite_count', 'attributeType': 'integer'} + Item 3: {'attributeName': 'retweet_count', 'attributeType': 'integer'} Path 2: - 1. CSVFileScan with properties: - - fileEncoding: UTF_8 - - customDelimiter: , - - hasHeader: True - - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv - Schema: - [] - → Connected to Projection-operator-5c723ccd-5f1a-4a60-8e0b-bc3cb7cbc8ac via ports output-0 → input-0 - 2. Projection with properties: - - isDrop: False - - attributes: [{'alias': '', 'originalAttribute': 'text'}] - Schema: - [ - [ - { - "attributeName": "tweet_id", - "attributeType": "integer" - }, - { - "attributeName": "create_at_month", - "attributeType": "timestamp" - }, - { - "attributeName": "favorite_count", - "attributeType": "integer" - }, - { - "attributeName": "retweet_count", - "attributeType": "integer" - }, - { - "attributeName": "is_retweet", - "attributeType": "boolean" - }, - { - "attributeName": "lang", - "attributeType": "string" - }, - { - "attributeName": "text", - "attributeType": "string" - }, - { - "attributeName": "user_id", - "attributeType": "integer" - } - ] -] - → Connected to PythonUDFV2-operator-29189f24-4d27-413f-9b67-1c8b37529289 via ports output-0 → input-0 - 3. PythonUDFV2 with properties: - - code: # Choose from the following templates: +- CSV File Scan (CSVFileScan) + Properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Connects from port 'output-0' to 'Projection' port 'input-0' +- Projection (Projection) + Properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'text'}] + Input Schema: + - Schema items: 8 + Item 0: {'attributeName': 'tweet_id', 'attributeType': 'integer'} + Item 1: {'attributeName': 'create_at_month', 'attributeType': 'timestamp'} + Item 2: {'attributeName': 'favorite_count', 'attributeType': 'integer'} + Item 3: {'attributeName': 'retweet_count', 'attributeType': 'integer'} + Item 4: {'attributeName': 'is_retweet', 'attributeType': 'boolean'} + Item 5: {'attributeName': 'lang', 'attributeType': 'string'} + Item 6: {'attributeName': 'text', 'attributeType': 'string'} + Item 7: {'attributeName': 'user_id', 'attributeType': 'integer'} + Connects from port 'output-0' to 'Python UDF' port 'input-0' +- Python UDF (PythonUDFV2) + Properties: + - code: # Choose from the following templates: # from pytexera import * @@ -150,17 +82,10 @@ class ProcessTupleOperator(UDFOperatorV2): # def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]: # yield table - - workers: 1 - - retainInputColumns: True - - outputColumns: [{'attributeName': 'text_length', 'attributeType': 'integer'}] - Schema: - [ - [ - { - "attributeName": "text", - "attributeType": "string" - } - ] -] - + - workers: 1 + - retainInputColumns: True + - outputColumns: [{'attributeName': 'text_length', 'attributeType': 'integer'}] + Input Schema: + - Schema items: 1 + Item 0: {'attributeName': 'text', 'attributeType': 'string'} diff --git a/core/suggestion-service/test/results/workflow2/suggestions.json b/core/suggestion-service/test/results/workflow2/suggestions.json index d302a1fc9de..5fcd7a0ccac 100644 --- a/core/suggestion-service/test/results/workflow2/suggestions.json +++ b/core/suggestion-service/test/results/workflow2/suggestions.json @@ -1,6 +1,6 @@ [ { - "id": "suggestion-eb0a5d8f-505b-4e5c-86c5-172bd68415c2", + "id": "suggestion-7b079311-f23c-4066-b3ce-961bbe53b33c", "description": "Add a KeywordSearch operator with sentiment analysis", "operatorsToAdd": [ { @@ -74,7 +74,7 @@ "isPreviewActive": false }, { - "id": "suggestion-c6293a99-27fa-44cf-8726-fb90ca13cc82", + "id": "suggestion-aeb12d91-6463-430c-8464-358334422bfc", "description": "Replace ScanSource with CSVFileScan for better performance", "operatorsToAdd": [ { diff --git a/core/suggestion-service/test/results/workflow3/by_path_interpretation.txt b/core/suggestion-service/test/results/workflow3/by_path_interpretation.txt index 558f7d4a876..a36f0c0a140 100644 --- a/core/suggestion-service/test/results/workflow3/by_path_interpretation.txt +++ b/core/suggestion-service/test/results/workflow3/by_path_interpretation.txt @@ -1,179 +1,76 @@ -Here are the existing paths in this workflow and related schemas: +This workflow contains the following execution paths: Path 1: - 1. CSVFileScan with properties: - - fileEncoding: UTF_8 - - customDelimiter: , - - hasHeader: True - - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv - Schema: - [] - → Connected to Projection-operator-fa6c76fa-3208-4f20-9b44-f920b91e898c via ports output-0 → input-0 - 2. Projection with properties: - - isDrop: False - - attributes: [{'alias': '', 'originalAttribute': 'tweet_id'}, {'alias': '', 'originalAttribute': 'create_at_month'}, {'alias': '', 'originalAttribute': 'favorite_count'}, {'alias': '', 'originalAttribute': 'retweet_count'}] - Schema: - [ - [ - { - "attributeName": "tweet_id", - "attributeType": "integer" - }, - { - "attributeName": "create_at_month", - "attributeType": "timestamp" - }, - { - "attributeName": "favorite_count", - "attributeType": "integer" - }, - { - "attributeName": "retweet_count", - "attributeType": "integer" - }, - { - "attributeName": "is_retweet", - "attributeType": "boolean" - }, - { - "attributeName": "lang", - "attributeType": "string" - }, - { - "attributeName": "text", - "attributeType": "string" - }, - { - "attributeName": "user_id", - "attributeType": "integer" - } - ] -] - → Connected to Sort-operator-3985eaf1-5af2-4f4a-bd0f-1c4b3f7e78c2 via ports output-0 → input-0 - 3. Sort with properties: - - attributes: [{'attribute': 'favorite_count', 'sortPreference': 'DESC'}] - Schema: - [ - [ - { - "attributeName": "tweet_id", - "attributeType": "integer" - }, - { - "attributeName": "create_at_month", - "attributeType": "timestamp" - }, - { - "attributeName": "favorite_count", - "attributeType": "integer" - }, - { - "attributeName": "retweet_count", - "attributeType": "integer" - } - ] -] - → Connected to LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865 via ports output-0 → input-0 - 4. LineChart with properties: - - yLabel: Y Axis - - xLabel: X Axis - Schema: - [ - [ - { - "attributeName": "tweet_id", - "attributeType": "integer" - }, - { - "attributeName": "create_at_month", - "attributeType": "timestamp" - }, - { - "attributeName": "favorite_count", - "attributeType": "integer" - }, - { - "attributeName": "retweet_count", - "attributeType": "integer" - } - ] -] - Errors: - { - "type": { - "value": 0, - "index": 0, - "name": "COMPILATION_ERROR", - "compilationError": true, - "executionFailure": false, - "unrecognized": false - }, - "timestamp": { - "seconds": 1744853005, - "nanos": 636787000, - "unknownFields": { - "fields": {} - } - }, - "message": "java.lang.RuntimeException: Operator is not configured properly: null", - "details": "Stack trace for developers: \n\njava.lang.RuntimeException: Operator is not configured properly: null\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$9(WorkflowCompiler.scala:149)\nscala.Option.foreach(Option.scala:437)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3(WorkflowCompiler.scala:146)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3$adapted(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$1(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.expandLogicalPlan(WorkflowCompiler.scala:109)\nedu.uci.ics.amber.compiler.WorkflowCompiler.compile(WorkflowCompiler.scala:188)\nedu.uci.ics.texera.service.resource.WorkflowCompilationResource.compileWorkflow(WorkflowCompilationResource.scala:52)\njdk.internal.reflect.GeneratedMethodAccessor44.invoke(Unknown Source)\njava.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\njava.base/java.lang.reflect.Method.invoke(Method.java:566)\norg.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:146)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:189)\norg.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:93)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:478)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:400)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)\norg.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:256)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:248)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:244)\norg.glassfish.jersey.internal.Errors.process(Errors.java:292)\norg.glassfish.jersey.internal.Errors.process(Errors.java:274)\norg.glassfish.jersey.internal.Errors.process(Errors.java:244)\norg.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)\norg.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:235)\norg.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684)\norg.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)\norg.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:358)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:311)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)\norg.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)\norg.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)\nio.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:36)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.handle(AllowedMethodsFilter.java:46)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.doFilter(AllowedMethodsFilter.java:40)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\norg.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)\norg.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)\norg.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)\norg.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)\norg.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)\norg.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\nio.dropwizard.metrics.jetty11.InstrumentedHandler.handle(InstrumentedHandler.java:313)\nio.dropwizard.jetty.RoutingHandler.handle(RoutingHandler.java:52)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:822)\nio.dropwizard.jetty.ZipExceptionHandlingGzipHandler.handle(ZipExceptionHandlingGzipHandler.java:26)\norg.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:46)\norg.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:173)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.Server.handle(Server.java:563)\norg.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)\norg.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)\norg.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)\norg.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)\norg.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)\norg.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)\norg.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)\norg.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)\norg.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)\njava.base/java.lang.Thread.run(Thread.java:829)", - "operatorId": "LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865", - "workerId": "" -} - +- CSV File Scan (CSVFileScan) + Properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Connects from port 'output-0' to 'Projection' port 'input-0' +- Projection (Projection) + Properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'tweet_id'}, {'alias': '', 'originalAttribute': 'create_at_month'}, {'alias': '', 'originalAttribute': 'favorite_count'}, {'alias': '', 'originalAttribute': 'retweet_count'}] + Input Schema: + - Schema items: 8 + Item 0: {'attributeName': 'tweet_id', 'attributeType': 'integer'} + Item 1: {'attributeName': 'create_at_month', 'attributeType': 'timestamp'} + Item 2: {'attributeName': 'favorite_count', 'attributeType': 'integer'} + Item 3: {'attributeName': 'retweet_count', 'attributeType': 'integer'} + Item 4: {'attributeName': 'is_retweet', 'attributeType': 'boolean'} + Item 5: {'attributeName': 'lang', 'attributeType': 'string'} + Item 6: {'attributeName': 'text', 'attributeType': 'string'} + Item 7: {'attributeName': 'user_id', 'attributeType': 'integer'} + Connects from port 'output-0' to 'Sort' port 'input-0' +- Sort (Sort) + Properties: + - attributes: [{'attribute': 'favorite_count', 'sortPreference': 'DESC'}] + Input Schema: + - Schema items: 4 + Item 0: {'attributeName': 'tweet_id', 'attributeType': 'integer'} + Item 1: {'attributeName': 'create_at_month', 'attributeType': 'timestamp'} + Item 2: {'attributeName': 'favorite_count', 'attributeType': 'integer'} + Item 3: {'attributeName': 'retweet_count', 'attributeType': 'integer'} + Connects from port 'output-0' to 'Line Chart' port 'input-0' +- Line Chart (LineChart) + Properties: + - yLabel: Y Axis + - xLabel: X Axis + ERROR: {'type': {'value': 0, 'index': 0, 'name': 'COMPILATION_ERROR', 'compilationError': True, 'executionFailure': False, 'unrecognized': False}, 'timestamp': {'seconds': 1744853005, 'nanos': 636787000, 'unknownFields': {'fields': {}}}, 'message': 'java.lang.RuntimeException: Operator is not configured properly: null', 'details': 'Stack trace for developers: \n\njava.lang.RuntimeException: Operator is not configured properly: null\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$9(WorkflowCompiler.scala:149)\nscala.Option.foreach(Option.scala:437)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3(WorkflowCompiler.scala:146)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$3$adapted(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.$anonfun$expandLogicalPlan$1(WorkflowCompiler.scala:119)\nscala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)\nscala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)\nscala.collection.AbstractIterator.foreach(Iterator.scala:1300)\nedu.uci.ics.amber.compiler.WorkflowCompiler.expandLogicalPlan(WorkflowCompiler.scala:109)\nedu.uci.ics.amber.compiler.WorkflowCompiler.compile(WorkflowCompiler.scala:188)\nedu.uci.ics.texera.service.resource.WorkflowCompilationResource.compileWorkflow(WorkflowCompilationResource.scala:52)\njdk.internal.reflect.GeneratedMethodAccessor44.invoke(Unknown Source)\njava.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\njava.base/java.lang.reflect.Method.invoke(Method.java:566)\norg.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:146)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:189)\norg.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219)\norg.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:93)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:478)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:400)\norg.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)\norg.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:256)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:248)\norg.glassfish.jersey.internal.Errors$1.call(Errors.java:244)\norg.glassfish.jersey.internal.Errors.process(Errors.java:292)\norg.glassfish.jersey.internal.Errors.process(Errors.java:274)\norg.glassfish.jersey.internal.Errors.process(Errors.java:244)\norg.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)\norg.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:235)\norg.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684)\norg.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)\norg.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:358)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:311)\norg.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)\norg.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)\norg.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)\nio.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:36)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.handle(AllowedMethodsFilter.java:46)\nio.dropwizard.jersey.filter.AllowedMethodsFilter.doFilter(AllowedMethodsFilter.java:40)\norg.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)\norg.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)\norg.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)\norg.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)\norg.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)\norg.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)\norg.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)\norg.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)\norg.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\nio.dropwizard.metrics.jetty11.InstrumentedHandler.handle(InstrumentedHandler.java:313)\nio.dropwizard.jetty.RoutingHandler.handle(RoutingHandler.java:52)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:822)\nio.dropwizard.jetty.ZipExceptionHandlingGzipHandler.handle(ZipExceptionHandlingGzipHandler.java:26)\norg.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:46)\norg.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:173)\norg.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\norg.eclipse.jetty.server.Server.handle(Server.java:563)\norg.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)\norg.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)\norg.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)\norg.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)\norg.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)\norg.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)\norg.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)\norg.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)\norg.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)\norg.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)\norg.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)\njava.base/java.lang.Thread.run(Thread.java:829)', 'operatorId': 'LineChart-operator-e3009841-32e4-4080-a6e4-f659762b3865', 'workerId': ''} + Input Schema: + - Schema items: 4 + Item 0: {'attributeName': 'tweet_id', 'attributeType': 'integer'} + Item 1: {'attributeName': 'create_at_month', 'attributeType': 'timestamp'} + Item 2: {'attributeName': 'favorite_count', 'attributeType': 'integer'} + Item 3: {'attributeName': 'retweet_count', 'attributeType': 'integer'} Path 2: - 1. CSVFileScan with properties: - - fileEncoding: UTF_8 - - customDelimiter: , - - hasHeader: True - - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv - Schema: - [] - → Connected to Projection-operator-9e443d52-a201-4f43-88fc-d8ad2cf4eb16 via ports output-0 → input-0 - 2. Projection with properties: - - isDrop: False - - attributes: [{'alias': '', 'originalAttribute': 'text'}] - Schema: - [ - [ - { - "attributeName": "tweet_id", - "attributeType": "integer" - }, - { - "attributeName": "create_at_month", - "attributeType": "timestamp" - }, - { - "attributeName": "favorite_count", - "attributeType": "integer" - }, - { - "attributeName": "retweet_count", - "attributeType": "integer" - }, - { - "attributeName": "is_retweet", - "attributeType": "boolean" - }, - { - "attributeName": "lang", - "attributeType": "string" - }, - { - "attributeName": "text", - "attributeType": "string" - }, - { - "attributeName": "user_id", - "attributeType": "integer" - } - ] -] - → Connected to PythonUDFV2-operator-3e3c9f53-dae3-4dc4-b724-7ffdb8e7b80c via ports output-0 → input-0 - 3. PythonUDFV2 with properties: - - code: # Choose from the following templates: +- CSV File Scan (CSVFileScan) + Properties: + - fileEncoding: UTF_8 + - customDelimiter: , + - hasHeader: True + - fileName: /workflow_migrator@texera/ds4all-lecture-example/v1/clean_tweets.csv + Connects from port 'output-0' to 'Projection' port 'input-0' +- Projection (Projection) + Properties: + - isDrop: False + - attributes: [{'alias': '', 'originalAttribute': 'text'}] + Input Schema: + - Schema items: 8 + Item 0: {'attributeName': 'tweet_id', 'attributeType': 'integer'} + Item 1: {'attributeName': 'create_at_month', 'attributeType': 'timestamp'} + Item 2: {'attributeName': 'favorite_count', 'attributeType': 'integer'} + Item 3: {'attributeName': 'retweet_count', 'attributeType': 'integer'} + Item 4: {'attributeName': 'is_retweet', 'attributeType': 'boolean'} + Item 5: {'attributeName': 'lang', 'attributeType': 'string'} + Item 6: {'attributeName': 'text', 'attributeType': 'string'} + Item 7: {'attributeName': 'user_id', 'attributeType': 'integer'} + Connects from port 'output-0' to 'Python UDF' port 'input-0' +- Python UDF (PythonUDFV2) + Properties: + - code: # Choose from the following templates: # from pytexera import * @@ -197,17 +94,10 @@ class ProcessTupleOperator(UDFOperatorV2): # def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]: # yield table - - workers: 1 - - retainInputColumns: True - - outputColumns: [{'attributeName': 'text_length', 'attributeType': 'integer'}] - Schema: - [ - [ - { - "attributeName": "text", - "attributeType": "string" - } - ] -] - + - workers: 1 + - retainInputColumns: True + - outputColumns: [{'attributeName': 'text_length', 'attributeType': 'integer'}] + Input Schema: + - Schema items: 1 + Item 0: {'attributeName': 'text', 'attributeType': 'string'} diff --git a/core/suggestion-service/test/results/workflow3/suggestions.json b/core/suggestion-service/test/results/workflow3/suggestions.json index 39d4ed0b5ce..8f31b66279c 100644 --- a/core/suggestion-service/test/results/workflow3/suggestions.json +++ b/core/suggestion-service/test/results/workflow3/suggestions.json @@ -1,6 +1,6 @@ [ { - "id": "suggestion-0bf71358-cd0a-474b-b5df-92abf6a28413", + "id": "suggestion-0acd0c27-dbff-47fc-bf1d-74d69c641e56", "description": "Replace ScanSource with CSVFileScan for better performance", "operatorsToAdd": [ { diff --git a/core/suggestion-service/test_llm_agent.py b/core/suggestion-service/test_llm_agent.py new file mode 100644 index 00000000000..1fbb442c79a --- /dev/null +++ b/core/suggestion-service/test_llm_agent.py @@ -0,0 +1,138 @@ +"""Test script for the LLM agent and formatter.""" +import os +import json +import argparse +from dotenv import load_dotenv + +from llm_agent.base import LLMAgentFactory +from output_formatter.formatter import format_raw_suggestions + + +# Load environment variables +load_dotenv() + + +def test_llm_agent(provider, prompt, output_file=None): + """ + Test generating suggestions with an LLM agent. + + Args: + provider: LLM provider (openai or anthropic) + prompt: Natural language prompt to send to the LLM + output_file: Optional file to save the raw output to + """ + try: + # Create LLM agent + agent = LLMAgentFactory.create(provider) + + print(f"Testing {provider} LLM agent...") + print(f"Prompt: {prompt}") + print("-" * 50) + + # Generate suggestions + suggestions = agent.generate_suggestions( + prompt=prompt, + max_suggestions=3, + temperature=0.7 + ) + + # Print suggestions + print(f"Generated {len(suggestions)} suggestions:") + for i, suggestion in enumerate(suggestions): + print(f"\nSuggestion {i+1}: {suggestion['suggestion']}") + print("Changes:") + changes = suggestion["changes"] + print(f" - Operators to add: {len(changes['operatorsToAdd'])}") + print(f" - Links to add: {len(changes['linksToAdd'])}") + print(f" - Operators to delete: {len(changes['operatorsToDelete'])}") + print(f" - Properties to change: {len(changes.get('operatorPropertiesToChange', []))}") + + # Save output to file if requested + if output_file: + with open(output_file, "w") as f: + json.dump(suggestions, f, indent=2) + print(f"\nSaved suggestions to {output_file}") + + return suggestions + + except Exception as e: + print(f"Error testing LLM agent: {str(e)}") + return [] + + +def test_formatter(raw_json_file, output_file=None): + """ + Test the formatter with a raw JSON file. + + Args: + raw_json_file: Path to a file containing raw JSON from an LLM + output_file: Optional file to save the formatted output to + """ + try: + # Load raw JSON + with open(raw_json_file, "r") as f: + raw_content = f.read() + + print(f"Testing formatter with {raw_json_file}...") + print("-" * 50) + + # Format suggestions + suggestions = format_raw_suggestions(raw_content) + + # Print suggestions + print(f"Formatted {len(suggestions)} suggestions:") + for i, suggestion in enumerate(suggestions): + print(f"\nSuggestion {i+1}: {suggestion['suggestion']}") + print("Changes:") + changes = suggestion["changes"] + print(f" - Operators to add: {len(changes['operatorsToAdd'])}") + print(f" - Links to add: {len(changes['linksToAdd'])}") + print(f" - Operators to delete: {len(changes['operatorsToDelete'])}") + print(f" - Properties to change: {len(changes.get('operatorPropertiesToChange', []))}") + + # Save output to file if requested + if output_file: + with open(output_file, "w") as f: + json.dump(suggestions, f, indent=2) + print(f"\nSaved formatted suggestions to {output_file}") + + return suggestions + + except Exception as e: + print(f"Error testing formatter: {str(e)}") + return [] + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Test the LLM agent and formatter") + + # Create subparsers for different commands + subparsers = parser.add_subparsers(dest="command", help="Command to run") + + # Agent testing subcommand + agent_parser = subparsers.add_parser("agent", help="Test the LLM agent") + agent_parser.add_argument("--provider", choices=["openai", "anthropic"], + default=os.environ.get("LLM_PROVIDER", "openai"), + help="LLM provider to use") + agent_parser.add_argument("--prompt", type=str, required=True, + help="Prompt to send to the LLM") + agent_parser.add_argument("--output", type=str, + help="File to save the output to") + + # Formatter testing subcommand + formatter_parser = subparsers.add_parser("formatter", help="Test the formatter") + formatter_parser.add_argument("--input", type=str, required=True, + help="Raw JSON file to format") + formatter_parser.add_argument("--output", type=str, + help="File to save the formatted output to") + + # Parse arguments + args = parser.parse_args() + + # Execute command + if args.command == "agent": + test_llm_agent(args.provider, args.prompt, args.output) + elif args.command == "formatter": + test_formatter(args.input, args.output) + else: + parser.print_help() \ No newline at end of file From 8b81eef301c131d5c3acfff724383da1432b58a3 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sun, 20 Apr 2025 20:36:09 -0700 Subject: [PATCH 019/104] refactor the openai agent --- core/suggestion-service/.env.example | 4 +- core/suggestion-service/README.md | 141 --------------- core/suggestion-service/llm_agent/base.py | 77 ++++++--- .../llm_agent/instruction.md | 74 ++++++++ .../llm_agent/openai_agent.py | 161 ++++++------------ .../llm_agent/output_format.json | 86 ++++++++++ .../suggestion_engine/generator.py | 14 +- 7 files changed, 288 insertions(+), 269 deletions(-) create mode 100644 core/suggestion-service/llm_agent/instruction.md create mode 100644 core/suggestion-service/llm_agent/output_format.json diff --git a/core/suggestion-service/.env.example b/core/suggestion-service/.env.example index f7fc40d5ff1..bd42cd20cfb 100644 --- a/core/suggestion-service/.env.example +++ b/core/suggestion-service/.env.example @@ -4,7 +4,9 @@ LLM_PROVIDER=openai # OpenAI configuration OPENAI_API_KEY=your_openai_api_key_here -OPENAI_MODEL=gpt-4-turbo-preview +OPENAI_MODEL=gpt-4o +# Optional: Specify an existing Assistant ID to reuse +OPENAI_ASSISTANT_ID= # Anthropic configuration ANTHROPIC_API_KEY=your_anthropic_api_key_here diff --git a/core/suggestion-service/README.md b/core/suggestion-service/README.md index ce4c9af7338..8eb6817581a 100644 --- a/core/suggestion-service/README.md +++ b/core/suggestion-service/README.md @@ -44,144 +44,3 @@ TODO ### LLM Agent Layer This layer is responsible for calling language models to get the response (the suggestions). - -The LLM Agent layer is organized as follows: - -1. `llm_agent/base.py` - Defines the abstract base class for all LLM agents and a factory for creating agents -2. `llm_agent/openai_agent.py` - Implementation for OpenAI models (GPT-4, GPT-3.5, etc.) -3. `llm_agent/anthropic_agent.py` - Implementation for Anthropic Claude models - -#### Configuration - -LLM agents are configured using environment variables: - -``` -# LLM provider configuration (openai or anthropic) -LLM_PROVIDER=openai - -# OpenAI configuration -OPENAI_API_KEY=your_openai_api_key_here -OPENAI_MODEL=gpt-4-turbo-preview - -# Anthropic configuration -ANTHROPIC_API_KEY=your_anthropic_api_key_here -ANTHROPIC_MODEL=claude-3-opus-20240229 -``` - -#### Usage - -The SuggestionGenerator automatically creates and uses an LLM agent based on the configuration. -You can also create and use an LLM agent directly: - -```python -from llm_agent.base import LLMAgentFactory - -# Create an LLM agent -agent = LLMAgentFactory.create("openai", model="gpt-4-turbo-preview") - -# Generate suggestions -suggestions = agent.generate_suggestions( - prompt="Description of the workflow", - max_suggestions=3, - temperature=0.7 -) -``` - -#### Adding New LLM Providers - -To add a new LLM provider: - -1. Create a new file `llm_agent/your_provider_agent.py` -2. Implement the LLMAgent interface -3. Register your implementation with the LLMAgentFactory - -Example: -```python -@LLMAgentFactory.register("your_provider") -class YourProviderAgent(LLMAgent): - def __init__(self, model="default-model", api_key=None): - # Initialize client - pass - - def generate_suggestions(self, prompt, max_suggestions=3, temperature=0.7, **kwargs): - # Generate suggestions using your provider's API - pass -``` - -### Output Formatting Layer - -This layer ensures that LLM outputs match the expected format for workflow suggestions. - -The output formatter is responsible for: - -1. Extracting JSON from raw LLM output -2. Validating suggestion structure -3. Filling in missing values (UUIDs, empty fields, etc.) -4. Converting to the correct format expected by the frontend - -#### Usage - -The output formatter is automatically used by the LLM agents, but you can also use it directly: - -```python -from output_formatter.formatter import format_raw_suggestions - -# Format raw LLM output -raw_output = """ -[ - { - "suggestion": "Add a keyword search", - "changes": { - "operatorsToAdd": [ - { - "operatorType": "KeywordSearch", - "operatorProperties": { - "keyword": "example" - } - } - ], - "linksToAdd": [] - } - } -] -""" - -formatted_suggestions = format_raw_suggestions(raw_output) -``` - -### Testing - -You can test the LLM agent and formatter using the test script: - -```bash -# Test an LLM agent -python test_llm_agent.py agent --provider openai --prompt "Generate a workflow suggestion" --output output.json - -# Test the formatter with a raw JSON file -python test_llm_agent.py formatter --input raw_output.json --output formatted_output.json -``` - -### API Endpoints - -The service exposes the following endpoints: - -- `GET /` - Health check endpoint -- `GET /api/config` - Get the current configuration -- `POST /api/workflow-suggestion` - Generate workflow suggestions - -Required request format: -```json -{ - "workflow": "{JSON string of the workflow}", - "compilationState": { - "state": "Succeeded", - "operatorInputSchemaMap": {}, - "operatorErrors": {} - }, - "executionState": { - "state": "Completed" - }, - "resultTables": {}, - "maxSuggestions": 3 -} -``` diff --git a/core/suggestion-service/llm_agent/base.py b/core/suggestion-service/llm_agent/base.py index b44c9f3d494..51f27a8ca10 100644 --- a/core/suggestion-service/llm_agent/base.py +++ b/core/suggestion-service/llm_agent/base.py @@ -1,6 +1,7 @@ """Base LLM agent interface""" from abc import ABC, abstractmethod -from typing import Dict, Any, List, Optional +from typing import Dict, Any, List, Optional, Type +import os class LLMAgent(ABC): @@ -9,6 +10,17 @@ class LLMAgent(ABC): All LLM providers (OpenAI, Anthropic, etc.) should implement this interface. """ + def __init__(self, model: str = None, api_key: str = None): + """ + Initialize the LLM agent. + + Args: + model: The model to use for generation + api_key: The API key for the LLM service + """ + self.model = model + self.api_key = api_key + @abstractmethod def generate_suggestions(self, prompt: str, @@ -44,32 +56,59 @@ def generate_suggestions(self, class LLMAgentFactory: """Factory for creating LLM agents based on provider name.""" - _registry = {} + _agent_registry: Dict[str, Type[LLMAgent]] = {} @classmethod - def register(cls, provider_name: str): - """Register an LLM agent class with the factory.""" - def inner_wrapper(wrapped_class): - cls._registry[provider_name] = wrapped_class - return wrapped_class - return inner_wrapper + def register(cls, name: str): + """ + Decorator to register an LLM agent class with the factory. + + Args: + name: The name to register the agent under + + Returns: + A decorator function + """ + def decorator(agent_class): + cls._agent_registry[name.lower()] = agent_class + return agent_class + return decorator @classmethod - def create(cls, provider_name: str, **kwargs) -> LLMAgent: + def create(cls, provider: str, model: str = None, api_key: str = None, **kwargs) -> LLMAgent: """ - Create an instance of the requested LLM agent. + Create an LLM agent instance based on the provider. Args: - provider_name: Name of the LLM provider - **kwargs: Parameters to pass to the LLM agent constructor - + provider: The LLM provider to use (e.g., 'openai', 'anthropic') + model: The model to use (if None, will use default for the provider) + api_key: The API key (if None, will use environment variable) + **kwargs: Additional parameters to pass to the agent constructor + Returns: - An instance of the requested LLM agent - + An instance of the appropriate LLM agent + Raises: - ValueError: If the provider is not registered + ValueError: If the provider is not registered or if required configuration is missing """ - if provider_name not in cls._registry: - raise ValueError(f"Unknown LLM provider: {provider_name}") + provider = provider.lower() + + if provider not in cls._agent_registry: + raise ValueError(f"LLM provider '{provider}' is not supported. Available providers: {list(cls._agent_registry.keys())}") + + # Get the agent class from the registry + agent_class = cls._agent_registry[provider] + + # Determine API key from environment if not provided + if api_key is None: + if provider == "openai": + api_key = os.environ.get("OPENAI_API_KEY") + elif provider == "anthropic": + api_key = os.environ.get("ANTHROPIC_API_KEY") + + # Check if API key is available + if not api_key: + raise ValueError(f"API key for {provider} not provided and not found in environment variables") - return cls._registry[provider_name](**kwargs) \ No newline at end of file + # Create and return the agent instance + return agent_class(model=model, api_key=api_key, **kwargs) \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/instruction.md b/core/suggestion-service/llm_agent/instruction.md new file mode 100644 index 00000000000..c06f94b4765 --- /dev/null +++ b/core/suggestion-service/llm_agent/instruction.md @@ -0,0 +1,74 @@ +# Identity + +You are an AI assistant that helps users improve their Texera workflows. Your task is to analyze workflow prompts and generate structured suggestions that can enhance or correct the workflow execution. + +# Instructions + +* You will receive one of two prompt formats: + 1. **RAW format**: Contains the workflow dictionary and optional input schemas or static errors. + 2. **BY_PATH format**: Lists each linear execution path in the workflow with descriptive metadata. + +* Regardless of format, your goal is to generate a list of actionable suggestions. Each suggestion must: + - Be expressed clearly in natural language. + - Include a structured JSON object describing the required changes. + +* Your suggestion should either: + - Help users **fix** potential issues in their workflow (e.g., broken links, misconfigured operators, incorrect data flow), or + - **Improve** their workflow by adding useful steps (e.g., for data cleaning, exploratory data analysis, data visualization, AI/ML model training or inference). + +* Each suggestion must include: + - A `suggestion` string that explains the proposed improvement. + - A `suggestionType` field with one of two values: `"fix"` or `"improve"`. + - A `changes` object containing: + * `operatorsToAdd`: array of new or updated operators with ID, type, and properties. + * `linksToAdd`: array of new links with operator ID and port info. + * `operatorsToDelete`: list of operator IDs to remove. + +* Do not include extra explanation or commentary. Your response must be a valid JSON array of suggestion objects. It will be parsed automatically. + +# Examples + + +Here is the workflow dict: +{ ... } + +Here is the input schema for each operator: +{ ... } + + + +[ + { + "suggestion": "Add a keyword search operator before the join.", + "suggestionType": "improve", + "changes": { + "operatorsToAdd": [ + { + "operatorType": "KeywordSearch", + "operatorID": "KeywordSearch-operator-123456", + "operatorProperties": { + "attribute": "description", + "keywords": ["urgent", "delayed"] + }, + "customDisplayName": "Keyword Search" + } + ], + "linksToAdd": [ + { + "linkID": "link-789", + "source": { + "operatorID": "KeywordSearch-operator-123456", + "portID": "output-0" + }, + "target": { + "operatorID": "Join-operator-456", + "portID": "input-0" + } + } + ], + "operatorsToDelete": [] + } + } +] + +""" \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/openai_agent.py b/core/suggestion-service/llm_agent/openai_agent.py index 17c485eaa08..ddf97e3b898 100644 --- a/core/suggestion-service/llm_agent/openai_agent.py +++ b/core/suggestion-service/llm_agent/openai_agent.py @@ -1,135 +1,84 @@ -"""OpenAI LLM agent.""" import os import json from typing import Dict, Any, List, Optional -import openai from openai import OpenAI from llm_agent.base import LLMAgent, LLMAgentFactory -from output_formatter.formatter import format_raw_suggestions @LLMAgentFactory.register("openai") class OpenAIAgent(LLMAgent): """ - Implementation of the LLM agent interface using OpenAI's API. - - This agent supports different model types from OpenAI, such as: - - gpt-4-turbo-preview - - gpt-4 - - gpt-3.5-turbo + Implementation of the LLM agent using OpenAI's `responses.create` API with JSON Schema validation. """ - - def __init__(self, - model: str = "gpt-4-turbo-preview", - api_key: Optional[str] = None): + + def __init__(self, + model: str = "gpt-4o-2024-08-06", + api_key: Optional[str] = None, + project: Optional[str] = None): """ Initialize the OpenAI agent. - + Args: - model: The OpenAI model to use - api_key: The OpenAI API key (if None, uses OPENAI_API_KEY environment variable) + model: OpenAI model name (e.g., "gpt-4o-2024-08-06") + api_key: API key for OpenAI + project: Project ID for usage (optional) """ self.model = model - self.client = OpenAI(api_key=api_key or os.environ.get("OPENAI_API_KEY")) - - def generate_suggestions(self, - prompt: str, - max_suggestions: int = 3, - temperature: float = 0.7, - max_tokens: Optional[int] = None, - **kwargs) -> List[Dict[str, Any]]: + self.client = OpenAI(api_key=api_key or os.environ.get("OPENAI_API_KEY"), + project=project or os.environ.get("OPENAI_PROJECT_ID")) + + # Load JSON Schema from file + with open("output_format.json", "r") as f: + self.schema = json.load(f) + + def generate_suggestions(self, + prompt: str, + max_suggestions: int = 3, + temperature: float = 0.7, + max_tokens: Optional[int] = None, + **kwargs) -> List[Dict[str, Any]]: """ - Generate workflow suggestions using OpenAI's API. - + Generate workflow suggestions using OpenAI's `responses.create` endpoint with schema enforcement. + Args: - prompt: The natural language prompt describing the workflow - max_suggestions: Maximum number of suggestions to generate - temperature: Sampling temperature (0.0-1.0) where lower is more deterministic - max_tokens: Maximum number of tokens to generate - **kwargs: Additional OpenAI-specific parameters - + prompt: Workflow description + max_suggestions: Max number of suggestions to return + temperature: Sampling temperature + max_tokens: Maximum tokens allowed in output + **kwargs: Additional options + Returns: - A list of suggestion dictionaries formatted according to the interface + A list of workflow suggestion dicts. """ - # Enhance the prompt with instruction about the output format - system_prompt = self._create_system_prompt(max_suggestions) - try: - response = self.client.chat.completions.create( + response = self.client.responses.create( model=self.model, - messages=[ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": prompt} + input=[ + { + "role": "system", + "content": "You are an AI assistant that helps users improve their Texera workflows. " + "Analyze the input and generate a list of suggestions in valid JSON format." + }, + { + "role": "user", + "content": prompt + } ], - temperature=temperature, - max_tokens=max_tokens, - n=1, # Generate a single completion - **kwargs + text={ + "format": { + "type": "json_schema", + "name": "workflow_suggestions", + "schema": self.schema, + "strict": True + } + } ) - - # Extract content from the response - raw_content = response.choices[0].message.content - - # Parse and format the suggestions - suggestions = format_raw_suggestions(raw_content) - + + suggestions = json.loads(response.output_text) return suggestions[:max_suggestions] - + except Exception as e: - print(f"Error generating suggestions with OpenAI: {str(e)}") + print(f"Error generating suggestions: {e}") return [] - - def _create_system_prompt(self, max_suggestions: int) -> str: - """Create the system prompt for the OpenAI model.""" - return f"""You are an AI assistant that helps users improve their Texera workflows by suggesting useful modifications. - -Analyze the provided workflow description and generate {max_suggestions} suggestions to improve it. -Your suggestions should address common issues, optimizations, or additional useful features. - -For each suggestion, provide: -1. A brief natural language description of the recommendation -2. Detailed changes required to implement the suggestion - -Structure each suggestion as a valid JSON object with the following format: -{{ - "suggestion": "A clear description of the improvement", - "changes": {{ - "operatorsToAdd": [ - {{ - "operatorType": "TypeOfOperator", - "operatorID": "OperatorType-operator-UUID", - "operatorProperties": {{ - "property1": "value1", - "property2": "value2" - }}, - "customDisplayName": "Logical name describing function" - }} - ], - "linksToAdd": [ - {{ - "linkID": "link-UUID", - "source": {{ - "operatorID": "SourceOperatorID", - "portID": "output-N" - }}, - "target": {{ - "operatorID": "TargetOperatorID", - "portID": "input-N" - }} - }} - ], - "operatorsToDelete": ["OperatorID1", "OperatorID2"], - "operatorPropertiesToChange": [ - {{ - "operatorID": "ExistingOperatorID", - "properties": {{ - "propertyToChange": "newValue" - }} - }} - ] - }} -}} - -Output all suggestions as a valid JSON array and ensure each suggestion follows the exact format above.""" \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/output_format.json b/core/suggestion-service/llm_agent/output_format.json new file mode 100644 index 00000000000..e98f4295f91 --- /dev/null +++ b/core/suggestion-service/llm_agent/output_format.json @@ -0,0 +1,86 @@ +{ + "type": "array", + "items": { + "type": "object", + "properties": { + "suggestion": { + "type": "string", + "description": "A clear description of the improvement" + }, + "suggestionType": { + "type": "string", + "description": "the type of the proposed suggestion. 2 POSSIBLE VALUES: fix, improve" + }, + "changes": { + "type": "object", + "properties": { + "operatorsToAdd": { + "type": "array", + "description": "Operators to add or update. If the operatorID exists in the original workflow, treat this as an update.", + "items": { + "type": "object", + "properties": { + "operatorType": { + "type": "string", + "description": "The type of operator to be added or updated" + }, + "operatorID": { + "type": "string", + "description": "A unique identifier, may match existing operator in prompt" + }, + "operatorProperties": { + "type": "object", + "description": "Properties to assign or update for the operator", + "additionalProperties": true + }, + "customDisplayName": { + "type": "string", + "description": "Optional logical name describing function" + } + }, + "required": ["operatorType", "operatorID", "operatorProperties"] + } + }, + "linksToAdd": { + "type": "array", + "items": { + "type": "object", + "properties": { + "linkID": { + "type": "string", + "description": "A unique ID for the link" + }, + "source": { + "type": "object", + "properties": { + "operatorID": { "type": "string" }, + "portID": { "type": "string", "pattern": "^output-\\d+$" } + }, + "required": ["operatorID", "portID"] + }, + "target": { + "type": "object", + "properties": { + "operatorID": { "type": "string" }, + "portID": { "type": "string", "pattern": "^input-\\d+$" } + }, + "required": ["operatorID", "portID"] + } + }, + "required": ["linkID", "source", "target"] + } + }, + "operatorsToDelete": { + "type": "array", + "items": { + "type": "string", + "description": "List of operator IDs to delete" + } + } + }, + "required": ["operatorsToAdd", "linksToAdd"] + } + }, + "required": ["suggestion", "changes"] + } +} diff --git a/core/suggestion-service/suggestion_engine/generator.py b/core/suggestion-service/suggestion_engine/generator.py index 22550e059af..9d1ca31a150 100644 --- a/core/suggestion-service/suggestion_engine/generator.py +++ b/core/suggestion-service/suggestion_engine/generator.py @@ -40,7 +40,7 @@ def __init__(self, # Set default model based on provider if self.llm_provider == "openai": - default_model = os.environ.get("OPENAI_MODEL", "gpt-4-turbo-preview") + default_model = os.environ.get("OPENAI_MODEL", "gpt-4o") elif self.llm_provider == "anthropic": default_model = os.environ.get("ANTHROPIC_MODEL", "claude-3-opus-20240229") else: @@ -51,10 +51,20 @@ def __init__(self, # Create the LLM agent try: + # Additional parameters based on provider + extra_params = {} + + if self.llm_provider == "openai": + # Add assistant_id if specified + assistant_id = os.environ.get("OPENAI_ASSISTANT_ID") + if assistant_id and assistant_id.strip(): + extra_params["assistant_id"] = assistant_id + self.llm_agent = LLMAgentFactory.create( self.llm_provider, model=self.llm_model, - api_key=self.llm_api_key + api_key=self.llm_api_key, + **extra_params ) except ValueError as e: print(f"Error creating LLM agent: {str(e)}") From 6b4846a349e915d09a5cb7c984303303f083412c Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sun, 20 Apr 2025 23:36:33 -0700 Subject: [PATCH 020/104] add knowledge TODO --- .../llm_agent/files/__init__.py | 0 .../llm_agent/files/operator_metadata.json | 41270 ++++++++++++++++ .../llm_agent/files/unit.py | 61 + 3 files changed, 41331 insertions(+) create mode 100644 core/suggestion-service/llm_agent/files/__init__.py create mode 100644 core/suggestion-service/llm_agent/files/operator_metadata.json create mode 100644 core/suggestion-service/llm_agent/files/unit.py diff --git a/core/suggestion-service/llm_agent/files/__init__.py b/core/suggestion-service/llm_agent/files/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/core/suggestion-service/llm_agent/files/operator_metadata.json b/core/suggestion-service/llm_agent/files/operator_metadata.json new file mode 100644 index 00000000000..cbb9333d898 --- /dev/null +++ b/core/suggestion-service/llm_agent/files/operator_metadata.json @@ -0,0 +1,41270 @@ +{ + "operators": [ + { + "operatorType": "IntervalJoin", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "leftAttributeName": { + "enum": [ + "integer", + "long", + "double", + "timestamp" + ] + }, + "rightAttributeName": { + "const": { + "$data": "leftAttributeName" + } + } + }, + "properties": { + "constant": { + "propertyOrder": 1, + "type": "integer", + "default": 10, + "description": "left attri in (right, right + constant)", + "title": "Interval Constant" + }, + "includeLeftBound": { + "propertyOrder": 2, + "type": "boolean", + "default": true, + "description": "Include condition left attri = right attri", + "title": "Include Left Bound" + }, + "includeRightBound": { + "propertyOrder": 3, + "type": "boolean", + "default": true, + "description": "Include condition left attri = right attri", + "title": "Include Right Bound" + }, + "timeIntervalType": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "default": "day", + "enum": [ + "year", + "month", + "day", + "hour", + "minute", + "second" + ], + "description": "Year, Month, Day, Hour, Minute or Second", + "title": "Time interval type" + }, + "dummyPropertyList": { + "propertyOrder": 5, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "leftAttributeName": { + "propertyOrder": 8, + "type": "string", + "description": "Choose one attribute in the left table", + "title": "Left Input attr", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "rightAttributeName": { + "propertyOrder": 9, + "type": "string", + "description": "Choose one attribute in the right table", + "title": "Right Input attr", + "autofill": "attributeName", + "autofillAttributeOnPort": 1 + } + }, + "required": [ + "constant", + "includeLeftBound", + "includeRightBound", + "leftAttributeName", + "rightAttributeName" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "IntervalJoin" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Interval Join", + "operatorDescription": "Join two inputs with left table join key in the range of [right table join key, right table join key + constant value]", + "operatorGroupName": "Join", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "left table", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "right table", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "DotPlot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "Count Attribute": { + "propertyOrder": 6, + "type": "string", + "description": "the attribute for the counting of the dot plot", + "title": "Count Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "Count Attribute" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "DotPlot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Dot Plot", + "operatorDescription": "Visualize data using a dot plot", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "CartesianProduct", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + } + }, + "required": [], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "CartesianProduct" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Cartesian Product", + "operatorDescription": "Append fields together to get the cartesian product of two inputs", + "operatorGroupName": "Join", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "left", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "right", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "HuggingFaceSentimentAnalysis", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "attribute": { + "propertyOrder": 6, + "type": "string", + "description": "column to perform sentiment analysis on", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Positive result attribute": { + "propertyOrder": 7, + "type": "string", + "default": "huggingface_sentiment_positive", + "description": "column name of the sentiment analysis result (positive)", + "title": "Positive result attribute" + }, + "Neutral result attribute": { + "propertyOrder": 8, + "type": "string", + "default": "huggingface_sentiment_neutral", + "description": "column name of the sentiment analysis result (neutral)", + "title": "Neutral result attribute" + }, + "Negative result attribute": { + "propertyOrder": 9, + "type": "string", + "default": "huggingface_sentiment_negative", + "description": "column name of the sentiment analysis result (negative)", + "title": "Negative result attribute" + } + }, + "required": [ + "attribute", + "Positive result attribute", + "Neutral result attribute", + "Negative result attribute" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "HuggingFaceSentimentAnalysis" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Hugging Face Sentiment Analysis", + "operatorDescription": "Analyzing Sentiments with a Twitter-Based Model from Hugging Face", + "operatorGroupName": "Hugging Face", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": true, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "PythonUDFSourceV2", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "code": { + "propertyOrder": 4, + "type": "string", + "default": "# from pytexera import *\n# class GenerateOperator(UDFSourceOperator):\n# \n# @overrides\n# \n# def produce(self) -> Iterator[Union[TupleLike, TableLike, None]]:\n# yield\n", + "description": "Input your code here", + "title": "Python script" + }, + "workers": { + "propertyOrder": 5, + "type": "integer", + "default": 1, + "description": "Specify how many parallel workers to launch", + "title": "Worker count" + }, + "columns": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + }, + "description": "The columns of the source", + "title": "Columns" + } + }, + "required": [ + "code", + "workers" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "PythonUDFSourceV2" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "Attribute": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Attribute Name" + }, + "attributeType": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "enum": [ + "string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + } + } + } + } + }, + "additionalMetadata": { + "userFriendlyName": "1-out Python UDF", + "operatorDescription": "User-defined function operator in Python script", + "operatorGroupName": "Python", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": true, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "TwitterFullArchiveSearch", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "apiKey": { + "propertyOrder": 4, + "type": "string", + "title": "API Key" + }, + "apiSecretKey": { + "propertyOrder": 5, + "type": "string", + "title": "API Secret Key" + }, + "stopWhenRateLimited": { + "propertyOrder": 6, + "type": "boolean", + "default": false, + "description": "Stop when hitting rate limit?", + "title": "Stop Upon Rate Limit" + }, + "searchQuery": { + "propertyOrder": 7, + "type": "string", + "description": "Up to 1024 characters (Limited By Twitter)", + "title": "Search Query", + "widget": { + "formlyConfig": { + "type": "textarea", + "templateOptions": { + "autosize": true, + "autosizeMinRows": 3 + } + } + } + }, + "fromDateTime": { + "propertyOrder": 8, + "type": "string", + "default": "2021-04-01T00:00:00Z", + "description": "ISO 8601 format", + "title": "From Datetime" + }, + "toDateTime": { + "propertyOrder": 9, + "type": "string", + "default": "2021-05-01T00:00:00Z", + "description": "ISO 8601 format", + "title": "To Datetime" + }, + "limit": { + "propertyOrder": 10, + "type": "integer", + "default": 100, + "description": "Maximum number of tweets to retrieve", + "title": "Limit" + } + }, + "required": [ + "apiKey", + "apiSecretKey", + "stopWhenRateLimited", + "searchQuery", + "fromDateTime", + "toDateTime", + "limit" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "TwitterFullArchiveSearch" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Twitter Full Archive Search API", + "operatorDescription": "Retrieve data from Twitter Full Archive Search API", + "operatorGroupName": "External API", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnLogisticRegressionCV", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnLogisticRegressionCV" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Logistic Regression Cross Validation", + "operatorDescription": "Sklearn Logistic Regression Cross Validation Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "JSONLFileScan", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "fileName": { + "propertyOrder": 4, + "type": "string", + "title": "File" + }, + "fileEncoding": { + "propertyOrder": 5, + "type": "string", + "default": "UTF_8", + "enum": [ + "UTF_8", + "UTF_16", + "US_ASCII" + ], + "description": "decoding charset to use on input", + "title": "File Encoding" + }, + "limit": { + "propertyOrder": 6, + "nullable": true, + "type": "integer", + "description": "max output count", + "title": "Limit" + }, + "offset": { + "propertyOrder": 7, + "nullable": true, + "type": "integer", + "description": "starting point of output", + "title": "Offset" + }, + "flatten": { + "propertyOrder": 8, + "type": "boolean", + "description": "flatten nested objects and arrays", + "title": "Flatten" + } + }, + "required": [ + "fileName", + "fileEncoding", + "flatten" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "JSONLFileScan" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "JSONL File Scan", + "operatorDescription": "Scan data from a JSONL file", + "operatorGroupName": "Data Input", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "CandlestickChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "date": { + "propertyOrder": 6, + "type": "string", + "description": "the date of the candlestick", + "title": "Date Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "open": { + "propertyOrder": 7, + "type": "string", + "description": "the opening price of the candlestick", + "title": "Opening Price Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "high": { + "propertyOrder": 8, + "type": "string", + "description": "the highest price of the candlestick", + "title": "Highest Price Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "low": { + "propertyOrder": 9, + "type": "string", + "description": "the lowest price of the candlestick", + "title": "Lowest Price Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "close": { + "propertyOrder": 10, + "type": "string", + "description": "the closing price of the candlestick", + "title": "Closing Price Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "date", + "open", + "high", + "low", + "close" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "CandlestickChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Candlestick Chart", + "operatorDescription": "Visualize data in a Candlestick Chart", + "operatorGroupName": "Financial", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "ReservoirSampling", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "number of item sampled in reservoir sampling": { + "propertyOrder": 6, + "type": "integer", + "description": "reservoir sampling with k items being kept randomly", + "title": "Number of item sampled in reservoir sampling" + } + }, + "required": [ + "number of item sampled in reservoir sampling" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "ReservoirSampling" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Reservoir Sampling", + "operatorDescription": "Reservoir Sampling with k items being kept randomly", + "operatorGroupName": "Utilities", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "ScatterMatrixChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "value": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "Selected Attributes": { + "propertyOrder": 6, + "type": "array", + "items": { + "type": "string" + }, + "description": "The axes of each scatter plot in the matrix.", + "title": "Selected Attributes", + "autofill": "attributeNameList", + "autofillAttributeOnPort": 0 + }, + "Color": { + "propertyOrder": 7, + "type": "string", + "description": "Column to color points", + "title": "Color Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "Selected Attributes", + "Color" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "ScatterMatrixChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Scatter Matrix Chart", + "operatorDescription": "Visualize datasets in a Scatter Matrix", + "operatorGroupName": "Statistical", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnKNN", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnKNN" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "K-nearest Neighbors", + "operatorDescription": "Sklearn K-nearest Neighbors Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnProbabilityCalibration", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnProbabilityCalibration" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Probability Calibration", + "operatorDescription": "Sklearn Probability Calibration Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SortPartitions", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "sortAttributeName": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "sortAttributeName": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute to sort (must be numerical).", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "domainMin": { + "propertyOrder": 5, + "type": "integer", + "description": "Minimum value of the domain of the attribute.", + "title": "Attribute Domain Min" + }, + "domainMax": { + "propertyOrder": 6, + "type": "integer", + "description": "Maximum value of the domain of the attribute.", + "title": "Attribute Domain Max" + } + }, + "required": [ + "sortAttributeName", + "domainMin", + "domainMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SortPartitions" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Sort Partitions", + "operatorDescription": "Sort Partitions", + "operatorGroupName": "Sort", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "DumbbellPlot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "measurementColumnName": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "categoryColumnName": { + "propertyOrder": 6, + "type": "string", + "description": "the name of the category column", + "title": "Category Column Name", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "dumbbellStartValue": { + "propertyOrder": 7, + "type": "string", + "description": "the start point value of each dumbbell", + "title": "Dumbbell Start Value" + }, + "dumbbellEndValue": { + "propertyOrder": 8, + "type": "string", + "description": "the end value of each dumbbell", + "title": "Dumbbell End Value" + }, + "measurementColumnName": { + "propertyOrder": 9, + "type": "string", + "description": "the name of the measurement column", + "title": "Measurement Column Name", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "comparedColumnName": { + "propertyOrder": 10, + "type": "string", + "description": "the column name that is being compared", + "title": "Compared Column Name", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "dots": { + "propertyOrder": 11, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DumbbellDotConfig" + }, + "title": "Dots" + }, + "showLegends": { + "propertyOrder": 12, + "type": "boolean", + "description": "whether show legends in the graph", + "title": "Show Legends?" + } + }, + "required": [ + "categoryColumnName", + "dumbbellStartValue", + "dumbbellEndValue", + "measurementColumnName", + "comparedColumnName", + "showLegends" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "DumbbellPlot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "DumbbellDotConfig": { + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "dot": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "dot": { + "propertyOrder": 1, + "type": "string", + "description": "value for dot axis", + "title": "Dot Column Value", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "dot" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Dumbbell Plot", + "operatorDescription": "Visualize data in a Dumbbell Plots. A dumbbell plots (also known as a lollipop chart) is typically used to compare two distinct values or time points for the same entity.", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "If", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "conditionName": { + "propertyOrder": 4, + "type": "string", + "description": "name of the state variable to evaluate", + "title": "Condition State" + } + }, + "required": [ + "conditionName" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "If" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "If", + "operatorDescription": "If", + "operatorGroupName": "Control Block", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "Condition", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "False", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "True", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnSDG", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnSDG" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Stochastic Gradient Descent", + "operatorDescription": "Sklearn Stochastic Gradient Descent Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "URLVisualizer", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "urlContentAttrName": { + "enum": [ + "string" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "urlContentAttrName": { + "propertyOrder": 4, + "type": "string", + "title": "URL content", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "urlContentAttrName" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "URLVisualizer" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "URL Visualizer", + "operatorDescription": "Render the content of URL", + "operatorGroupName": "Media", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Dummy", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "dummyOperator": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "description": "The description of this dummy operator", + "title": "Description" + } + }, + "required": [], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Dummy" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Dummy", + "operatorDescription": "A dummy operator used as a placeholder.", + "operatorGroupName": "Utilities", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": true, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": true, + "dynamicOutputPorts": true, + "supportReconfiguration": true, + "allowPortCustomization": true + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "HuggingFaceTextSummarization", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "attribute": { + "propertyOrder": 6, + "type": "string", + "description": "attribute to perform text summarization on", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Result attribute name": { + "propertyOrder": 7, + "nullable": true, + "type": "string", + "default": "summary", + "description": "attribute name of the text summary result", + "title": "Result attribute name" + } + }, + "required": [ + "attribute" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "HuggingFaceTextSummarization" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Hugging Face Text Summarization", + "operatorDescription": "Summarize the given text content with a mini2bert pre-trained model from Hugging Face", + "operatorGroupName": "Hugging Face", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Union", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + } + }, + "required": [], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Union" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Union", + "operatorDescription": "Unions the output rows from multiple input operators", + "operatorGroupName": "Set", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": true, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnGradientBoosting", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnGradientBoosting" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Gradient Boosting", + "operatorDescription": "Sklearn Gradient Boosting Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "KNNRegressorTrainer", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "paraList": { + "propertyOrder": 4, + "type": "array", + "items": { + "$ref": "#/definitions/HyperParameters(SklearnAdvancedKNNParameters)" + }, + "title": "Parameter Setting" + }, + "groundTruthAttribute": { + "propertyOrder": 5, + "type": "string", + "description": "Ground truth attribute column", + "title": "Ground Truth Attribute Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Selected Features": { + "propertyOrder": 8, + "type": "array", + "items": { + "type": "string" + }, + "description": "Features used to train the model", + "title": "Selected Features", + "autofill": "attributeNameList", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "paraList", + "groundTruthAttribute", + "Selected Features" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "KNNRegressorTrainer" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "HyperParameters(SklearnAdvancedKNNParameters)": { + "type": "object", + "additionalProperties": false, + "properties": { + "parameter": { + "propertyOrder": 1, + "type": "string", + "enum": [ + "n_neighbors", + "p", + "weights", + "algorithm", + "leaf_size", + "metric", + "metric_params" + ], + "description": "Choose the name of the parameter", + "title": "Parameter" + }, + "parametersSource": { + "propertyOrder": 2, + "type": "boolean", + "default": false, + "description": "Parameter from workflow", + "title": "Workflow" + }, + "attribute": { + "propertyOrder": 3, + "nullable": true, + "type": "string", + "title": "Attribute", + "autofill": "attributeName", + "hideTarget": "parametersSource", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 1 + }, + "value": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "title": "Value", + "hideTarget": "parametersSource", + "hideType": "equals", + "hideExpectedValue": "true", + "hideOnNull": true + } + }, + "required": [ + "parameter", + "parametersSource" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "KNN Regressor", + "operatorDescription": "Sklearn KNN Regressor Operator", + "operatorGroupName": "Advanced Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "parameter", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "RUDFSource", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "code": { + "propertyOrder": 4, + "type": "string", + "default": "# If using Table API:\n# function() { \n# return (data.frame(Column_Here = \"Value_Here\")) \n# }\n\n# If using Tuple API:\n# library(coro)\n# coro::generator(function() {\n# yield (list(text= \"hello world!\"))\n# })", + "description": "Input your code here", + "title": "R Source UDF Script" + }, + "workers": { + "propertyOrder": 5, + "type": "integer", + "default": 1, + "description": "Specify how many parallel workers to launch", + "title": "Worker count" + }, + "useTupleAPI": { + "propertyOrder": 6, + "type": "boolean", + "default": false, + "description": "Check this box to use Tuple API, leave unchecked to use Table API", + "title": "Use Tuple API?" + }, + "columns": { + "propertyOrder": 7, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + }, + "description": "The columns of the source", + "title": "Columns" + } + }, + "required": [ + "code", + "workers", + "useTupleAPI" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "RUDFSource" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "Attribute": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Attribute Name" + }, + "attributeType": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "enum": [ + "string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + } + } + } + } + }, + "additionalMetadata": { + "userFriendlyName": "1-out R UDF", + "operatorDescription": "User-defined function operator in R script", + "operatorGroupName": "R", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "HuggingFaceIrisLogisticRegression", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "petalLengthCmAttribute": { + "propertyOrder": 6, + "type": "string", + "description": "attribute in your dataset corresponding to PetalLengthCm", + "title": "Petal Length Cm Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "petalWidthCmAttribute": { + "propertyOrder": 7, + "type": "string", + "description": "attribute in your dataset corresponding to PetalWidthCm", + "title": "Petal Width Cm Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "prediction class name": { + "propertyOrder": 8, + "type": "string", + "default": "Species_prediction", + "description": "output attribute name for the predicted class of species", + "title": "Prediction class name" + }, + "prediction probability name": { + "propertyOrder": 9, + "type": "string", + "default": "Species_probability", + "description": "output attribute name for the prediction's probability of being a Iris-setosa", + "title": "Prediction probability name" + } + }, + "required": [ + "petalLengthCmAttribute", + "petalWidthCmAttribute", + "prediction class name", + "prediction probability name" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "HuggingFaceIrisLogisticRegression" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Hugging Face Iris Logistic Regression", + "operatorDescription": "Predict whether an iris is an Iris-setosa using a pre-trained logistic regression model", + "operatorGroupName": "Hugging Face", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "ContinuousErrorBands", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "xLabel": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "default": "X Axis", + "description": "Label used for x axis", + "title": "X Label" + }, + "yLabel": { + "propertyOrder": 7, + "nullable": true, + "type": "string", + "default": "Y Axis", + "description": "Label used for y axis", + "title": "Y Label" + }, + "bands": { + "propertyOrder": 8, + "type": "array", + "items": { + "$ref": "#/definitions/BandConfig" + }, + "title": "Bands" + } + }, + "required": [ + "bands" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "ContinuousErrorBands" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "BandConfig": { + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "yValue": { + "enum": [ + "integer", + "long", + "double" + ] + }, + "xValue": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "yUpper": { + "propertyOrder": 1, + "type": "string", + "description": "Represents upper bound error of y-values", + "title": "Y-Axis Upper Bound", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "yLower": { + "propertyOrder": 2, + "type": "string", + "description": "Represents lower bound error of y-values", + "title": "Y-Axis Lower Bound", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "fillColor": { + "propertyOrder": 3, + "nullable": true, + "type": "string", + "description": "must be a valid CSS color or hex color string", + "title": "Fill Color" + }, + "y": { + "propertyOrder": 4, + "type": "string", + "description": "value for y axis", + "title": "Y Value", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "x": { + "propertyOrder": 5, + "type": "string", + "description": "value for x axis", + "title": "X Value", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "mode": { + "propertyOrder": 6, + "type": "string", + "default": "line with dots", + "enum": [ + "line", + "dots", + "line with dots" + ], + "title": "Line Mode" + }, + "name": { + "propertyOrder": 7, + "nullable": true, + "type": "string", + "title": "Line Name" + }, + "color": { + "propertyOrder": 8, + "nullable": true, + "type": "string", + "description": "must be a valid CSS color or hex color string", + "title": "Line Color" + } + }, + "required": [ + "yUpper", + "yLower", + "y", + "x", + "mode" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Continuous Error Bands", + "operatorDescription": "Visualize error or uncertainty along a continuous line", + "operatorGroupName": "Statistical", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "TwitterSearch", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "apiKey": { + "propertyOrder": 4, + "type": "string", + "title": "API Key" + }, + "apiSecretKey": { + "propertyOrder": 5, + "type": "string", + "title": "API Secret Key" + }, + "stopWhenRateLimited": { + "propertyOrder": 6, + "type": "boolean", + "default": false, + "description": "Stop when hitting rate limit?", + "title": "Stop Upon Rate Limit" + }, + "searchQuery": { + "propertyOrder": 7, + "type": "string", + "description": "Up to 1024 characters (Limited by Twitter)", + "title": "Search Query", + "widget": { + "formlyConfig": { + "type": "textarea", + "templateOptions": { + "autosize": true, + "autosizeMinRows": 3 + } + } + } + }, + "limit": { + "propertyOrder": 8, + "type": "integer", + "default": 100, + "description": "Maximum number of tweets to retrieve", + "title": "Limit" + } + }, + "required": [ + "apiKey", + "apiSecretKey", + "stopWhenRateLimited", + "searchQuery", + "limit" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "TwitterSearch" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Twitter Search API", + "operatorDescription": "Retrieve data from Twitter Search API", + "operatorGroupName": "External API", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnPassiveAggressive", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnPassiveAggressive" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Passive Aggressive", + "operatorDescription": "Sklearn Passive Aggressive Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "HTMLVisualizer", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "htmlContentAttrName": { + "propertyOrder": 4, + "type": "string", + "title": "HTML content", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "htmlContentAttrName" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "HTMLVisualizer" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "HTML Visualizer", + "operatorDescription": "Render the result of HTML content", + "operatorGroupName": "Media", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnComplementNaiveBayes", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnComplementNaiveBayes" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Complement Naive Bayes", + "operatorDescription": "Sklearn Complement Naive Bayes Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "URLFetcher", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "url": { + "propertyOrder": 4, + "type": "string", + "description": "Only accepts standard URL format", + "title": "URL" + }, + "decodingMethod": { + "propertyOrder": 5, + "type": "string", + "enum": [ + "UTF-8", + "RAW BYTES" + ], + "description": "The decoding method for the url content", + "title": "Decoding" + } + }, + "required": [ + "url", + "decodingMethod" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "URLFetcher" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "URL fetcher", + "operatorDescription": "Fetch the content of a single url", + "operatorGroupName": "External API", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "JavaUDF", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "code": { + "propertyOrder": 4, + "type": "string", + "default": "import edu.uci.ics.texera.workflow.common.operators.map.MapOpExec;\nimport edu.uci.ics.amber.engine.common.model.tuple.Tuple;\nimport edu.uci.ics.amber.engine.common.model.tuple.TupleLike;\nimport scala.Function1;\nimport java.io.Serializable;\n\npublic class JavaUDFOpExec extends MapOpExec {\n public JavaUDFOpExec () {\n this.setMapFunc((Function1 & Serializable) this::processTuple);\n }\n \n public TupleLike processTuple(Tuple tuple) {\n return tuple;\n }\n}", + "description": "Input your code here", + "title": "Java UDF script" + }, + "workers": { + "propertyOrder": 5, + "type": "integer", + "default": 1, + "description": "Specify how many parallel workers to lunch", + "title": "Worker count" + }, + "retainInputColumns": { + "propertyOrder": 6, + "type": "boolean", + "default": true, + "description": "Keep the original input columns?", + "title": "Retain input columns" + }, + "outputColumns": { + "propertyOrder": 7, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + }, + "description": "Name of the newly added output columns that the UDF will produce, if any", + "title": "Extra output column(s)" + } + }, + "required": [ + "code", + "workers", + "retainInputColumns" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "JavaUDF" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "Attribute": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Attribute Name" + }, + "attributeType": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "enum": [ + "string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + } + } + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Java UDF", + "operatorDescription": "User-defined function operator in Java script", + "operatorGroupName": "Java", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": true, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": true, + "dynamicOutputPorts": true, + "supportReconfiguration": true, + "allowPortCustomization": true + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "PieChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "value": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "value": { + "propertyOrder": 6, + "type": "string", + "description": "The value associated with slice of pie", + "title": "Value Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "name": { + "propertyOrder": 7, + "type": "string", + "description": "The name of the slice of pie", + "title": "Name Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "value", + "name" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "PieChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Pie Chart", + "operatorDescription": "Visualize data in a Pie Chart", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "DictionaryMatcher", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "Dictionary": { + "propertyOrder": 6, + "type": "string", + "description": "dictionary values separated by a comma", + "title": "Dictionary" + }, + "Attribute": { + "propertyOrder": 7, + "type": "string", + "description": "column name to match", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "result attribute": { + "propertyOrder": 8, + "type": "string", + "default": "matched", + "description": "column name of the matching result", + "title": "Result attribute" + }, + "Matching type": { + "propertyOrder": 9, + "type": "string", + "enum": [ + "Scan", + "Substring", + "Conjunction" + ], + "title": "Matching type" + } + }, + "required": [ + "Dictionary", + "Attribute", + "result attribute", + "Matching type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "DictionaryMatcher" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Dictionary matcher", + "operatorDescription": "Matches tuples if they appear in a given dictionary", + "operatorGroupName": "Search", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": true, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "UnnestString", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "Delimiter": { + "propertyOrder": 6, + "type": "string", + "default": ",", + "description": "string that separates the data", + "title": "Delimiter" + }, + "Attribute": { + "propertyOrder": 7, + "type": "string", + "description": "column of the string to unnest", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Result attribute": { + "propertyOrder": 8, + "type": "string", + "default": "unnestResult", + "description": "column name of the unnest result", + "title": "Result attribute" + } + }, + "required": [ + "Delimiter", + "Attribute", + "Result attribute" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "UnnestString" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Unnest String", + "operatorDescription": "Unnest the string values in the column separated by a delimiter to multiple values", + "operatorGroupName": "Utilities", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "BubbleChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "xValue": { + "propertyOrder": 6, + "type": "string", + "description": "Data column for the x-axis", + "title": "X-Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "yValue": { + "propertyOrder": 7, + "type": "string", + "description": "Data column for the y-axis", + "title": "Y-Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "zValue": { + "propertyOrder": 8, + "type": "string", + "description": "Data column to determine bubble size", + "title": "Z-Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "enableColor": { + "propertyOrder": 9, + "type": "boolean", + "default": false, + "description": "Colors bubbles using a data column", + "title": "Enable Color" + }, + "colorCategory": { + "propertyOrder": 10, + "type": "string", + "description": "Picks data column to color bubbles with if color is enabled", + "title": "Color-Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "xValue", + "yValue", + "zValue", + "enableColor", + "colorCategory" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "BubbleChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Bubble Chart", + "operatorDescription": "a 3D Scatter Plot; Bubbles are graphed using x and y labels, and their sizes determined by a z-value.", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "RedditSearch", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "clientId": { + "propertyOrder": 4, + "type": "string", + "description": "Client id that uses to access Reddit API", + "title": "Client Id" + }, + "clientSecret": { + "propertyOrder": 5, + "type": "string", + "description": "Client secret that uses to access Reddit API", + "title": "Client Secret" + }, + "query": { + "propertyOrder": 6, + "type": "string", + "description": "Search query", + "title": "Query" + }, + "limit": { + "propertyOrder": 7, + "type": "integer", + "default": 100, + "description": "Up to 1000", + "title": "Limit" + }, + "sorting": { + "propertyOrder": 8, + "type": "string", + "default": "none", + "enum": [ + "none", + "controversial", + "gilded", + "hot", + "new", + "rising", + "top" + ], + "description": "The sorting method, hot, new, etc.", + "title": "Sorting" + } + }, + "required": [ + "clientId", + "clientSecret", + "query", + "limit", + "sorting" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "RedditSearch" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Reddit Search", + "operatorDescription": "Search for recent posts with python-wrapped Reddit API, PRAW", + "operatorGroupName": "External API", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SVRTrainer", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "paraList": { + "propertyOrder": 4, + "type": "array", + "items": { + "$ref": "#/definitions/HyperParameters(SklearnAdvancedSVRParameters)" + }, + "title": "Parameter Setting" + }, + "groundTruthAttribute": { + "propertyOrder": 5, + "type": "string", + "description": "Ground truth attribute column", + "title": "Ground Truth Attribute Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Selected Features": { + "propertyOrder": 8, + "type": "array", + "items": { + "type": "string" + }, + "description": "Features used to train the model", + "title": "Selected Features", + "autofill": "attributeNameList", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "paraList", + "groundTruthAttribute", + "Selected Features" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SVRTrainer" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "HyperParameters(SklearnAdvancedSVRParameters)": { + "type": "object", + "additionalProperties": false, + "properties": { + "parameter": { + "propertyOrder": 1, + "type": "string", + "enum": [ + "C", + "kernel", + "gamma", + "degree", + "coef0", + "tol", + "probability", + "verbose", + "epsilon", + "cache_size", + "max_iter" + ], + "description": "Choose the name of the parameter", + "title": "Parameter" + }, + "parametersSource": { + "propertyOrder": 2, + "type": "boolean", + "default": false, + "description": "Parameter from workflow", + "title": "Workflow" + }, + "attribute": { + "propertyOrder": 3, + "nullable": true, + "type": "string", + "title": "Attribute", + "autofill": "attributeName", + "hideTarget": "parametersSource", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 1 + }, + "value": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "title": "Value", + "hideTarget": "parametersSource", + "hideType": "equals", + "hideExpectedValue": "true", + "hideOnNull": true + } + }, + "required": [ + "parameter", + "parametersSource" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "SVM Regressor", + "operatorDescription": "Sklearn SVM Regressor Operator", + "operatorGroupName": "Advanced Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "parameter", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "RUDF", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "code": { + "propertyOrder": 4, + "type": "string", + "default": "# If using Table API:\n# function(table, port) { \n# return (table) \n# }\n\n# If using Tuple API:\n# library(coro)\n# coro::generator(function(tuple, port) {\n# yield (tuple)\n# })", + "description": "Input your code here", + "title": "R UDF Script" + }, + "workers": { + "propertyOrder": 5, + "type": "integer", + "default": 1, + "description": "Specify how many parallel workers to lunch", + "title": "Worker count" + }, + "useTupleAPI": { + "propertyOrder": 6, + "type": "boolean", + "default": false, + "description": "Check this box to use Tuple API, leave unchecked to use Table API", + "title": "Use Tuple API?" + }, + "retainInputColumns": { + "propertyOrder": 7, + "type": "boolean", + "default": true, + "description": "Keep the original input columns?", + "title": "Retain input columns" + }, + "outputColumns": { + "propertyOrder": 8, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + }, + "description": "Name of the newly added output columns that the UDF will produce, if any", + "title": "Extra output column(s)" + } + }, + "required": [ + "code", + "workers", + "useTupleAPI", + "retainInputColumns" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "RUDF" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "Attribute": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Attribute Name" + }, + "attributeType": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "enum": [ + "string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + } + } + } + } + }, + "additionalMetadata": { + "userFriendlyName": "R UDF", + "operatorDescription": "User-defined function operator in R script", + "operatorGroupName": "R", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": true, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "BoxViolinPlot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "value": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "value": { + "propertyOrder": 1, + "type": "string", + "description": "Data column for box plot", + "title": "Value Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Quartile Method": { + "propertyOrder": 2, + "type": "string", + "default": "linear", + "enum": [ + "linear", + "inclusive", + "exclusive" + ], + "title": "Quartile Method" + }, + "horizontalOrientation": { + "propertyOrder": 3, + "type": "boolean", + "default": false, + "description": "Orientation style", + "title": "Horizontal Orientation" + }, + "violinPlot": { + "propertyOrder": 4, + "type": "boolean", + "default": false, + "description": "Check this box to overlay a violin plot on the box plot; otherwise, show only the box plot", + "title": "Violin Plot" + }, + "dummyPropertyList": { + "propertyOrder": 5, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + } + }, + "required": [ + "value", + "Quartile Method", + "horizontalOrientation", + "violinPlot" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "BoxViolinPlot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Box/Violin Plot", + "operatorDescription": "Visualize data using either a Box Plot or a Violin Plot. Box plots are drawn as a box with a vertical line down the middle which is mean value, and has horizontal lines attached to each side (known as “whiskers”). Violin plots provide more detail by showing a smoothed density curve on each side, and also include a box plot inside for comparison.", + "operatorGroupName": "Statistical", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnAdaptiveBoosting", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnAdaptiveBoosting" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Adaptive Boosting", + "operatorDescription": "Sklearn Adaptive Boosting Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Scatterplot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "xColumn": { + "enum": [ + "integer", + "double" + ] + }, + "yColumn": { + "enum": [ + "integer", + "double" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "xColumn": { + "propertyOrder": 4, + "type": "string", + "description": "X Column", + "title": "X-Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "yColumn": { + "propertyOrder": 5, + "type": "string", + "description": "Y Column", + "title": "Y-Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "colorColumn": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Dots will be assigned different colors based on their values of this column", + "title": "Color-Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "xLogScale": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Values in X-column is log-scaled", + "title": "log scale X" + }, + "yLogScale": { + "propertyOrder": 8, + "type": "boolean", + "default": false, + "description": "Values in Y-column is log-scaled", + "title": "log scale Y" + }, + "hoverName": { + "propertyOrder": 9, + "nullable": true, + "type": "string", + "description": "Column value to display when a dot is hovered over", + "title": "Hover column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "xColumn", + "yColumn", + "xLogScale", + "yLogScale" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Scatterplot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Scatter Plot", + "operatorDescription": "View the result in a scatterplot", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnPerceptron", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnPerceptron" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Linear Perceptron", + "operatorDescription": "Sklearn Linear Perceptron Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "KeywordSearch", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "attribute": { + "propertyOrder": 4, + "type": "string", + "description": "column to search keyword on", + "title": "attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "keyword": { + "propertyOrder": 5, + "type": "string", + "description": "keywords", + "title": "keywords" + } + }, + "required": [ + "attribute", + "keyword" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "KeywordSearch" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Keyword Search", + "operatorDescription": "Search for keyword(s) in a string column", + "operatorGroupName": "Search", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": true, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "PythonUDFV2", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "code": { + "propertyOrder": 4, + "type": "string", + "default": "# Choose from the following templates:\n# \n# from pytexera import *\n# \n# class ProcessTupleOperator(UDFOperatorV2):\n# \n# @overrides\n# def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:\n# yield tuple_\n# \n# class ProcessBatchOperator(UDFBatchOperator):\n# BATCH_SIZE = 10 # must be a positive integer\n# \n# @overrides\n# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]:\n# yield batch\n# \n# class ProcessTableOperator(UDFTableOperator):\n# \n# @overrides\n# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:\n# yield table\n", + "description": "Input your code here", + "title": "Python script" + }, + "workers": { + "propertyOrder": 5, + "type": "integer", + "default": 1, + "description": "Specify how many parallel workers to lunch", + "title": "Worker count" + }, + "retainInputColumns": { + "propertyOrder": 6, + "type": "boolean", + "default": true, + "description": "Keep the original input columns?", + "title": "Retain input columns" + }, + "outputColumns": { + "propertyOrder": 7, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + }, + "description": "Name of the newly added output columns that the UDF will produce, if any", + "title": "Extra output column(s)" + } + }, + "required": [ + "code", + "workers", + "retainInputColumns" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "PythonUDFV2" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "Attribute": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Attribute Name" + }, + "attributeType": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "enum": [ + "string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + } + } + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Python UDF", + "operatorDescription": "User-defined function operator in Python script", + "operatorGroupName": "Python", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": true, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": true, + "dynamicOutputPorts": true, + "supportReconfiguration": true, + "allowPortCustomization": true + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnLogisticRegression", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnLogisticRegression" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Logistic Regression", + "operatorDescription": "Sklearn Logistic Regression Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnRandomForest", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnRandomForest" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Random Forest", + "operatorDescription": "Sklearn Random Forest Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "TypeCasting", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "typeCastingUnits": { + "propertyOrder": 4, + "type": "array", + "items": { + "$ref": "#/definitions/TypeCastingUnit" + }, + "description": "Multiple type castings", + "title": "TypeCasting Units" + } + }, + "required": [ + "typeCastingUnits" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "TypeCasting" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "TypeCastingUnit": { + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "attribute": { + "allOf": [ + { + "if": { + "resultType": { + "valEnum": [ + "integer" + ] + } + }, + "then": { + "enum": [ + "string", + "long", + "double", + "boolean" + ] + } + }, + { + "if": { + "resultType": { + "valEnum": [ + "double" + ] + } + }, + "then": { + "enum": [ + "string", + "integer", + "long", + "boolean" + ] + } + }, + { + "if": { + "resultType": { + "valEnum": [ + "boolean" + ] + } + }, + "then": { + "enum": [ + "string", + "integer", + "long", + "double" + ] + } + }, + { + "if": { + "resultType": { + "valEnum": [ + "long" + ] + } + }, + "then": { + "enum": [ + "string", + "integer", + "double", + "boolean", + "timestamp" + ] + } + }, + { + "if": { + "resultType": { + "valEnum": [ + "timestamp" + ] + } + }, + "then": { + "enum": [ + "string", + "long" + ] + } + } + ] + } + }, + "properties": { + "attribute": { + "propertyOrder": 1, + "type": "string", + "description": "Attribute for type casting", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "resultType": { + "propertyOrder": 2, + "type": "string", + "enum": [ + "string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "description": "Result type after type casting", + "title": "Cast type" + } + }, + "required": [ + "attribute", + "resultType" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Type Casting", + "operatorDescription": "Cast between types", + "operatorGroupName": "Data Cleaning", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnGaussianNaiveBayes", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnGaussianNaiveBayes" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Gaussian Naive Bayes", + "operatorDescription": "Sklearn Gaussian Naive Bayes Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "AsterixDBSource", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "host": { + "propertyOrder": 4, + "type": "string", + "title": "Host", + "enable-presets": true + }, + "port": { + "propertyOrder": 5, + "type": "string", + "default": "default", + "description": "A port number or 'default'", + "title": "Port", + "enable-presets": true + }, + "database": { + "propertyOrder": 6, + "type": "string", + "title": "Database", + "enable-presets": true + }, + "table": { + "propertyOrder": 7, + "type": "string", + "title": "Table Name", + "enable-presets": true + }, + "limit": { + "propertyOrder": 8, + "nullable": true, + "type": "integer", + "description": "max output count", + "title": "Limit" + }, + "offset": { + "propertyOrder": 9, + "nullable": true, + "type": "integer", + "description": "starting point of output", + "title": "Offset" + }, + "keywordSearch": { + "propertyOrder": 10, + "nullable": true, + "type": "boolean", + "default": false, + "title": "Keyword Search?", + "toggleHidden": [ + "keywordSearchByColumn", + "keywords" + ] + }, + "keywordSearchByColumn": { + "propertyOrder": 11, + "nullable": true, + "type": "string", + "title": "Keyword Search Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "keywords": { + "propertyOrder": 12, + "nullable": true, + "type": "string", + "description": "\"['hello', 'world'], {'mode':'any'}\" OR \"['hello', 'world'], {'mode':'all'}\"", + "title": "Keywords to Search", + "widget": { + "formlyConfig": { + "type": "textarea", + "templateOptions": { + "autosize": true, + "autosizeMinRows": 3 + } + } + } + }, + "progressive": { + "propertyOrder": 13, + "nullable": true, + "type": "boolean", + "default": false, + "title": "Progressive?", + "toggleHidden": [ + "batchByColumn", + "min", + "max", + "interval" + ] + }, + "batchByColumn": { + "propertyOrder": 14, + "nullable": true, + "type": "string", + "title": "Batch by Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "min": { + "propertyOrder": 15, + "nullable": true, + "type": "string", + "default": "auto", + "title": "Min", + "dependOn": "batchByColumn" + }, + "max": { + "propertyOrder": 16, + "nullable": true, + "type": "string", + "default": "auto", + "title": "Max", + "dependOn": "batchByColumn" + }, + "interval": { + "propertyOrder": 17, + "type": "integer", + "default": 1000000000, + "title": "Batch by Interval", + "dependOn": "batchByColumn" + }, + "geoSearch": { + "propertyOrder": 18, + "nullable": true, + "type": "boolean", + "default": false, + "title": "Geo Search?", + "toggleHidden": [ + "geoSearchByColumns", + "geoSearchBoundingBox" + ] + }, + "geoSearchByColumns": { + "propertyOrder": 19, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "description": "column(s) to check if any of them is in the bounding box below", + "title": "Geo Search By Columns", + "autofill": "attributeNameList", + "autofillAttributeOnPort": 0 + }, + "geoSearchBoundingBox": { + "propertyOrder": 20, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "description": "at least 2 entries should be provided to form a bounding box. format of each entry: long, lat", + "title": "Geo Search Bounding Box" + }, + "regexSearch": { + "propertyOrder": 21, + "nullable": true, + "type": "boolean", + "default": false, + "title": "Regex Search?", + "toggleHidden": [ + "regexSearchByColumn", + "regex" + ] + }, + "regexSearchByColumn": { + "propertyOrder": 22, + "nullable": true, + "type": "string", + "title": "Regex Search By Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "regex": { + "propertyOrder": 23, + "nullable": true, + "type": "string", + "title": "Regex to Search", + "widget": { + "formlyConfig": { + "type": "textarea", + "templateOptions": { + "autosize": true, + "autosizeMinRows": 3 + } + } + } + }, + "filterCondition": { + "propertyOrder": 24, + "nullable": true, + "type": "boolean", + "default": false, + "title": "Filter Condition?", + "toggleHidden": [ + "predicates" + ] + }, + "predicates": { + "propertyOrder": 27, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/FilterPredicate" + }, + "description": "multiple predicates in OR", + "title": "Predicates" + } + }, + "required": [ + "host", + "port", + "database", + "table", + "interval" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "AsterixDBSource" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "FilterPredicate": { + "type": "object", + "additionalProperties": false, + "properties": { + "attribute": { + "propertyOrder": 1, + "type": "string", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "condition": { + "propertyOrder": 2, + "type": "string", + "enum": [ + "=", + ">", + ">=", + "<", + "<=", + "!=", + "is null", + "is not null" + ], + "title": "Condition" + }, + "value": { + "propertyOrder": 3, + "nullable": true, + "type": "string", + "title": "Value", + "hideTarget": "condition", + "hideType": "regex", + "hideExpectedValue": "is null|is not null" + } + }, + "required": [ + "attribute", + "condition" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "AsterixDB Source", + "operatorDescription": "Read data from a AsterixDB instance", + "operatorGroupName": "Database Connector", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "DualInputPortsPythonUDFV2", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "code": { + "propertyOrder": 4, + "type": "string", + "default": "# Choose from the following templates:\n# \n# from pytexera import *\n# \n# class ProcessTupleOperator(UDFOperatorV2):\n# \n# @overrides\n# def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:\n# yield tuple_\n# \n# class ProcessBatchOperator(UDFBatchOperator):\n# BATCH_SIZE = 10 # must be a positive integer\n# \n# @overrides\n# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]:\n# yield batch\n# \n# class ProcessTableOperator(UDFTableOperator):\n# \n# @overrides\n# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:\n# yield table\n", + "description": "Input your code here", + "title": "Python script" + }, + "workers": { + "propertyOrder": 5, + "type": "integer", + "default": 1, + "description": "Specify how many parallel workers to lunch", + "title": "Worker count" + }, + "retainInputColumns": { + "propertyOrder": 6, + "type": "boolean", + "default": true, + "description": "Keep the original input columns?", + "title": "Retain input columns" + }, + "outputColumns": { + "propertyOrder": 7, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Attribute" + }, + "description": "Name of the newly added output columns that the UDF will produce, if any", + "title": "Extra output column(s)" + } + }, + "required": [ + "code", + "workers", + "retainInputColumns" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "DualInputPortsPythonUDFV2" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "Attribute": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Attribute Name" + }, + "attributeType": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "enum": [ + "string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + } + } + } + } + }, + "additionalMetadata": { + "userFriendlyName": "2-in Python UDF", + "operatorDescription": "User-defined function operator in Python script", + "operatorGroupName": "Python", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "model", + "allowMultiLinks": true, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "tuples", + "allowMultiLinks": true, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Histogram", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "color": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "description": "Column for differentiating data by its value.", + "title": "Color Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "separateBy": { + "propertyOrder": 5, + "nullable": true, + "type": "string", + "description": "Column for separating histogram chart by its value.", + "title": "SeparateBy Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "marginal": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Distribution type (rug, box, violin).", + "title": "Distribution Type" + }, + "pattern": { + "propertyOrder": 7, + "nullable": true, + "type": "string", + "description": "Add texture to the chart based on an attribute", + "title": "Pattern", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "value": { + "propertyOrder": 10, + "type": "string", + "description": "Column for counting values.", + "title": "Value Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "value" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Histogram" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Histogram", + "operatorDescription": "Visualize data in a Histogram Chart", + "operatorGroupName": "Statistical", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnDummyClassifier", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnDummyClassifier" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Dummy Classifier", + "operatorDescription": "Sklearn Dummy Classifier Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Distinct", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + } + }, + "required": [], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Distinct" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Distinct", + "operatorDescription": "Remove duplicate tuples", + "operatorGroupName": "Data Cleaning", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "NetworkGraph", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "source": { + "propertyOrder": 4, + "type": "string", + "description": "Source node for edge in graph", + "title": "Source Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "destination": { + "propertyOrder": 5, + "type": "string", + "description": "Destination node for edge in graph", + "title": "Destination Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "title": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "default": "Network Graph", + "title": "Title" + } + }, + "required": [ + "source", + "destination" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "NetworkGraph" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Network Graph", + "operatorDescription": "Visualize data in a network graph", + "operatorGroupName": "Scientific", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "WaterfallChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "xColumn": { + "propertyOrder": 6, + "type": "string", + "description": "The column representing categories or stages", + "title": "X Axis Values", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "yColumn": { + "propertyOrder": 7, + "type": "string", + "description": "The column representing numeric values for each stage", + "title": "Y Axis Values", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "xColumn", + "yColumn" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "WaterfallChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Waterfall Chart", + "operatorDescription": "Visualize data as a waterfall chart", + "operatorGroupName": "Financial", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Limit", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "limit": { + "propertyOrder": 4, + "type": "integer", + "description": "the max number of output rows", + "title": "Limit" + } + }, + "required": [ + "limit" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Limit" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Limit", + "operatorDescription": "Limit the number of output rows", + "operatorGroupName": "Data Cleaning", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": true, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Scorer", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "isRegression": { + "propertyOrder": 4, + "type": "boolean", + "default": false, + "description": "Choose to solve a regression task", + "title": "Regression" + }, + "actualValueColumn": { + "propertyOrder": 5, + "type": "string", + "description": "Specify the label attribute", + "title": "Actual Value", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "predictValueColumn": { + "propertyOrder": 6, + "type": "string", + "description": "Specify the attribute generated by the model", + "title": "Predicted Value", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "classificationFlag": { + "propertyOrder": 9, + "nullable": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "Accuracy", + "Precision Score", + "Recall Score", + "F1 Score" + ] + }, + "description": "Select classification tasks metrics", + "title": "Scorer Functions", + "hideTarget": "isRegression", + "hideType": "equals", + "hideExpectedValue": "true" + }, + "regressionFlag": { + "propertyOrder": 10, + "nullable": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "MSE", + "RMSE", + "MAE", + "R2" + ] + }, + "description": "Select regression tasks metrics", + "title": "Scorer Functions", + "hideTarget": "isRegression", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "isRegression", + "actualValueColumn", + "predictValueColumn" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Scorer" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Machine Learning Scorer", + "operatorDescription": "Scorer for machine learning models", + "operatorGroupName": "Machine Learning General", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnExtraTrees", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnExtraTrees" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Extra Trees", + "operatorDescription": "Sklearn Extra Trees Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "FileScan", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "fileName": { + "propertyOrder": 4, + "type": "string", + "title": "File" + }, + "encoding": { + "propertyOrder": 5, + "type": "string", + "default": "UTF_8", + "enum": [ + "UTF_8", + "UTF_16", + "US_ASCII" + ], + "title": "Encoding", + "hideTarget": "attributeType", + "hideType": "equals", + "hideExpectedValue": "binary" + }, + "extract": { + "propertyOrder": 6, + "type": "boolean", + "default": false, + "title": "Extract" + }, + "outputFileName": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "title": "Include Filename", + "hideTarget": "extract", + "hideType": "equals", + "hideExpectedValue": "false" + }, + "attributeType": { + "propertyOrder": 8, + "type": "string", + "default": "string", + "enum": [ + "string", + "single string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + }, + "attributeName": { + "propertyOrder": 9, + "type": "string", + "default": "line", + "title": "Attribute Name" + }, + "fileScanLimit": { + "propertyOrder": 10, + "nullable": true, + "type": "integer", + "title": "Limit", + "hideTarget": "attributeType", + "hideType": "regex", + "hideExpectedValue": "^binary$|^single string$" + }, + "fileScanOffset": { + "propertyOrder": 11, + "nullable": true, + "type": "integer", + "title": "Offset", + "hideTarget": "attributeType", + "hideType": "regex", + "hideExpectedValue": "^binary$|^single string$" + } + }, + "required": [ + "fileName", + "encoding", + "extract", + "outputFileName", + "attributeType", + "attributeName" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "FileScan" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": " File Scan", + "operatorDescription": "Scan data from a file", + "operatorGroupName": "Data Input", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "GanttChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "start": { + "enum": [ + "timestamp" + ] + }, + "finish": { + "enum": [ + "timestamp" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "pattern": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "description": "Add texture to the chart based on an attribute", + "title": "Pattern", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "start": { + "propertyOrder": 7, + "type": "string", + "description": "the start timestamp of the task", + "title": "Start Datetime Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "finish": { + "propertyOrder": 8, + "type": "string", + "description": "the end timestamp of the task", + "title": "Finish Datetime Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "task": { + "propertyOrder": 9, + "type": "string", + "description": "the name of the task", + "title": "Task Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "color": { + "propertyOrder": 10, + "nullable": true, + "type": "string", + "description": "column to color tasks", + "title": "Color Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "start", + "finish", + "task" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "GanttChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Gantt Chart", + "operatorDescription": "A Gantt chart is a type of bar chart that illustrates a project schedule. The chart lists the tasks to be performed on the vertical axis, and time intervals on the horizontal axis. The width of the horizontal bars in the graph shows the duration of each activity.", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "TernaryPlot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "firstVariable": { + "propertyOrder": 6, + "type": "string", + "description": "First variable data field", + "title": "Variable 1", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "secondVariable": { + "propertyOrder": 7, + "type": "string", + "description": "Second variable data field", + "title": "Variable 2", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "thirdVariable": { + "propertyOrder": 8, + "type": "string", + "description": "Third variable data field", + "title": "Variable 3", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "colorEnabled": { + "propertyOrder": 9, + "type": "boolean", + "default": false, + "description": "Optionally color points using a data field", + "title": "Categorize by Color" + }, + "colorDataField": { + "propertyOrder": 10, + "nullable": true, + "type": "string", + "description": "Specify the data field to color", + "title": "Color Data Field", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "firstVariable", + "secondVariable", + "thirdVariable", + "colorEnabled" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "TernaryPlot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Ternary Plot", + "operatorDescription": "Points are graphed on a Ternary Plot using 3 specified data fields", + "operatorGroupName": "Scientific", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SVCTrainer", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "paraList": { + "propertyOrder": 4, + "type": "array", + "items": { + "$ref": "#/definitions/HyperParameters(SklearnAdvancedSVCParameters)" + }, + "title": "Parameter Setting" + }, + "groundTruthAttribute": { + "propertyOrder": 5, + "type": "string", + "description": "Ground truth attribute column", + "title": "Ground Truth Attribute Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Selected Features": { + "propertyOrder": 8, + "type": "array", + "items": { + "type": "string" + }, + "description": "Features used to train the model", + "title": "Selected Features", + "autofill": "attributeNameList", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "paraList", + "groundTruthAttribute", + "Selected Features" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SVCTrainer" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "HyperParameters(SklearnAdvancedSVCParameters)": { + "type": "object", + "additionalProperties": false, + "properties": { + "parameter": { + "propertyOrder": 1, + "type": "string", + "enum": [ + "C", + "kernel", + "gamma", + "degree", + "coef0", + "tol", + "probability" + ], + "description": "Choose the name of the parameter", + "title": "Parameter" + }, + "parametersSource": { + "propertyOrder": 2, + "type": "boolean", + "default": false, + "description": "Parameter from workflow", + "title": "Workflow" + }, + "attribute": { + "propertyOrder": 3, + "nullable": true, + "type": "string", + "title": "Attribute", + "autofill": "attributeName", + "hideTarget": "parametersSource", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 1 + }, + "value": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "title": "Value", + "hideTarget": "parametersSource", + "hideType": "equals", + "hideExpectedValue": "true", + "hideOnNull": true + } + }, + "required": [ + "parameter", + "parametersSource" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "SVM Classifier", + "operatorDescription": "Sklearn SVM Classifier Operator", + "operatorGroupName": "Advanced Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "parameter", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnLinearRegression", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "degree": { + "propertyOrder": 5, + "type": "integer", + "description": "Degree of polynomial function", + "title": "Degree" + } + }, + "required": [ + "target", + "degree" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnLinearRegression" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Linear Regression", + "operatorDescription": "Sklearn Linear Regression Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "MySQLSource", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "host": { + "propertyOrder": 4, + "type": "string", + "title": "Host", + "enable-presets": true + }, + "port": { + "propertyOrder": 5, + "type": "string", + "default": "default", + "description": "A port number or 'default'", + "title": "Port", + "enable-presets": true + }, + "database": { + "propertyOrder": 6, + "type": "string", + "title": "Database", + "enable-presets": true + }, + "table": { + "propertyOrder": 7, + "type": "string", + "title": "Table Name", + "enable-presets": true + }, + "username": { + "propertyOrder": 8, + "type": "string", + "title": "Username", + "enable-presets": true + }, + "password": { + "propertyOrder": 9, + "type": "string", + "title": "Password", + "widget": { + "formlyConfig": { + "templateOptions": { + "type": "password" + } + } + } + }, + "limit": { + "propertyOrder": 10, + "nullable": true, + "type": "integer", + "description": "max output count", + "title": "Limit" + }, + "offset": { + "propertyOrder": 11, + "nullable": true, + "type": "integer", + "description": "starting point of output", + "title": "Offset" + }, + "keywordSearch": { + "propertyOrder": 12, + "nullable": true, + "type": "boolean", + "default": false, + "title": "Keyword Search?", + "toggleHidden": [ + "keywordSearchByColumn", + "keywords" + ] + }, + "keywordSearchByColumn": { + "propertyOrder": 13, + "nullable": true, + "type": "string", + "title": "Keyword Search Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "keywords": { + "propertyOrder": 14, + "nullable": true, + "type": "string", + "title": "Keywords to Search", + "widget": { + "formlyConfig": { + "type": "textarea", + "templateOptions": { + "autosize": true, + "autosizeMinRows": 3 + } + } + } + }, + "progressive": { + "propertyOrder": 15, + "nullable": true, + "type": "boolean", + "default": false, + "title": "Progressive?", + "toggleHidden": [ + "batchByColumn", + "min", + "max", + "interval" + ] + }, + "batchByColumn": { + "propertyOrder": 16, + "nullable": true, + "type": "string", + "title": "Batch by Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "min": { + "propertyOrder": 17, + "nullable": true, + "type": "string", + "default": "auto", + "title": "Min", + "dependOn": "batchByColumn" + }, + "max": { + "propertyOrder": 18, + "nullable": true, + "type": "string", + "default": "auto", + "title": "Max", + "dependOn": "batchByColumn" + }, + "interval": { + "propertyOrder": 19, + "type": "integer", + "default": 1000000000, + "title": "Batch by Interval", + "dependOn": "batchByColumn" + } + }, + "required": [ + "host", + "port", + "database", + "table", + "username", + "password", + "interval" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "MySQLSource" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "MySQL Source", + "operatorDescription": "Read data from a MySQL instance", + "operatorGroupName": "Database Connector", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "CSVOldFileScan", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "fileName": { + "propertyOrder": 4, + "type": "string", + "title": "File" + }, + "fileEncoding": { + "propertyOrder": 5, + "type": "string", + "default": "UTF_8", + "enum": [ + "UTF_8", + "UTF_16", + "US_ASCII" + ], + "description": "decoding charset to use on input", + "title": "File Encoding" + }, + "limit": { + "propertyOrder": 6, + "nullable": true, + "type": "integer", + "description": "max output count", + "title": "Limit" + }, + "offset": { + "propertyOrder": 7, + "nullable": true, + "type": "integer", + "description": "starting point of output", + "title": "Offset" + }, + "customDelimiter": { + "propertyOrder": 8, + "nullable": true, + "type": "string", + "default": ",", + "description": "delimiter to separate each line into fields", + "title": "Delimiter" + }, + "hasHeader": { + "propertyOrder": 9, + "type": "boolean", + "default": true, + "description": "whether the CSV file contains a header line", + "title": "Header" + } + }, + "required": [ + "fileName", + "fileEncoding", + "hasHeader" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "CSVOldFileScan" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "CSVOld File Scan", + "operatorDescription": "Scan data from a CSVOld file", + "operatorGroupName": "Data Input", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "CSVFileScan", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "fileName": { + "propertyOrder": 4, + "type": "string", + "title": "File" + }, + "fileEncoding": { + "propertyOrder": 5, + "type": "string", + "default": "UTF_8", + "enum": [ + "UTF_8", + "UTF_16", + "US_ASCII" + ], + "description": "decoding charset to use on input", + "title": "File Encoding" + }, + "limit": { + "propertyOrder": 6, + "nullable": true, + "type": "integer", + "description": "max output count", + "title": "Limit" + }, + "offset": { + "propertyOrder": 7, + "nullable": true, + "type": "integer", + "description": "starting point of output", + "title": "Offset" + }, + "customDelimiter": { + "propertyOrder": 8, + "nullable": true, + "type": "string", + "default": ",", + "description": "delimiter to separate each line into fields", + "title": "Delimiter" + }, + "hasHeader": { + "propertyOrder": 9, + "type": "boolean", + "default": true, + "description": "whether the CSV file contains a header line", + "title": "Header" + } + }, + "required": [ + "fileName", + "fileEncoding", + "hasHeader" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "CSVFileScan" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "CSV File Scan", + "operatorDescription": "Scan data from a CSV file", + "operatorGroupName": "Data Input", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "FunnelPlot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "title": "string" + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "x": { + "propertyOrder": 4, + "type": "string", + "description": "Data column for the x-axis", + "title": "X Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "y": { + "propertyOrder": 5, + "type": "string", + "description": "Data column for the y-axis", + "title": "Y Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "color": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Column to categorically colorize funnel sections", + "title": "Color Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "x", + "y" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "FunnelPlot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Funnel Plot", + "operatorDescription": "Visualize data in a Funnel Plot", + "operatorGroupName": "Financial", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Projection", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "isDrop": { + "propertyOrder": 4, + "type": "boolean", + "default": false, + "description": "check to drop the selected attributes", + "title": "Drop Option" + }, + "attributes": { + "propertyOrder": 5, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/AttributeUnit" + }, + "title": "Attributes" + } + }, + "required": [ + "isDrop" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Projection" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "AttributeUnit": { + "type": "object", + "additionalProperties": false, + "properties": { + "originalAttribute": { + "propertyOrder": 1, + "type": "string", + "description": "Attribute name in the schema", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "alias": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "description": "Renamed attribute name", + "title": "Alias" + } + }, + "required": [ + "originalAttribute" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Projection", + "operatorDescription": "Keeps or drops the column", + "operatorGroupName": "Data Cleaning", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Filter", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "predicates": { + "propertyOrder": 6, + "type": "array", + "items": { + "$ref": "#/definitions/FilterPredicate" + }, + "description": "multiple predicates in OR", + "title": "Predicates" + } + }, + "required": [ + "predicates" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Filter" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "FilterPredicate": { + "type": "object", + "additionalProperties": false, + "properties": { + "attribute": { + "propertyOrder": 1, + "type": "string", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "condition": { + "propertyOrder": 2, + "type": "string", + "enum": [ + "=", + ">", + ">=", + "<", + "<=", + "!=", + "is null", + "is not null" + ], + "title": "Condition" + }, + "value": { + "propertyOrder": 3, + "nullable": true, + "type": "string", + "title": "Value", + "hideTarget": "condition", + "hideType": "regex", + "hideExpectedValue": "is null|is not null" + } + }, + "required": [ + "attribute", + "condition" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Filter", + "operatorDescription": "Performs a filter operation", + "operatorGroupName": "Data Cleaning", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": true, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnRidge", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnRidge" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Ridge Regression", + "operatorDescription": "Sklearn Ridge Regression Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Intersect", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + } + }, + "required": [], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Intersect" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Intersect", + "operatorDescription": "Take the intersect of two inputs", + "operatorGroupName": "Set", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnPrediction", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "Model Attribute": { + "propertyOrder": 6, + "type": "string", + "default": "model", + "description": "attribute corresponding to ML model", + "title": "Model Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Output Attribute Name": { + "propertyOrder": 7, + "type": "string", + "default": "prediction", + "description": "attribute name of the prediction result", + "title": "Output Attribute Name" + }, + "Ground Truth Attribute Name to Ignore": { + "propertyOrder": 8, + "nullable": true, + "type": "string", + "description": "attribute name of the ground truth", + "title": "Ground Truth Attribute Name to Ignore", + "autofill": "attributeName", + "autofillAttributeOnPort": 1 + } + }, + "required": [ + "Model Attribute", + "Output Attribute Name" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnPrediction" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Sklearn Prediction", + "operatorDescription": "Skleanr Prediction Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "model", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SymmetricDifference", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + } + }, + "required": [], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SymmetricDifference" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "SymmetricDifference", + "operatorDescription": "find the symmetric difference (the set of elements which are in either of the sets, but not in their intersection) of two inputs", + "operatorGroupName": "Set", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "FigureFactoryTable", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "fontSize": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "description": "Font size of the Figure Factory Table", + "title": "Font Size" + }, + "fontColor": { + "propertyOrder": 5, + "nullable": true, + "type": "string", + "description": "Font color of the Figure Factory Table", + "title": "Font Color (Hex Code)" + }, + "rowHeight": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Row height of the Figure Factory Table", + "title": "Row Height" + }, + "add attribute": { + "propertyOrder": 9, + "type": "array", + "items": { + "$ref": "#/definitions/FigureFactoryTableConfig" + }, + "description": "List of columns to include in the figure factory table", + "title": "Add attribute" + } + }, + "required": [ + "add attribute" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "FigureFactoryTable" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "FigureFactoryTableConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "type": "string", + "title": "Attribute Name", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "attributeName" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Figure Factory Table", + "operatorDescription": "Visualize data in a figure factory table", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "FilledAreaPlot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "x": { + "propertyOrder": 4, + "type": "string", + "description": "The attribute for your x-axis", + "title": "X-axis Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "y": { + "propertyOrder": 5, + "type": "string", + "description": "The attribute for your y-axis", + "title": "Y-axis Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "lineGroup": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "The attribute for group of each line", + "title": "Line Group", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "color": { + "propertyOrder": 7, + "nullable": true, + "type": "string", + "description": "Choose an attribute to color the plot", + "title": "Color", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "facetColumn": { + "propertyOrder": 8, + "type": "boolean", + "description": "Do you want to split the graph", + "title": "Split Plot by Line Group" + }, + "pattern": { + "propertyOrder": 9, + "nullable": true, + "type": "string", + "description": "Add texture to the chart based on an attribute", + "title": "Pattern", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "x", + "y", + "facetColumn" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "FilledAreaPlot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Filled Area Plot", + "operatorDescription": "Visualize data in filled area plot", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnRidgeCV", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnRidgeCV" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Ridge Regression Cross Validation", + "operatorDescription": "Sklearn Ridge Regression Cross Validation Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "IcicleChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "value": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "hierarchy": { + "propertyOrder": 4, + "type": "array", + "items": { + "$ref": "#/definitions/HierarchySection" + }, + "description": "hierarchy of attributes from a root (higher-level category) to leaves (lower-level category)", + "title": "Hierarchy Path" + }, + "value": { + "propertyOrder": 7, + "type": "string", + "description": "the value associated with the size of each sector in the chart", + "title": "Value Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "hierarchy", + "value" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "IcicleChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "HierarchySection": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "type": "string", + "title": "Attribute Name", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "attributeName" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Icicle Chart", + "operatorDescription": "Visualize hierarchical data from root to leaves", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Regex", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "caseInsensitive": { + "propertyOrder": 4, + "type": "boolean", + "default": false, + "description": "regex match is case sensitive", + "title": "Case Insensitive" + }, + "attribute": { + "propertyOrder": 7, + "type": "string", + "description": "column to search regex on", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "regex": { + "propertyOrder": 8, + "type": "string", + "description": "regular expression", + "title": "Regex" + } + }, + "required": [ + "caseInsensitive", + "attribute", + "regex" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Regex" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Regular Expression", + "operatorDescription": "Search a regular expression in a string column", + "operatorGroupName": "Search", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": true, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "HeatMap", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "x": { + "propertyOrder": 6, + "type": "string", + "description": "the values along the x-axis", + "title": "Value X Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "y": { + "propertyOrder": 7, + "type": "string", + "description": "the values along the y-axis", + "title": "Value Y Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Values": { + "propertyOrder": 8, + "type": "string", + "description": "the values of the heatmap", + "title": "Values", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "x", + "y", + "Values" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "HeatMap" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Heatmap", + "operatorDescription": "Visualize data in a HeatMap Chart", + "operatorGroupName": "Scientific", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "TablesPlot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "add attribute": { + "propertyOrder": 6, + "type": "array", + "items": { + "$ref": "#/definitions/TablesConfig" + }, + "description": "List of columns to include in the table chart", + "title": "Add attribute" + } + }, + "required": [ + "add attribute" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "TablesPlot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "TablesConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "type": "string", + "title": "Attribute Name", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "attributeName" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Tables Plot", + "operatorDescription": "Visualize data in a table chart.", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "HierarchyChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "value": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "hierarchyChartType": { + "propertyOrder": 4, + "type": "string", + "enum": [ + "treemap", + "sunburst" + ], + "description": "Treemap or Sunburst", + "title": "Chart Type" + }, + "hierarchy": { + "propertyOrder": 5, + "type": "array", + "items": { + "$ref": "#/definitions/HierarchySection" + }, + "description": "Hierarchy of attributes from a higher-level category to lower-level category", + "title": "Hierarchy Path" + }, + "value": { + "propertyOrder": 8, + "type": "string", + "description": "The value associated with the size of each sector in the chart", + "title": "Value Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "hierarchyChartType", + "hierarchy", + "value" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "HierarchyChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "HierarchySection": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "type": "string", + "title": "Attribute Name", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "attributeName" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Hierarchy Chart", + "operatorDescription": "Visualize data in hierarchy", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnExtraTree", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnExtraTree" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Extra Tree", + "operatorDescription": "Sklearn Extra Tree Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Sort", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "attributes": { + "propertyOrder": 4, + "type": "array", + "items": { + "$ref": "#/definitions/SortCriteriaUnit" + }, + "description": "column to perform sorting on", + "title": "Attributes" + } + }, + "required": [ + "attributes" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Sort" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "SortCriteriaUnit": { + "type": "object", + "additionalProperties": false, + "properties": { + "attribute": { + "propertyOrder": 1, + "type": "string", + "description": "Attribute name to sort by", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "sortPreference": { + "propertyOrder": 2, + "type": "string", + "enum": [ + "ASC", + "DESC" + ], + "description": "Sort preference (ASC or DESC)", + "title": "Sort Preference" + } + }, + "required": [ + "attribute", + "sortPreference" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Sort", + "operatorDescription": "Sort based on the columns and sorting methods", + "operatorGroupName": "Sort", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Scatter3DChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "title": "string" + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "x": { + "propertyOrder": 6, + "type": "string", + "description": "Data column for the x-axis", + "title": "X Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "y": { + "propertyOrder": 7, + "type": "string", + "description": "Data column for the y-axis", + "title": "Y Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "z": { + "propertyOrder": 8, + "type": "string", + "description": "Data column for the z-axis", + "title": "Z Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "x", + "y", + "z" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Scatter3DChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Scatter3D Chart", + "operatorDescription": "Visualize data in a Scatter3D Plot", + "operatorGroupName": "Advanced", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnBagging", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnBagging" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Bagging", + "operatorDescription": "Sklearn Bagging Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Difference", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + } + }, + "required": [], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Difference" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Difference", + "operatorDescription": "find the set difference of two inputs", + "operatorGroupName": "Set", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "left", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "right", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "ContourPlot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "gridSize": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "default": "10", + "description": "Grid resolution of the final image", + "title": "Grid Size" + }, + "connectGaps": { + "propertyOrder": 5, + "type": "boolean", + "default": true, + "description": "Automatically fill in the missing parts", + "title": "Connect Gaps" + }, + "x": { + "propertyOrder": 8, + "type": "string", + "description": "The column name of X-axis", + "title": "x", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "y": { + "propertyOrder": 9, + "type": "string", + "description": "The column name of Y-axis", + "title": "y", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "z": { + "propertyOrder": 10, + "type": "string", + "description": "The column name of color bar", + "title": "z", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Coloring Method": { + "propertyOrder": 11, + "nullable": true, + "type": "string", + "default": "heatmap", + "enum": [ + "heatmap", + "lines", + "none" + ], + "title": "Coloring Method" + } + }, + "required": [ + "connectGaps", + "x", + "y", + "z" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "ContourPlot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Contour Plot", + "operatorDescription": "Displays terrain or gradient variations in a Contour Plot", + "operatorGroupName": "Scientific", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "PythonLambdaFunction", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "lambdaAttributeUnits": { + "propertyOrder": 4, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/LambdaAttributeUnit" + }, + "title": "Add/Modify column(s)" + } + }, + "required": [], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "PythonLambdaFunction" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "LambdaAttributeUnit": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "type": "string", + "title": "Attribute Name", + "autofill": "attributeName", + "hideTarget": "attributeName", + "hideType": "regex", + "hideExpectedValue": "Add New Column", + "additionalEnumValue": "Add New Column", + "autofillAttributeOnPort": 0 + }, + "newAttributeName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "New Attribute Name", + "hideTarget": "attributeName", + "hideType": "regex", + "hideExpectedValue": "(?!Add New Column).*", + "hideOnNull": true + }, + "attributeType": { + "propertyOrder": 3, + "type": "string", + "enum": [ + "string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + }, + "expression": { + "propertyOrder": 4, + "type": "string", + "title": "Expression" + } + }, + "required": [ + "attributeName", + "attributeType", + "expression" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Python Lambda Function", + "operatorDescription": "Modify or add a new column with more ease", + "operatorGroupName": "Python", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": true, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "WordCloud", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "textColumn": { + "propertyOrder": 4, + "type": "string", + "title": "Text column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "topN": { + "propertyOrder": 5, + "nullable": true, + "type": "integer", + "default": 100, + "title": "Number of most frequent words", + "exclusiveMinimum": 0 + } + }, + "required": [ + "textColumn" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "WordCloud" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Word Cloud", + "operatorDescription": "Generate word cloud for texts", + "operatorGroupName": "Media", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "LineChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "yLabel": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "default": "Y Axis", + "description": "the label for y axis", + "title": "Y Label" + }, + "xLabel": { + "propertyOrder": 7, + "nullable": true, + "type": "string", + "default": "X Axis", + "description": "the label for x axis", + "title": "X Label" + }, + "lines": { + "propertyOrder": 8, + "type": "array", + "items": { + "$ref": "#/definitions/LineConfig" + }, + "title": "Lines" + } + }, + "required": [ + "lines" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "LineChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "LineConfig": { + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "yValue": { + "enum": [ + "integer", + "long", + "double" + ] + }, + "xValue": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "y": { + "propertyOrder": 1, + "type": "string", + "description": "value for y axis", + "title": "Y Value", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "x": { + "propertyOrder": 2, + "type": "string", + "description": "value for x axis", + "title": "X Value", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "mode": { + "propertyOrder": 3, + "type": "string", + "default": "line with dots", + "enum": [ + "line", + "dots", + "line with dots" + ], + "title": "Line Mode" + }, + "name": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "title": "Line Name" + }, + "color": { + "propertyOrder": 5, + "nullable": true, + "type": "string", + "description": "must be a valid CSS color or hex color string", + "title": "Line Color" + } + }, + "required": [ + "y", + "x", + "mode" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Line Chart", + "operatorDescription": "View the result in line chart", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "RandomKSampling", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "random k sample percentage": { + "propertyOrder": 6, + "type": "integer", + "description": "random k sampling with given percentage", + "title": "Random k sample percentage" + } + }, + "required": [ + "random k sample percentage" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "RandomKSampling" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Random K Sampling", + "operatorDescription": "random sampling with given percentage", + "operatorGroupName": "Utilities", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": true, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Split", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "k": { + "propertyOrder": 4, + "type": "integer", + "default": 80, + "description": "percentage of data going to the upper port", + "title": "Split Percentage" + }, + "random": { + "propertyOrder": 5, + "type": "boolean", + "default": true, + "description": "Shuffle the data based on a random seed", + "title": "Auto-Generate Seed" + }, + "seed": { + "propertyOrder": 6, + "type": "integer", + "default": 1, + "description": "An int for reproducible output across multiple run", + "title": "Seed", + "hideTarget": "random", + "hideType": "equals", + "hideExpectedValue": "true" + } + }, + "required": [ + "k", + "random", + "seed" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Split" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Split", + "operatorDescription": "Split data to two different ports", + "operatorGroupName": "Utilities", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": true, + "dynamicOutputPorts": true, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnMultiLayerPerceptron", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnMultiLayerPerceptron" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Multi-layer Perceptron", + "operatorDescription": "Sklearn Multi-layer Perceptron Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "BarChart", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "value": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "fields": { + "propertyOrder": 4, + "type": "string", + "description": "Visualize categorical data in a Bar Chart", + "title": "Fields", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "categoryColumn": { + "propertyOrder": 5, + "nullable": true, + "type": "string", + "default": "No Selection", + "description": "Optional - Select a column to Color Code the Categories", + "title": "Category Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "horizontalOrientation": { + "propertyOrder": 6, + "type": "boolean", + "default": false, + "description": "Orientation Style", + "title": "Horizontal Orientation" + }, + "pattern": { + "propertyOrder": 7, + "nullable": true, + "type": "string", + "description": "Add texture to the chart based on an attribute", + "title": "Pattern", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "value": { + "propertyOrder": 10, + "type": "string", + "description": "The value associated with each category", + "title": "Value Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "fields", + "horizontalOrientation", + "value" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "BarChart" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Bar Chart", + "operatorDescription": "Visualize data in a Bar Chart", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "HashJoin", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "buildAttributeName": { + "const": { + "$data": "probeAttributeName" + } + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "buildAttributeName": { + "propertyOrder": 4, + "type": "string", + "description": "attribute to be joined on the Left Input", + "title": "Left Input Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "probeAttributeName": { + "propertyOrder": 5, + "type": "string", + "description": "attribute to be joined on the Right Input", + "title": "Right Input Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 1 + }, + "joinType": { + "propertyOrder": 6, + "type": "string", + "default": "inner", + "enum": [ + "inner", + "left outer", + "right outer", + "full outer" + ], + "description": "select the join type to execute", + "title": "Join Type" + } + }, + "required": [ + "buildAttributeName", + "probeAttributeName", + "joinType" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "HashJoin" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Hash Join", + "operatorDescription": "join two inputs", + "operatorGroupName": "Join", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "left", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "right", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "PythonTableReducer", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "lambdaAttributeUnits": { + "propertyOrder": 4, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/LambdaAttributeUnit" + }, + "title": "Output columns" + } + }, + "required": [], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "PythonTableReducer" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "LambdaAttributeUnit": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributeName": { + "propertyOrder": 1, + "type": "string", + "title": "Attribute Name", + "autofill": "attributeName", + "hideTarget": "attributeName", + "hideType": "regex", + "hideExpectedValue": "Add New Column", + "additionalEnumValue": "Add New Column", + "autofillAttributeOnPort": 0 + }, + "newAttributeName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "New Attribute Name", + "hideTarget": "attributeName", + "hideType": "regex", + "hideExpectedValue": "(?!Add New Column).*", + "hideOnNull": true + }, + "attributeType": { + "propertyOrder": 3, + "type": "string", + "enum": [ + "string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + }, + "expression": { + "propertyOrder": 4, + "type": "string", + "title": "Expression" + } + }, + "required": [ + "attributeName", + "attributeType", + "expression" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Python Table Reducer", + "operatorDescription": "Reduce Table to Tuple", + "operatorGroupName": "Python", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Dendrogram", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "threshold": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "description": "Value at which separation of clusters will be made", + "title": "Color Threshold" + }, + "xVal": { + "propertyOrder": 7, + "type": "string", + "description": "The x values of points in dendrogram", + "title": "Value X Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "yVal": { + "propertyOrder": 8, + "type": "string", + "description": "The y value of points in dendrogram", + "title": "Value Y Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Labels": { + "propertyOrder": 9, + "type": "string", + "description": "The label of points in dendrogram", + "title": "Labels", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "xVal", + "yVal", + "Labels" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Dendrogram" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Dendrogram", + "operatorDescription": "Visualize data in a Dendrogram", + "operatorGroupName": "Scientific", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "KNNClassifierTrainer", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "paraList": { + "propertyOrder": 4, + "type": "array", + "items": { + "$ref": "#/definitions/HyperParameters(SklearnAdvancedKNNParameters)" + }, + "title": "Parameter Setting" + }, + "groundTruthAttribute": { + "propertyOrder": 5, + "type": "string", + "description": "Ground truth attribute column", + "title": "Ground Truth Attribute Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Selected Features": { + "propertyOrder": 8, + "type": "array", + "items": { + "type": "string" + }, + "description": "Features used to train the model", + "title": "Selected Features", + "autofill": "attributeNameList", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "paraList", + "groundTruthAttribute", + "Selected Features" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "KNNClassifierTrainer" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "HyperParameters(SklearnAdvancedKNNParameters)": { + "type": "object", + "additionalProperties": false, + "properties": { + "parameter": { + "propertyOrder": 1, + "type": "string", + "enum": [ + "n_neighbors", + "p", + "weights", + "algorithm", + "leaf_size", + "metric", + "metric_params" + ], + "description": "Choose the name of the parameter", + "title": "Parameter" + }, + "parametersSource": { + "propertyOrder": 2, + "type": "boolean", + "default": false, + "description": "Parameter from workflow", + "title": "Workflow" + }, + "attribute": { + "propertyOrder": 3, + "nullable": true, + "type": "string", + "title": "Attribute", + "autofill": "attributeName", + "hideTarget": "parametersSource", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 1 + }, + "value": { + "propertyOrder": 4, + "nullable": true, + "type": "string", + "title": "Value", + "hideTarget": "parametersSource", + "hideType": "equals", + "hideExpectedValue": "true", + "hideOnNull": true + } + }, + "required": [ + "parameter", + "parametersSource" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "KNN Classifier", + "operatorDescription": "Sklearn KNN Classifier Operator", + "operatorGroupName": "Advanced Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "parameter", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnMultinomialNaiveBayes", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnMultinomialNaiveBayes" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Multinomial Naive Bayes", + "operatorDescription": "Sklearn Multinomial Naive Bayes Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnDecisionTree", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnDecisionTree" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Decision Tree", + "operatorDescription": "Sklearn Decision Tree Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "PostgreSQLSource", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "host": { + "propertyOrder": 4, + "type": "string", + "title": "Host", + "enable-presets": true + }, + "port": { + "propertyOrder": 5, + "type": "string", + "default": "default", + "description": "A port number or 'default'", + "title": "Port", + "enable-presets": true + }, + "database": { + "propertyOrder": 6, + "type": "string", + "title": "Database", + "enable-presets": true + }, + "table": { + "propertyOrder": 7, + "type": "string", + "title": "Table Name", + "enable-presets": true + }, + "username": { + "propertyOrder": 8, + "type": "string", + "title": "Username", + "enable-presets": true + }, + "password": { + "propertyOrder": 9, + "type": "string", + "title": "Password", + "widget": { + "formlyConfig": { + "templateOptions": { + "type": "password" + } + } + } + }, + "limit": { + "propertyOrder": 10, + "nullable": true, + "type": "integer", + "description": "max output count", + "title": "Limit" + }, + "offset": { + "propertyOrder": 11, + "nullable": true, + "type": "integer", + "description": "starting point of output", + "title": "Offset" + }, + "keywordSearch": { + "propertyOrder": 12, + "nullable": true, + "type": "boolean", + "default": false, + "title": "Keyword Search?", + "toggleHidden": [ + "keywordSearchByColumn", + "keywords" + ] + }, + "keywordSearchByColumn": { + "propertyOrder": 13, + "nullable": true, + "type": "string", + "title": "Keyword Search Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "keywords": { + "propertyOrder": 14, + "nullable": true, + "type": "string", + "description": "E.g. 'sore & throat' for AND; 'sore', 'throat' for OR. See official postgres documents for details.", + "title": "Keywords to Search", + "widget": { + "formlyConfig": { + "type": "textarea", + "templateOptions": { + "autosize": true, + "autosizeMinRows": 3 + } + } + } + }, + "progressive": { + "propertyOrder": 15, + "nullable": true, + "type": "boolean", + "default": false, + "title": "Progressive?", + "toggleHidden": [ + "batchByColumn", + "min", + "max", + "interval" + ] + }, + "batchByColumn": { + "propertyOrder": 16, + "nullable": true, + "type": "string", + "title": "Batch by Column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "min": { + "propertyOrder": 17, + "nullable": true, + "type": "string", + "default": "auto", + "title": "Min", + "dependOn": "batchByColumn" + }, + "max": { + "propertyOrder": 18, + "nullable": true, + "type": "string", + "default": "auto", + "title": "Max", + "dependOn": "batchByColumn" + }, + "interval": { + "propertyOrder": 19, + "type": "integer", + "default": 1000000000, + "title": "Batch by Interval", + "dependOn": "batchByColumn" + } + }, + "required": [ + "host", + "port", + "database", + "table", + "username", + "password", + "interval" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "PostgreSQLSource" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "PostgreSQL Source", + "operatorDescription": "Read data from a PostgreSQL instance", + "operatorGroupName": "Database Connector", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "ArrowSource", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "fileName": { + "propertyOrder": 4, + "type": "string", + "title": "File" + }, + "limit": { + "propertyOrder": 5, + "nullable": true, + "type": "integer", + "description": "max output count", + "title": "Limit" + }, + "offset": { + "propertyOrder": 6, + "nullable": true, + "type": "integer", + "description": "starting point of output", + "title": "Offset" + } + }, + "required": [ + "fileName" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "ArrowSource" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Arrow File Scan", + "operatorDescription": "Scan data from a Arrow file", + "operatorGroupName": "Data Input", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SankeyDiagram", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "Source Attribute": { + "propertyOrder": 6, + "type": "string", + "description": "The source node of the Sankey diagram", + "title": "Source Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Target Attribute": { + "propertyOrder": 7, + "type": "string", + "description": "The target node of the Sankey diagram", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Value Attribute": { + "propertyOrder": 8, + "type": "string", + "description": "The value/volume of the flow between source and target", + "title": "Value Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "Source Attribute", + "Target Attribute", + "Value Attribute" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SankeyDiagram" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Sankey Diagram", + "operatorDescription": "Visualize data using a Sankey diagram", + "operatorGroupName": "Basic", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnSVM", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnSVM" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Support Vector Machine", + "operatorDescription": "Sklearn Support Vector Machine Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnBernoulliNaiveBayes", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnBernoulliNaiveBayes" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Bernoulli Naive Bayes", + "operatorDescription": "Sklearn Bernoulli Naive Bayes Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "ImageVisualizer", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "binaryContent": { + "propertyOrder": 4, + "type": "string", + "description": "The Binary data of the Image", + "title": "image content column", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "binaryContent" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "ImageVisualizer" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Image Visualizer", + "operatorDescription": "visualize image content", + "operatorGroupName": "Media", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnLinearSVM", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnLinearSVM" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Linear Support Vector Machine", + "operatorDescription": "Sklearn Linear Support Vector Machine Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "TextInput", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "textInput": { + "propertyOrder": 4, + "type": "string", + "title": "Text", + "widget": { + "formlyConfig": { + "type": "textarea", + "templateOptions": { + "autosize": true, + "autosizeMinRows": 3 + } + } + } + }, + "attributeType": { + "propertyOrder": 5, + "type": "string", + "default": "string", + "enum": [ + "string", + "single string", + "integer", + "long", + "double", + "boolean", + "timestamp", + "binary" + ], + "title": "Attribute Type" + }, + "attributeName": { + "propertyOrder": 6, + "type": "string", + "default": "line", + "title": "Attribute Name" + }, + "fileScanLimit": { + "propertyOrder": 7, + "nullable": true, + "type": "integer", + "title": "Limit", + "hideTarget": "attributeType", + "hideType": "regex", + "hideExpectedValue": "^binary$|^single string$" + }, + "fileScanOffset": { + "propertyOrder": 8, + "nullable": true, + "type": "integer", + "title": "Offset", + "hideTarget": "attributeType", + "hideType": "regex", + "hideExpectedValue": "^binary$|^single string$" + } + }, + "required": [ + "textInput", + "attributeType", + "attributeName" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "TextInput" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Text Input", + "operatorDescription": "Source data from manually inputted text", + "operatorGroupName": "Data Input", + "inputPorts": [], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "HuggingFaceSpamSMSDetection", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "attribute": { + "propertyOrder": 6, + "type": "string", + "description": "column to perform spam detection on", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "Spam result attribute": { + "propertyOrder": 7, + "type": "string", + "default": "is_spam", + "description": "column name of whether spam or not", + "title": "Spam result attribute" + }, + "Score result attribute": { + "propertyOrder": 8, + "type": "string", + "default": "score", + "description": "column name of Probability for classification", + "title": "Score result attribute" + } + }, + "required": [ + "attribute", + "Spam result attribute", + "Score result attribute" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "HuggingFaceSpamSMSDetection" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Hugging Face Spam Detection", + "operatorDescription": "Spam Detection by SMS Spam Detection Model from Hugging Face", + "operatorGroupName": "Hugging Face", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "QuiverPlot", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "value": { + "enum": [ + "integer", + "long", + "double" + ] + } + }, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "x": { + "propertyOrder": 6, + "type": "string", + "description": "Column for the x-coordinate of the starting point", + "title": "x", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "y": { + "propertyOrder": 7, + "type": "string", + "description": "Column for the y-coordinate of the starting point", + "title": "y", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "u": { + "propertyOrder": 8, + "type": "string", + "description": "Column for the vector component in the x-direction", + "title": "u", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "v": { + "propertyOrder": 9, + "type": "string", + "description": "Column for the vector component in the y-direction", + "title": "v", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "x", + "y", + "u", + "v" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "QuiverPlot" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Quiver Plot", + "operatorDescription": "Visualize vector data in a Quiver Plot", + "operatorGroupName": "Scientific", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 2, + "index": 2, + "name": "SINGLE_SNAPSHOT", + "singleSnapshot": true, + "setDelta": false, + "unrecognized": false, + "setSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "SklearnNearestCentroid", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "target": { + "propertyOrder": 4, + "type": "string", + "description": "Attribute in your dataset corresponding to target.", + "title": "Target Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "countVectorizer": { + "propertyOrder": 5, + "type": "boolean", + "default": false, + "description": "Convert a collection of text documents to a matrix of token counts.", + "title": "Count Vectorizer" + }, + "text": { + "propertyOrder": 6, + "nullable": true, + "type": "string", + "description": "Attribute in your dataset with text to vectorize.", + "title": "Text Attribute", + "autofill": "attributeName", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false", + "autofillAttributeOnPort": 0 + }, + "tfidfTransformer": { + "propertyOrder": 7, + "type": "boolean", + "default": false, + "description": "Transform a count matrix to a normalized tf or tf-idf representation.", + "title": "Tfidf Transformer", + "hideTarget": "countVectorizer", + "hideType": "equals", + "hideExpectedValue": "false" + } + }, + "required": [ + "target", + "countVectorizer", + "tfidfTransformer" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "SklearnNearestCentroid" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Nearest Centroid", + "operatorDescription": "Sklearn Nearest Centroid Operator", + "operatorGroupName": "Sklearn", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "training", + "allowMultiLinks": false, + "dependencies": [] + }, + { + "id": { + "id": 1, + "internal": false + }, + "displayName": "testing", + "allowMultiLinks": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": true, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + }, + { + "operatorType": "Aggregate", + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "dummyPropertyList": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/DummyProperties" + }, + "description": "Add dummy property if needed", + "title": "Dummy Property List" + }, + "aggregations": { + "propertyOrder": 6, + "type": "array", + "minItems": 1, + "maxItems": 2147483647, + "items": { + "$ref": "#/definitions/AggregationOperation" + }, + "description": "multiple aggregation functions", + "title": "Aggregations" + }, + "groupByKeys": { + "propertyOrder": 7, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "description": "group by columns", + "title": "Group By Keys", + "autofill": "attributeNameList", + "autofillAttributeOnPort": 0 + } + }, + "required": [ + "aggregations" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "operatorType", + "value": "Aggregate" + } + }, + "definitions": { + "DummyProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "dummyProperty": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Dummy Property" + }, + "dummyValue": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Dummy Value" + } + } + }, + "PortDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "portID": { + "propertyOrder": 1, + "nullable": true, + "type": "string", + "title": "Port ID" + }, + "displayName": { + "propertyOrder": 2, + "nullable": true, + "type": "string", + "title": "Display Name" + }, + "allowMultiInputs": { + "propertyOrder": 3, + "type": "boolean", + "title": "Allow Multi Inputs" + }, + "isDynamicPort": { + "propertyOrder": 4, + "type": "boolean", + "title": "Is Dynamic Port" + }, + "partitionRequirement": { + "propertyOrder": 5, + "nullable": true, + "oneOf": [ + { + "$ref": "#/definitions/HashPartition" + }, + { + "$ref": "#/definitions/RangePartition" + }, + { + "$ref": "#/definitions/SinglePartition" + }, + { + "$ref": "#/definitions/BroadcastPartition" + }, + { + "$ref": "#/definitions/UnknownPartition" + } + ], + "title": "Partition Requirement" + }, + "dependencies": { + "propertyOrder": 6, + "nullable": true, + "type": "array", + "items": { + "$ref": "#/definitions/Object" + }, + "title": "Dependencies" + } + }, + "required": [ + "allowMultiInputs", + "isDynamicPort" + ] + }, + "HashPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "hash" + ], + "default": "hash", + "options": { + "hidden": true + } + }, + "hashAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Hash Attribute Names" + } + }, + "title": "hash", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "hash" + } + } + }, + "RangePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "range" + ], + "default": "range", + "options": { + "hidden": true + } + }, + "rangeAttributeNames": { + "propertyOrder": 1, + "nullable": true, + "type": "array", + "items": { + "type": "string" + }, + "title": "Range Attribute Names" + }, + "rangeMin": { + "propertyOrder": 2, + "type": "integer", + "title": "Range Min" + }, + "rangeMax": { + "propertyOrder": 3, + "type": "integer", + "title": "Range Max" + } + }, + "title": "range", + "required": [ + "type", + "rangeMin", + "rangeMax" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "range" + } + } + }, + "SinglePartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ], + "default": "single", + "options": { + "hidden": true + } + } + }, + "title": "single", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "single" + } + } + }, + "BroadcastPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast" + ], + "default": "broadcast", + "options": { + "hidden": true + } + } + }, + "title": "broadcast", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "broadcast" + } + } + }, + "UnknownPartition": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "none" + ], + "default": "none", + "options": { + "hidden": true + } + } + }, + "title": "none", + "required": [ + "type" + ], + "options": { + "multiple_editor_select_via_property": { + "property": "type", + "value": "none" + } + } + }, + "Object": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "AggregationOperation": { + "type": "object", + "additionalProperties": false, + "attributeTypeRules": { + "attribute": { + "allOf": [ + { + "if": { + "aggFunction": { + "valEnum": [ + "sum", + "average", + "min", + "max" + ] + } + }, + "then": { + "enum": [ + "integer", + "long", + "double", + "timestamp" + ] + } + }, + { + "if": { + "aggFunction": { + "valEnum": [ + "concat" + ] + } + }, + "then": { + "enum": [ + "string" + ] + } + } + ] + } + }, + "properties": { + "aggFunction": { + "propertyOrder": 1, + "type": "string", + "enum": [ + "sum", + "count", + "average", + "min", + "max", + "concat" + ], + "description": "sum, count, average, min, max, or concat", + "title": "Aggregate Func" + }, + "attribute": { + "propertyOrder": 2, + "type": "string", + "description": "column to calculate average value", + "title": "Attribute", + "autofill": "attributeName", + "autofillAttributeOnPort": 0 + }, + "result attribute": { + "propertyOrder": 3, + "type": "string", + "minLength": 1, + "description": "column name of average result", + "title": "Result attribute" + } + }, + "required": [ + "aggFunction", + "attribute", + "result attribute" + ] + } + } + }, + "additionalMetadata": { + "userFriendlyName": "Aggregate", + "operatorDescription": "Calculate different types of aggregation values", + "operatorGroupName": "Aggregate", + "inputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "allowMultiLinks": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "id": { + "id": 0, + "internal": false + }, + "displayName": "", + "blocking": false, + "mode": { + "value": 0, + "index": 0, + "name": "SET_SNAPSHOT", + "setSnapshot": true, + "setDelta": false, + "unrecognized": false, + "singleSnapshot": false + } + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "supportReconfiguration": false, + "allowPortCustomization": false + }, + "operatorVersion": "N/A" + } + ], + "groups": [ + { + "groupName": "Data Input", + "children": null + }, + { + "groupName": "Database Connector", + "children": null + }, + { + "groupName": "Search", + "children": null + }, + { + "groupName": "Data Cleaning", + "children": [ + { + "groupName": "Join", + "children": null + }, + { + "groupName": "Aggregate", + "children": null + }, + { + "groupName": "Sort", + "children": null + } + ] + }, + { + "groupName": "Machine Learning", + "children": [ + { + "groupName": "Sklearn", + "children": null + }, + { + "groupName": "Advanced Sklearn", + "children": null + }, + { + "groupName": "Hugging Face", + "children": null + }, + { + "groupName": "Machine Learning General", + "children": null + } + ] + }, + { + "groupName": "Utilities", + "children": null + }, + { + "groupName": "External API", + "children": null + }, + { + "groupName": "User-defined Functions", + "children": [ + { + "groupName": "Python", + "children": null + }, + { + "groupName": "Java", + "children": null + }, + { + "groupName": "R", + "children": null + } + ] + }, + { + "groupName": "Visualization", + "children": [ + { + "groupName": "Basic", + "children": null + }, + { + "groupName": "Statistical", + "children": null + }, + { + "groupName": "Scientific", + "children": null + }, + { + "groupName": "Financial", + "children": null + }, + { + "groupName": "Media", + "children": null + }, + { + "groupName": "Advanced", + "children": null + } + ] + }, + { + "groupName": "Control Block", + "children": null + } + ] +} \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/files/unit.py b/core/suggestion-service/llm_agent/files/unit.py new file mode 100644 index 00000000000..f48a638dedd --- /dev/null +++ b/core/suggestion-service/llm_agent/files/unit.py @@ -0,0 +1,61 @@ +# TODO: translate: workflow-util.service.ts +# public getNewOperatorPredicate(operatorType: string): OperatorPredicate { +# const operatorSchema = this.operatorSchemaList.find(schema => schema.operatorType === operatorType); +# if (operatorSchema === undefined) { +# throw new Error(`operatorType ${operatorType} doesn't exist in operator metadata`); +# } +# +# const operatorId = operatorSchema.operatorType + "-" + this.getOperatorRandomUUID(); +# const operatorProperties = {}; +# +# // Remove the ID field for the schema to prevent warning messages from Ajv +# const { ...schemaWithoutId } = operatorSchema.jsonSchema; +# +# // value inserted in the data will be the deep clone of the default in the schema +# const validate = this.ajv.compile(schemaWithoutId); +# validate(operatorProperties); +# +# const inputPorts: PortDescription[] = []; +# const outputPorts: PortDescription[] = []; +# +# // by default, the operator will not show advanced option in the properties to the user +# const showAdvanced = false; +# +# // by default, the operator is not disabled +# const isDisabled = false; +# +# // by default, the operator name is the user friendly name +# const customDisplayName = operatorSchema.additionalMetadata.userFriendlyName; +# +# const dynamicInputPorts = operatorSchema.additionalMetadata.dynamicInputPorts ?? false; +# const dynamicOutputPorts = operatorSchema.additionalMetadata.dynamicOutputPorts ?? false; +# +# for (let i = 0; i < operatorSchema.additionalMetadata.inputPorts.length; i++) { +# const portID = "input-" + i.toString(); +# const portInfo = operatorSchema.additionalMetadata.inputPorts[i]; +# inputPorts.push(WorkflowUtilService.inputPortToPortDescription(portID, portInfo)); +# } +# +# for (let i = 0; i < operatorSchema.additionalMetadata.outputPorts.length; i++) { +# const portID = "output-" + i.toString(); +# const portInfo = operatorSchema.additionalMetadata.outputPorts[i]; +# outputPorts.push(WorkflowUtilService.outputPortToPortDescription(portID, portInfo)); +# } +# +# const operatorVersion = operatorSchema.operatorVersion; +# +# return { +# operatorID: operatorId, +# operatorType, +# operatorVersion, +# operatorProperties, +# inputPorts, +# outputPorts, +# showAdvanced, +# isDisabled, +# customDisplayName, +# dynamicInputPorts, +# dynamicOutputPorts, +# }; +# } +# into python \ No newline at end of file From 8c8e436199d6d8c1e37a55a25903b868936de9af Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 24 Apr 2025 10:01:49 -0700 Subject: [PATCH 021/104] add config and more knowledge base --- core/suggestion-service/.env.example | 16 - core/suggestion-service/.gitignore | 3 +- .../llm_agent/files/operator_format.json | 4153 +++++++++++++++++ ...etadata.json => operator_json_schema.json} | 0 .../files/operator_metadata_converter.py | 93 + .../llm_agent/files/texera_paper.pdf | Bin 0 -> 783734 bytes .../llm_agent/files/texera_python_udf.md | 159 + .../llm_agent/files/unit.py | 61 - .../llm_agent/instruction.md | 1 + .../llm_agent/openai_agent.py | 23 +- .../suggestion_engine/generator.py | 43 +- 11 files changed, 4442 insertions(+), 110 deletions(-) delete mode 100644 core/suggestion-service/.env.example create mode 100644 core/suggestion-service/llm_agent/files/operator_format.json rename core/suggestion-service/llm_agent/files/{operator_metadata.json => operator_json_schema.json} (100%) create mode 100644 core/suggestion-service/llm_agent/files/operator_metadata_converter.py create mode 100644 core/suggestion-service/llm_agent/files/texera_paper.pdf create mode 100644 core/suggestion-service/llm_agent/files/texera_python_udf.md delete mode 100644 core/suggestion-service/llm_agent/files/unit.py diff --git a/core/suggestion-service/.env.example b/core/suggestion-service/.env.example deleted file mode 100644 index bd42cd20cfb..00000000000 --- a/core/suggestion-service/.env.example +++ /dev/null @@ -1,16 +0,0 @@ -# LLM provider configuration -# Options: "openai" or "anthropic" -LLM_PROVIDER=openai - -# OpenAI configuration -OPENAI_API_KEY=your_openai_api_key_here -OPENAI_MODEL=gpt-4o -# Optional: Specify an existing Assistant ID to reuse -OPENAI_ASSISTANT_ID= - -# Anthropic configuration -ANTHROPIC_API_KEY=your_anthropic_api_key_here -ANTHROPIC_MODEL=claude-3-opus-20240229 - -# Suggestion configuration -MAX_SUGGESTIONS=3 \ No newline at end of file diff --git a/core/suggestion-service/.gitignore b/core/suggestion-service/.gitignore index 8db194340ae..eb6b8bc6268 100644 --- a/core/suggestion-service/.gitignore +++ b/core/suggestion-service/.gitignore @@ -80,4 +80,5 @@ instance/ *.mp4 *.mov *.wav -*.mp3 \ No newline at end of file +*.mp3 +.env \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/files/operator_format.json b/core/suggestion-service/llm_agent/files/operator_format.json new file mode 100644 index 00000000000..d967f89866c --- /dev/null +++ b/core/suggestion-service/llm_agent/files/operator_format.json @@ -0,0 +1,4153 @@ +[ + { + "operatorID": "IntervalJoin-operator-baf71e8d-a82e-43b8-90c5-afb4c095d82f", + "operatorType": "IntervalJoin", + "operatorVersion": "N/A", + "operatorProperties": { + "constant": 10, + "includeLeftBound": true, + "includeRightBound": true, + "timeIntervalType": "day", + "dummyPropertyList": [], + "leftAttributeName": null, + "rightAttributeName": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "left table", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "right table", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Interval Join" + }, + { + "operatorID": "DotPlot-operator-a33e49ae-c7cc-4cc1-8626-7365a341bd7c", + "operatorType": "DotPlot", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "Count Attribute": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Dot Plot" + }, + { + "operatorID": "CartesianProduct-operator-801f7047-716a-4bce-b0dd-9735cf0b0fe4", + "operatorType": "CartesianProduct", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "left", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "right", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Cartesian Product" + }, + { + "operatorID": "HuggingFaceSentimentAnalysis-operator-1268c763-ea72-400f-9953-ecf312fc7bb2", + "operatorType": "HuggingFaceSentimentAnalysis", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "attribute": null, + "Positive result attribute": "huggingface_sentiment_positive", + "Neutral result attribute": "huggingface_sentiment_neutral", + "Negative result attribute": "huggingface_sentiment_negative" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Hugging Face Sentiment Analysis" + }, + { + "operatorID": "PythonUDFSourceV2-operator-e4d8bef4-679f-4f6c-bcaa-76d9d8662d04", + "operatorType": "PythonUDFSourceV2", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "code": "# from pytexera import *\n# class GenerateOperator(UDFSourceOperator):\n# \n# @overrides\n# \n# def produce(self) -> Iterator[Union[TupleLike, TableLike, None]]:\n# yield\n", + "workers": 1, + "columns": [] + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "1-out Python UDF" + }, + { + "operatorID": "TwitterFullArchiveSearch-operator-414c0b55-8c97-4574-9b2a-819f411c3319", + "operatorType": "TwitterFullArchiveSearch", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "apiKey": null, + "apiSecretKey": null, + "stopWhenRateLimited": false, + "searchQuery": null, + "fromDateTime": "2021-04-01T00:00:00Z", + "toDateTime": "2021-05-01T00:00:00Z", + "limit": 100 + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Twitter Full Archive Search API" + }, + { + "operatorID": "SklearnLogisticRegressionCV-operator-171e545b-f5d5-4083-8ca9-7fc3681000fa", + "operatorType": "SklearnLogisticRegressionCV", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Logistic Regression Cross Validation" + }, + { + "operatorID": "JSONLFileScan-operator-14a8a99c-daaa-4cfe-a9fd-ea06a6e1c1e1", + "operatorType": "JSONLFileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "fileName": null, + "fileEncoding": "UTF_8", + "limit": null, + "offset": null, + "flatten": null + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "JSONL File Scan" + }, + { + "operatorID": "CandlestickChart-operator-e6bb74bc-dde8-4d9c-91cc-52a75d4f38f3", + "operatorType": "CandlestickChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "date": null, + "open": null, + "high": null, + "low": null, + "close": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Candlestick Chart" + }, + { + "operatorID": "ReservoirSampling-operator-59f869a4-081c-4452-b0d2-a021880a9862", + "operatorType": "ReservoirSampling", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "number of item sampled in reservoir sampling": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Reservoir Sampling" + }, + { + "operatorID": "ScatterMatrixChart-operator-62894ebb-3155-4edd-8904-21e9b171e43e", + "operatorType": "ScatterMatrixChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "Selected Attributes": [], + "Color": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Scatter Matrix Chart" + }, + { + "operatorID": "SklearnKNN-operator-3cc16e49-747d-42f3-a6e9-de5c8468604c", + "operatorType": "SklearnKNN", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "K-nearest Neighbors" + }, + { + "operatorID": "SklearnProbabilityCalibration-operator-bcc9a9d1-e724-4466-8147-37ec14fa749a", + "operatorType": "SklearnProbabilityCalibration", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Probability Calibration" + }, + { + "operatorID": "SortPartitions-operator-78161db0-acb9-45ef-b6d4-d5217ea011a5", + "operatorType": "SortPartitions", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "sortAttributeName": null, + "domainMin": null, + "domainMax": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sort Partitions" + }, + { + "operatorID": "DumbbellPlot-operator-ed3ea2ff-b87e-47aa-b9ca-c91955141ef3", + "operatorType": "DumbbellPlot", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "categoryColumnName": null, + "dumbbellStartValue": null, + "dumbbellEndValue": null, + "measurementColumnName": null, + "comparedColumnName": null, + "dots": [], + "showLegends": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Dumbbell Plot" + }, + { + "operatorID": "If-operator-c8c718da-1d0f-40b8-a5e9-0ca6c66eee3a", + "operatorType": "If", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "conditionName": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "Condition", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "False", + "allowMultiInputs": false, + "isDynamicPort": false + }, + { + "portID": "output-1", + "displayName": "True", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "If" + }, + { + "operatorID": "SklearnSDG-operator-8d799e8e-b14b-4e84-af56-9f296fa56ca4", + "operatorType": "SklearnSDG", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Stochastic Gradient Descent" + }, + { + "operatorID": "URLVisualizer-operator-db184eb3-db50-4664-b28b-f09eb60f26c9", + "operatorType": "URLVisualizer", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "urlContentAttrName": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "URL Visualizer" + }, + { + "operatorID": "Dummy-operator-fb4a58a0-d025-4a4b-bf20-e87ef725d037", + "operatorType": "Dummy", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "dummyOperator": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": true, + "dynamicOutputPorts": true, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Dummy" + }, + { + "operatorID": "HuggingFaceTextSummarization-operator-32470540-35ea-4661-80dc-13082338dc2d", + "operatorType": "HuggingFaceTextSummarization", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "attribute": null, + "Result attribute name": "summary" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Hugging Face Text Summarization" + }, + { + "operatorID": "Union-operator-605b583e-26c0-4dbd-b4fa-f2dad82687ef", + "operatorType": "Union", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Union" + }, + { + "operatorID": "SklearnGradientBoosting-operator-1d3c0c54-2bbf-43a7-8fa2-dc1eec51e8a5", + "operatorType": "SklearnGradientBoosting", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Gradient Boosting" + }, + { + "operatorID": "KNNRegressorTrainer-operator-e2e670b3-936b-4579-95dc-2c25a4b32b98", + "operatorType": "KNNRegressorTrainer", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "paraList": [], + "groundTruthAttribute": null, + "Selected Features": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "parameter", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "KNN Regressor" + }, + { + "operatorID": "RUDFSource-operator-5c4bd0c5-30ec-42a4-b532-ce59e6f1c033", + "operatorType": "RUDFSource", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "code": "# If using Table API:\n# function() { \n# return (data.frame(Column_Here = \"Value_Here\")) \n# }\n\n# If using Tuple API:\n# library(coro)\n# coro::generator(function() {\n# yield (list(text= \"hello world!\"))\n# })", + "workers": 1, + "useTupleAPI": false, + "columns": [] + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "1-out R UDF" + }, + { + "operatorID": "HuggingFaceIrisLogisticRegression-operator-7463a6e9-16c8-401f-9c81-f31f687ed299", + "operatorType": "HuggingFaceIrisLogisticRegression", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "petalLengthCmAttribute": null, + "petalWidthCmAttribute": null, + "prediction class name": "Species_prediction", + "prediction probability name": "Species_probability" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Hugging Face Iris Logistic Regression" + }, + { + "operatorID": "ContinuousErrorBands-operator-d6903dab-ea84-4ea1-b4be-231b4d21a1e2", + "operatorType": "ContinuousErrorBands", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "xLabel": "X Axis", + "yLabel": "Y Axis", + "bands": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Continuous Error Bands" + }, + { + "operatorID": "TwitterSearch-operator-58b8073f-a166-42ab-96f4-25458a07a3bb", + "operatorType": "TwitterSearch", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "apiKey": null, + "apiSecretKey": null, + "stopWhenRateLimited": false, + "searchQuery": null, + "limit": 100 + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Twitter Search API" + }, + { + "operatorID": "SklearnPassiveAggressive-operator-dc3c49c1-ca29-44e5-bf6a-48ab9c698b09", + "operatorType": "SklearnPassiveAggressive", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Passive Aggressive" + }, + { + "operatorID": "HTMLVisualizer-operator-6c309e7b-1edb-4903-a1ab-61da14c18ae8", + "operatorType": "HTMLVisualizer", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "htmlContentAttrName": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "HTML Visualizer" + }, + { + "operatorID": "SklearnComplementNaiveBayes-operator-df29f54f-08f2-4e5f-8428-17e45f5d68b4", + "operatorType": "SklearnComplementNaiveBayes", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Complement Naive Bayes" + }, + { + "operatorID": "URLFetcher-operator-a8e900e2-a340-4300-bc96-249fbb7177cb", + "operatorType": "URLFetcher", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "url": null, + "decodingMethod": null + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "URL fetcher" + }, + { + "operatorID": "JavaUDF-operator-7439ea85-e4e7-4474-82b7-f9278e88d7a1", + "operatorType": "JavaUDF", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "code": "import edu.uci.ics.texera.workflow.common.operators.map.MapOpExec;\nimport edu.uci.ics.amber.engine.common.model.tuple.Tuple;\nimport edu.uci.ics.amber.engine.common.model.tuple.TupleLike;\nimport scala.Function1;\nimport java.io.Serializable;\n\npublic class JavaUDFOpExec extends MapOpExec {\n public JavaUDFOpExec () {\n this.setMapFunc((Function1 & Serializable) this::processTuple);\n }\n \n public TupleLike processTuple(Tuple tuple) {\n return tuple;\n }\n}", + "workers": 1, + "retainInputColumns": true, + "outputColumns": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": true, + "dynamicOutputPorts": true, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Java UDF" + }, + { + "operatorID": "PieChart-operator-58dbbfea-2a43-4741-bce3-bacedbb01301", + "operatorType": "PieChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "value": null, + "name": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Pie Chart" + }, + { + "operatorID": "DictionaryMatcher-operator-89073760-d5c5-4c4d-aae6-bc57f7e11d2c", + "operatorType": "DictionaryMatcher", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "Dictionary": null, + "Attribute": null, + "result attribute": "matched", + "Matching type": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Dictionary matcher" + }, + { + "operatorID": "UnnestString-operator-c6673579-e2ab-4218-9e8e-763a9a115cf3", + "operatorType": "UnnestString", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "Delimiter": ",", + "Attribute": null, + "Result attribute": "unnestResult" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Unnest String" + }, + { + "operatorID": "BubbleChart-operator-4350842a-0ce2-4a49-ab41-4a6219083606", + "operatorType": "BubbleChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "xValue": null, + "yValue": null, + "zValue": null, + "enableColor": false, + "colorCategory": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Bubble Chart" + }, + { + "operatorID": "RedditSearch-operator-ae85c2eb-d897-481f-a8b9-4193bd256c05", + "operatorType": "RedditSearch", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "clientId": null, + "clientSecret": null, + "query": null, + "limit": 100, + "sorting": "none" + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Reddit Search" + }, + { + "operatorID": "SVRTrainer-operator-ff19d251-82ab-4a3d-9169-f70e97b1d40d", + "operatorType": "SVRTrainer", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "paraList": [], + "groundTruthAttribute": null, + "Selected Features": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "parameter", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "SVM Regressor" + }, + { + "operatorID": "RUDF-operator-a2b1f867-ab5e-433f-98db-d1869a4d541a", + "operatorType": "RUDF", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "code": "# If using Table API:\n# function(table, port) { \n# return (table) \n# }\n\n# If using Tuple API:\n# library(coro)\n# coro::generator(function(tuple, port) {\n# yield (tuple)\n# })", + "workers": 1, + "useTupleAPI": false, + "retainInputColumns": true, + "outputColumns": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "R UDF" + }, + { + "operatorID": "BoxViolinPlot-operator-6a03ad17-a0f9-498c-a480-a45d1aefbc13", + "operatorType": "BoxViolinPlot", + "operatorVersion": "N/A", + "operatorProperties": { + "value": null, + "Quartile Method": "linear", + "horizontalOrientation": false, + "violinPlot": false, + "dummyPropertyList": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Box/Violin Plot" + }, + { + "operatorID": "SklearnAdaptiveBoosting-operator-52a6f13a-f641-4008-b874-f458cc39f769", + "operatorType": "SklearnAdaptiveBoosting", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Adaptive Boosting" + }, + { + "operatorID": "Scatterplot-operator-dd032ef6-ae57-4844-8182-d81a1205d199", + "operatorType": "Scatterplot", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "xColumn": null, + "yColumn": null, + "colorColumn": null, + "xLogScale": false, + "yLogScale": false, + "hoverName": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Scatter Plot" + }, + { + "operatorID": "SklearnPerceptron-operator-2610da3c-934a-4947-8501-19c42a5a9693", + "operatorType": "SklearnPerceptron", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Linear Perceptron" + }, + { + "operatorID": "KeywordSearch-operator-540684d4-5a85-4f60-940d-765815beedae", + "operatorType": "KeywordSearch", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "attribute": null, + "keyword": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Keyword Search" + }, + { + "operatorID": "PythonUDFV2-operator-b28d6a92-3d2e-49a0-b88d-afc9bb085b9e", + "operatorType": "PythonUDFV2", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "code": "# Choose from the following templates:\n# \n# from pytexera import *\n# \n# class ProcessTupleOperator(UDFOperatorV2):\n# \n# @overrides\n# def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:\n# yield tuple_\n# \n# class ProcessBatchOperator(UDFBatchOperator):\n# BATCH_SIZE = 10 # must be a positive integer\n# \n# @overrides\n# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]:\n# yield batch\n# \n# class ProcessTableOperator(UDFTableOperator):\n# \n# @overrides\n# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:\n# yield table\n", + "workers": 1, + "retainInputColumns": true, + "outputColumns": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": true, + "dynamicOutputPorts": true, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Python UDF" + }, + { + "operatorID": "SklearnLogisticRegression-operator-9341a1a8-8c89-46d7-a5ae-9717fed95011", + "operatorType": "SklearnLogisticRegression", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Logistic Regression" + }, + { + "operatorID": "SklearnRandomForest-operator-8b7637cc-eb2f-4e69-9d22-d20fa786a314", + "operatorType": "SklearnRandomForest", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Random Forest" + }, + { + "operatorID": "TypeCasting-operator-9d1f0c09-671e-463b-b5f1-73e647b36a6a", + "operatorType": "TypeCasting", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "typeCastingUnits": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Type Casting" + }, + { + "operatorID": "SklearnGaussianNaiveBayes-operator-50fbd19b-c5cb-4fd5-a222-c0b9e5cd7858", + "operatorType": "SklearnGaussianNaiveBayes", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Gaussian Naive Bayes" + }, + { + "operatorID": "AsterixDBSource-operator-536bed44-bf80-4999-9ab6-f612c9b62b39", + "operatorType": "AsterixDBSource", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "host": null, + "port": "default", + "database": null, + "table": null, + "limit": null, + "offset": null, + "keywordSearch": false, + "keywordSearchByColumn": null, + "keywords": null, + "progressive": false, + "batchByColumn": null, + "min": "auto", + "max": "auto", + "interval": 1000000000, + "geoSearch": false, + "geoSearchByColumns": [], + "geoSearchBoundingBox": [], + "regexSearch": false, + "regexSearchByColumn": null, + "regex": null, + "filterCondition": false, + "predicates": [] + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "AsterixDB Source" + }, + { + "operatorID": "DualInputPortsPythonUDFV2-operator-7ff2fc96-b18d-4374-b48d-4e711420e324", + "operatorType": "DualInputPortsPythonUDFV2", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "code": "# Choose from the following templates:\n# \n# from pytexera import *\n# \n# class ProcessTupleOperator(UDFOperatorV2):\n# \n# @overrides\n# def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]:\n# yield tuple_\n# \n# class ProcessBatchOperator(UDFBatchOperator):\n# BATCH_SIZE = 10 # must be a positive integer\n# \n# @overrides\n# def process_batch(self, batch: Batch, port: int) -> Iterator[Optional[BatchLike]]:\n# yield batch\n# \n# class ProcessTableOperator(UDFTableOperator):\n# \n# @overrides\n# def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]:\n# yield table\n", + "workers": 1, + "retainInputColumns": true, + "outputColumns": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "model", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "tuples", + "allowMultiInputs": true, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "2-in Python UDF" + }, + { + "operatorID": "Histogram-operator-0f78373c-3ddb-4ba1-afc1-613d5499f327", + "operatorType": "Histogram", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "color": null, + "separateBy": null, + "marginal": null, + "pattern": null, + "value": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Histogram" + }, + { + "operatorID": "SklearnDummyClassifier-operator-3efbc39d-c235-4337-bf5e-1779c71c2057", + "operatorType": "SklearnDummyClassifier", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Dummy Classifier" + }, + { + "operatorID": "Distinct-operator-2e6e9a64-fd86-48dd-b0c0-579848ced20d", + "operatorType": "Distinct", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Distinct" + }, + { + "operatorID": "NetworkGraph-operator-588e4434-cf2b-4f4d-9e5a-f528433eba9c", + "operatorType": "NetworkGraph", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "source": null, + "destination": null, + "title": "Network Graph" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Network Graph" + }, + { + "operatorID": "WaterfallChart-operator-c84015aa-da43-4c10-b70e-95d4310f50cf", + "operatorType": "WaterfallChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "xColumn": null, + "yColumn": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Waterfall Chart" + }, + { + "operatorID": "Limit-operator-7bbc752f-7c63-4f3e-8cfa-ea5c2b17dfac", + "operatorType": "Limit", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "limit": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Limit" + }, + { + "operatorID": "Scorer-operator-ce8cafac-a838-4f40-a5b5-95cb7542b8d0", + "operatorType": "Scorer", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "isRegression": false, + "actualValueColumn": null, + "predictValueColumn": null, + "classificationFlag": [], + "regressionFlag": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Machine Learning Scorer" + }, + { + "operatorID": "SklearnExtraTrees-operator-07320457-f191-4d59-b3c2-f53f098fe762", + "operatorType": "SklearnExtraTrees", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Extra Trees" + }, + { + "operatorID": "FileScan-operator-ac2fa70c-b7e2-41b9-a660-4fcd2985cf12", + "operatorType": "FileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "fileName": null, + "encoding": "UTF_8", + "extract": false, + "outputFileName": false, + "attributeType": "string", + "attributeName": "line", + "fileScanLimit": null, + "fileScanOffset": null + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": " File Scan" + }, + { + "operatorID": "GanttChart-operator-2758330f-db0d-494e-9d32-8e1f2ea9d814", + "operatorType": "GanttChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "pattern": null, + "start": null, + "finish": null, + "task": null, + "color": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Gantt Chart" + }, + { + "operatorID": "TernaryPlot-operator-3a52153d-6a0d-4036-90da-6a1da416bbec", + "operatorType": "TernaryPlot", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "firstVariable": null, + "secondVariable": null, + "thirdVariable": null, + "colorEnabled": false, + "colorDataField": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Ternary Plot" + }, + { + "operatorID": "SVCTrainer-operator-af398718-6aad-4984-a763-13a7e12bc243", + "operatorType": "SVCTrainer", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "paraList": [], + "groundTruthAttribute": null, + "Selected Features": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "parameter", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "SVM Classifier" + }, + { + "operatorID": "SklearnLinearRegression-operator-f1db6599-93a3-40f9-8b24-9b08b36e7465", + "operatorType": "SklearnLinearRegression", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "degree": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Linear Regression" + }, + { + "operatorID": "MySQLSource-operator-5eed434e-a69b-428c-a918-f0a6c1623081", + "operatorType": "MySQLSource", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "host": null, + "port": "default", + "database": null, + "table": null, + "username": null, + "password": null, + "limit": null, + "offset": null, + "keywordSearch": false, + "keywordSearchByColumn": null, + "keywords": null, + "progressive": false, + "batchByColumn": null, + "min": "auto", + "max": "auto", + "interval": 1000000000 + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "MySQL Source" + }, + { + "operatorID": "CSVOldFileScan-operator-30b07e25-e184-4b71-850e-87a8d5309e13", + "operatorType": "CSVOldFileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "fileName": null, + "fileEncoding": "UTF_8", + "limit": null, + "offset": null, + "customDelimiter": ",", + "hasHeader": true + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "CSVOld File Scan" + }, + { + "operatorID": "CSVFileScan-operator-cce61f95-2cc1-4d35-9fc7-6f70c179417b", + "operatorType": "CSVFileScan", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "fileName": null, + "fileEncoding": "UTF_8", + "limit": null, + "offset": null, + "customDelimiter": ",", + "hasHeader": true + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "CSV File Scan" + }, + { + "operatorID": "FunnelPlot-operator-879b2180-b41e-4a01-be0a-ba137f420ece", + "operatorType": "FunnelPlot", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "x": null, + "y": null, + "color": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Funnel Plot" + }, + { + "operatorID": "Projection-operator-8a897d90-d4f1-4006-9e56-9cf5e0ade491", + "operatorType": "Projection", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "isDrop": false, + "attributes": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Projection" + }, + { + "operatorID": "Filter-operator-cb71e488-71ed-46b9-9b4d-d6b8d3d3ca52", + "operatorType": "Filter", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "predicates": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Filter" + }, + { + "operatorID": "SklearnRidge-operator-3881091b-0054-49c0-bdab-fe329890ecd2", + "operatorType": "SklearnRidge", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Ridge Regression" + }, + { + "operatorID": "Intersect-operator-ef60ba5e-20fd-4f00-b140-8a9c949be34c", + "operatorType": "Intersect", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Intersect" + }, + { + "operatorID": "SklearnPrediction-operator-93a54a36-a977-4d67-9721-5c720d7de041", + "operatorType": "SklearnPrediction", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "Model Attribute": "model", + "Output Attribute Name": "prediction", + "Ground Truth Attribute Name to Ignore": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "model", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sklearn Prediction" + }, + { + "operatorID": "SymmetricDifference-operator-272908a1-fcec-463f-be82-79fac9765c22", + "operatorType": "SymmetricDifference", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "SymmetricDifference" + }, + { + "operatorID": "FigureFactoryTable-operator-f14ad41d-f824-4b40-b79f-df5ba4e726c8", + "operatorType": "FigureFactoryTable", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "fontSize": null, + "fontColor": null, + "rowHeight": null, + "add attribute": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Figure Factory Table" + }, + { + "operatorID": "FilledAreaPlot-operator-d05bff73-e5b6-4a94-91b0-a024cbc9c6d9", + "operatorType": "FilledAreaPlot", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "x": null, + "y": null, + "lineGroup": null, + "color": null, + "facetColumn": null, + "pattern": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Filled Area Plot" + }, + { + "operatorID": "SklearnRidgeCV-operator-3f4ab9aa-5e4e-4eed-9b69-a6fe133ab6a7", + "operatorType": "SklearnRidgeCV", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Ridge Regression Cross Validation" + }, + { + "operatorID": "IcicleChart-operator-2ed6bb54-b54a-480d-991f-8676e0189105", + "operatorType": "IcicleChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "hierarchy": [], + "value": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Icicle Chart" + }, + { + "operatorID": "Regex-operator-4507bc87-f91a-49cc-aee2-6ca6f38326c5", + "operatorType": "Regex", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "caseInsensitive": false, + "attribute": null, + "regex": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Regular Expression" + }, + { + "operatorID": "HeatMap-operator-82711949-c839-41ff-8d7b-3c8ac268084b", + "operatorType": "HeatMap", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "x": null, + "y": null, + "Values": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Heatmap" + }, + { + "operatorID": "TablesPlot-operator-d1293a95-cd07-4cc0-af2e-5f6dd5ebbfd3", + "operatorType": "TablesPlot", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "add attribute": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Tables Plot" + }, + { + "operatorID": "HierarchyChart-operator-f267a2d8-950d-4f04-a881-7c1d8d2121be", + "operatorType": "HierarchyChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "hierarchyChartType": null, + "hierarchy": [], + "value": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Hierarchy Chart" + }, + { + "operatorID": "SklearnExtraTree-operator-2da3e033-9934-459a-ab40-32ca72521cc0", + "operatorType": "SklearnExtraTree", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Extra Tree" + }, + { + "operatorID": "Sort-operator-f74fad5b-eb76-44b1-b3ce-a07a5db07a82", + "operatorType": "Sort", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "attributes": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sort" + }, + { + "operatorID": "Scatter3DChart-operator-e537f538-9972-416f-9dc9-c563add70457", + "operatorType": "Scatter3DChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "x": null, + "y": null, + "z": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Scatter3D Chart" + }, + { + "operatorID": "SklearnBagging-operator-2a4dd61b-e5c5-4df6-aadf-11deb552a00e", + "operatorType": "SklearnBagging", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Bagging" + }, + { + "operatorID": "Difference-operator-39fc6c14-238d-4404-9771-303632a23332", + "operatorType": "Difference", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "left", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "right", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Difference" + }, + { + "operatorID": "ContourPlot-operator-e7fa4a0f-954b-4dd9-8c0e-cc18fbe411d8", + "operatorType": "ContourPlot", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "gridSize": "10", + "connectGaps": true, + "x": null, + "y": null, + "z": null, + "Coloring Method": "heatmap" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Contour Plot" + }, + { + "operatorID": "PythonLambdaFunction-operator-e340dbfc-7576-4ea0-8b4a-c957c9de63ff", + "operatorType": "PythonLambdaFunction", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "lambdaAttributeUnits": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Python Lambda Function" + }, + { + "operatorID": "WordCloud-operator-751e787f-5786-4ded-8481-503704313e9e", + "operatorType": "WordCloud", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "textColumn": null, + "topN": 100 + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Word Cloud" + }, + { + "operatorID": "LineChart-operator-5726b996-a0c6-488b-b5bf-02d64c819f02", + "operatorType": "LineChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "yLabel": "Y Axis", + "xLabel": "X Axis", + "lines": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Line Chart" + }, + { + "operatorID": "RandomKSampling-operator-1ef7d528-309f-43e7-ba17-3e366570153e", + "operatorType": "RandomKSampling", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "random k sample percentage": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Random K Sampling" + }, + { + "operatorID": "Split-operator-a6496457-ed63-4c8b-b34d-fa1ba44401e4", + "operatorType": "Split", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "k": 80, + "random": true, + "seed": 1 + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + }, + { + "portID": "output-1", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": true, + "dynamicOutputPorts": true, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Split" + }, + { + "operatorID": "SklearnMultiLayerPerceptron-operator-c779f136-bff7-4a62-aedb-98f2bf283bb7", + "operatorType": "SklearnMultiLayerPerceptron", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Multi-layer Perceptron" + }, + { + "operatorID": "BarChart-operator-9f5bda27-60dd-4f7a-90aa-17188d8ba163", + "operatorType": "BarChart", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "fields": null, + "categoryColumn": "No Selection", + "horizontalOrientation": false, + "pattern": null, + "value": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Bar Chart" + }, + { + "operatorID": "HashJoin-operator-bbcf453a-141e-4585-a026-c9d6cf240857", + "operatorType": "HashJoin", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "buildAttributeName": null, + "probeAttributeName": null, + "joinType": "inner" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "left", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "right", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Hash Join" + }, + { + "operatorID": "PythonTableReducer-operator-7c840868-56dc-4476-a942-d08263e3e6ff", + "operatorType": "PythonTableReducer", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "lambdaAttributeUnits": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Python Table Reducer" + }, + { + "operatorID": "Dendrogram-operator-5dfb79eb-e65d-4c18-8da5-c3adf830a471", + "operatorType": "Dendrogram", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "threshold": null, + "xVal": null, + "yVal": null, + "Labels": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Dendrogram" + }, + { + "operatorID": "KNNClassifierTrainer-operator-ecbeb85e-8929-4d6f-a410-10a7a3aa3eef", + "operatorType": "KNNClassifierTrainer", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "paraList": [], + "groundTruthAttribute": null, + "Selected Features": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "parameter", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "KNN Classifier" + }, + { + "operatorID": "SklearnMultinomialNaiveBayes-operator-901327d8-bc4a-43cc-8027-42e21aaab122", + "operatorType": "SklearnMultinomialNaiveBayes", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Multinomial Naive Bayes" + }, + { + "operatorID": "SklearnDecisionTree-operator-612d82a8-c23c-49bf-98e3-d0efa745670e", + "operatorType": "SklearnDecisionTree", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Decision Tree" + }, + { + "operatorID": "PostgreSQLSource-operator-b188a2f8-17cb-47b9-9892-6ff39a733797", + "operatorType": "PostgreSQLSource", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "host": null, + "port": "default", + "database": null, + "table": null, + "username": null, + "password": null, + "limit": null, + "offset": null, + "keywordSearch": false, + "keywordSearchByColumn": null, + "keywords": null, + "progressive": false, + "batchByColumn": null, + "min": "auto", + "max": "auto", + "interval": 1000000000 + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "PostgreSQL Source" + }, + { + "operatorID": "ArrowSource-operator-5bb90faf-e6d6-4bfe-8332-fc26762de1b7", + "operatorType": "ArrowSource", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "fileName": null, + "limit": null, + "offset": null + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Arrow File Scan" + }, + { + "operatorID": "SankeyDiagram-operator-8ad13f24-e3d9-49f7-83bb-842ac6e263dc", + "operatorType": "SankeyDiagram", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "Source Attribute": null, + "Target Attribute": null, + "Value Attribute": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Sankey Diagram" + }, + { + "operatorID": "SklearnSVM-operator-1b44e1b3-0886-4e78-9049-3613c303f2cf", + "operatorType": "SklearnSVM", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Support Vector Machine" + }, + { + "operatorID": "SklearnBernoulliNaiveBayes-operator-a338d850-450d-4dd4-840a-f6c5a97fd686", + "operatorType": "SklearnBernoulliNaiveBayes", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Bernoulli Naive Bayes" + }, + { + "operatorID": "ImageVisualizer-operator-4d15ad37-d6f1-4964-b8d6-57ba66a0f37b", + "operatorType": "ImageVisualizer", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "binaryContent": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Image Visualizer" + }, + { + "operatorID": "SklearnLinearSVM-operator-136ef25e-4406-47c4-b8d6-3ceebfc66863", + "operatorType": "SklearnLinearSVM", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Linear Support Vector Machine" + }, + { + "operatorID": "TextInput-operator-52f7f823-3364-4123-aab9-ba79e1357586", + "operatorType": "TextInput", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "textInput": null, + "attributeType": "string", + "attributeName": "line", + "fileScanLimit": null, + "fileScanOffset": null + }, + "inputPorts": [], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Text Input" + }, + { + "operatorID": "HuggingFaceSpamSMSDetection-operator-3893bb5d-8f38-406b-b6e6-6123d4c2fc18", + "operatorType": "HuggingFaceSpamSMSDetection", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "attribute": null, + "Spam result attribute": "is_spam", + "Score result attribute": "score" + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Hugging Face Spam Detection" + }, + { + "operatorID": "QuiverPlot-operator-f93cc00f-1bd0-4563-920c-6db247e2ab2f", + "operatorType": "QuiverPlot", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "x": null, + "y": null, + "u": null, + "v": null + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Quiver Plot" + }, + { + "operatorID": "SklearnNearestCentroid-operator-53850cfd-3d7f-46dc-943f-630d885b6594", + "operatorType": "SklearnNearestCentroid", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "target": null, + "countVectorizer": false, + "text": null, + "tfidfTransformer": false + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "training", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + }, + { + "portID": "input-1", + "displayName": "testing", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [ + { + "id": 0, + "internal": false + } + ] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Nearest Centroid" + }, + { + "operatorID": "Aggregate-operator-694a5f09-9b56-45c1-a9a2-2d35d35909a0", + "operatorType": "Aggregate", + "operatorVersion": "N/A", + "operatorProperties": { + "dummyPropertyList": [], + "aggregations": [], + "groupByKeys": [] + }, + "inputPorts": [ + { + "portID": "input-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false, + "dependencies": [] + } + ], + "outputPorts": [ + { + "portID": "output-0", + "displayName": "", + "allowMultiInputs": false, + "isDynamicPort": false + } + ], + "dynamicInputPorts": false, + "dynamicOutputPorts": false, + "showAdvanced": false, + "isDisabled": false, + "customDisplayName": "Aggregate" + } +] \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/files/operator_metadata.json b/core/suggestion-service/llm_agent/files/operator_json_schema.json similarity index 100% rename from core/suggestion-service/llm_agent/files/operator_metadata.json rename to core/suggestion-service/llm_agent/files/operator_json_schema.json diff --git a/core/suggestion-service/llm_agent/files/operator_metadata_converter.py b/core/suggestion-service/llm_agent/files/operator_metadata_converter.py new file mode 100644 index 00000000000..eaf3f8b83f5 --- /dev/null +++ b/core/suggestion-service/llm_agent/files/operator_metadata_converter.py @@ -0,0 +1,93 @@ +import json +import uuid +from typing import Dict, Any, List +import copy + +# Load the operator metadata file +with open("operator_json_schema.json", "r") as f: + operator_metadata = json.load(f) + + +def resolve_ref(ref: str, definitions: Dict[str, Any]) -> Dict[str, Any]: + try: + ref_path = ref.lstrip("#/").split("/") + resolved = definitions + for part in ref_path: + resolved = resolved.get(part, {}) + return resolved + except Exception: + return {} + + +def fill_defaults(schema: Dict[str, Any], definitions: Dict[str, Any]) -> Dict[str, Any]: + """ + Fills default values into a schema-defined object. + """ + if "$ref" in schema: + schema = resolve_ref(schema["$ref"], definitions) + + if schema.get("type") == "object": + obj = {} + for prop, subschema in schema.get("properties", {}).items(): + obj[prop] = fill_defaults(subschema, definitions) + return obj + elif schema.get("type") == "array": + return [] + elif "default" in schema: + return copy.deepcopy(schema["default"]) + return None + + +def convert_to_operator_predicate(schema: Dict[str, Any]) -> Dict[str, Any]: + operator_type = schema["operatorType"] + metadata = schema["additionalMetadata"] + json_schema = schema["jsonSchema"] + definitions = json_schema.get("definitions", {}) + + operator_id = f"{operator_type}-operator-{uuid.uuid4()}" + operator_properties = fill_defaults(json_schema, definitions) + + input_ports = [] + for i, port_info in enumerate(metadata.get("inputPorts", [])): + input_ports.append({ + "portID": f"input-{i}", + "displayName": port_info.get("displayName", ""), + "allowMultiInputs": port_info.get("allowMultiLinks", False), + "isDynamicPort": False, + "dependencies": port_info.get("dependencies", []) + }) + + output_ports = [] + for i, port_info in enumerate(metadata.get("outputPorts", [])): + output_ports.append({ + "portID": f"output-{i}", + "displayName": port_info.get("displayName", ""), + "allowMultiInputs": False, + "isDynamicPort": False + }) + + return { + "operatorID": operator_id, + "operatorType": operator_type, + "operatorVersion": schema.get("operatorVersion", "N/A"), + "operatorProperties": operator_properties, + "inputPorts": input_ports, + "outputPorts": output_ports, + "dynamicInputPorts": metadata.get("dynamicInputPorts", False), + "dynamicOutputPorts": metadata.get("dynamicOutputPorts", False), + "showAdvanced": False, + "isDisabled": False, + "customDisplayName": metadata.get("userFriendlyName", operator_type) + } + + +# Convert all schemas +operator_predicates: List[Dict[str, Any]] = [ + convert_to_operator_predicate(schema) + for schema in operator_metadata["operators"] +] + +# Save result to file +output_path = "operator_format.json" +with open(output_path, "w") as f: + json.dump(operator_predicates, f, indent=2) \ No newline at end of file diff --git a/core/suggestion-service/llm_agent/files/texera_paper.pdf b/core/suggestion-service/llm_agent/files/texera_paper.pdf new file mode 100644 index 0000000000000000000000000000000000000000..49a64b0d00b0c92c5cc35eea168bc25903e51fa3 GIT binary patch literal 783734 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

    `3%xj$!I+!G%WcRD%K+nx(2s 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

    diff --git a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.scss b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.scss index fb57ae1e734..6d2177243c5 100644 --- a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.scss +++ b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.scss @@ -46,22 +46,30 @@ } .suggestions-list { - background-color: #fffbe6; - border: 1px solid #ffe58f; - padding: 10px; + // Remove background and border from the list container itself + // background-color: #fffbe6; + // border: 1px solid #ffe58f; + padding: 0; // Remove padding if items will have their own border-radius: 4px; list-style-type: none; margin: 0; .suggestion-item { - padding: 6px 10px; - margin-bottom: 4px; + padding: 8px 12px; // Adjusted padding for better spacing within item + margin-bottom: 6px; // Space between items color: #333; border-radius: 3px; cursor: pointer; transition: background-color 0.2s ease-in-out, - color 0.2s ease-in-out; + color 0.2s ease-in-out, + border-color 0.2s ease-in-out; // Added border-color transition + display: flex; + flex-direction: column; + + // Default state - yellow background for each item + background-color: #fffbe6; + border: 1px solid #ffe58f; &:last-child { margin-bottom: 0; @@ -69,14 +77,79 @@ &:hover { background-color: #ffe58f; + border-color: #ffd700; // Darker border on hover color: #000; } .suggestion-type { + // This style was outside .suggestion-item before, moved in font-size: 0.85em; color: #777; margin-left: 4px; } + + .suggestion-summary { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + + .expand-icon { + margin-left: 8px; + font-size: 0.8em; + color: #555; + } + + // Styles when expanded + &.expanded { + background-color: #fff9e0; // Slightly lighter or different yellow for expanded + border-color: #ffecb3; // Lighter border for expanded + + &:hover { + background-color: #fff0b3; // Hover for expanded item + border-color: #ffe58f; + } + } + + .suggestion-details-wrapper { + padding-top: 10px; // Increased padding + margin-top: 10px; // Increased margin + border-top: 1px dashed #dcdcdc; // Slightly darker dashed line + } + + .suggestion-details-area { + width: 100%; + min-height: 80px; // Increased min-height for the textarea + // Consider adding max-height or using 'rows' attribute if needed + // max-height: 150px; + // overflow-y: auto; // if max-height is set + font-family: monospace; + font-size: 0.85em; + padding: 8px; // Increased padding + border: 1px solid #ccc; + border-radius: 3px; + resize: vertical; + background-color: #fdfdfd; // Slightly off-white background for textarea + color: #333; + margin-bottom: 10px; // Increased margin + } + + .suggestion-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + + .action-btn { + &.accept-btn { + // Default primary button style is usually fine + } + + &.reject-btn { + // Default danger button style is usually fine + } + } + } } } } diff --git a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts index 19560493333..2857255d506 100644 --- a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts +++ b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts @@ -14,6 +14,11 @@ import { WorkflowDataCleaningSuggestionList, } from "src/app/workspace/types/workflow-suggestion.interface"; +interface DisplayableDataCleaningSuggestion extends WorkflowDataCleaningSuggestion { + isExpanded?: boolean; + details: string; // To store a string representation of changes or other details +} + @UntilDestroy() @Component({ selector: "texera-column-profile-frame", @@ -43,10 +48,9 @@ export class ColumnProfileFrameComponent implements OnInit { }; // For Data Cleaning Suggestions - public dataCleaningSuggestions: WorkflowDataCleaningSuggestion[] = []; + public dataCleaningSuggestions: DisplayableDataCleaningSuggestion[] = []; public isLoadingDataCleaningSuggestions: boolean = false; - // Cache can be managed here or in the service if suggestions are more global - private dataCleaningSuggestionsCache: Map = new Map(); + private dataCleaningSuggestionsCache: Map = new Map(); public lastFetchedSuggestionsForColumn: string | null = null; constructor( @@ -74,7 +78,7 @@ export class ColumnProfileFrameComponent implements OnInit { this.changeDetectorRef.detectChanges(); }); - this.updateChartViewWidth(); // Initial set + this.updateChartViewWidth(); } // Add AfterViewChecked to update chart width if panel resizes @@ -191,7 +195,7 @@ export class ColumnProfileFrameComponent implements OnInit { private fetchOrGetCachedDataCleaningSuggestions(columnProfile: ColumnProfile): void { const cached = this.dataCleaningSuggestionsCache.get(columnProfile.columnName); if (cached) { - this.dataCleaningSuggestions = cached; + this.dataCleaningSuggestions = cached.map(s => ({ ...s, isExpanded: s.isExpanded || false })); this.lastFetchedSuggestionsForColumn = columnProfile.columnName; this.isLoadingDataCleaningSuggestions = false; } else { @@ -219,8 +223,12 @@ export class ColumnProfileFrameComponent implements OnInit { ) .subscribe( (response: WorkflowDataCleaningSuggestionList) => { - this.dataCleaningSuggestions = response.suggestions; - this.dataCleaningSuggestionsCache.set(columnProfile.columnName, response.suggestions); + this.dataCleaningSuggestions = response.suggestions.map(s => ({ + ...s, + isExpanded: false, + details: s.details || "No specific changes detailed.", + })); + this.dataCleaningSuggestionsCache.set(columnProfile.columnName, this.dataCleaningSuggestions); this.lastFetchedSuggestionsForColumn = columnProfile.columnName; }, (error: unknown) => { @@ -237,8 +245,42 @@ export class ColumnProfileFrameComponent implements OnInit { } } - public handleDataCleaningSuggestionClick(suggestion: WorkflowDataCleaningSuggestion): void { - console.log("Clicked data cleaning suggestion in ColumnProfileFrameComponent:", suggestion); - // Implement actual handling logic here + public toggleSuggestionExpansion(suggestion: DisplayableDataCleaningSuggestion): void { + suggestion.isExpanded = !suggestion.isExpanded; + this.changeDetectorRef.detectChanges(); + } + + public acceptSuggestion(suggestion: DisplayableDataCleaningSuggestion, event: MouseEvent): void { + event.stopPropagation(); + console.log("Suggestion accepted (placeholder):", suggestion); + } + + public rejectSuggestion(suggestion: DisplayableDataCleaningSuggestion, event: MouseEvent): void { + event.stopPropagation(); + console.log("Suggestion rejected:", suggestion); + this.dataCleaningSuggestions = this.dataCleaningSuggestions.filter(s => s.suggestionID !== suggestion.suggestionID); + if (this.columnProfile) { + this.dataCleaningSuggestionsCache.set(this.columnProfile.columnName, this.dataCleaningSuggestions); + } + this.changeDetectorRef.detectChanges(); + } + + private removeSuggestion(suggestionToRemove: DisplayableDataCleaningSuggestion): void { + this.dataCleaningSuggestions = this.dataCleaningSuggestions.filter( + s => s.suggestionID !== suggestionToRemove.suggestionID + ); + if (this.columnProfile) { + const cached = this.dataCleaningSuggestionsCache.get(this.columnProfile.columnName); + if (cached) { + this.dataCleaningSuggestionsCache.set( + this.columnProfile.columnName, + cached.filter(s => s.suggestionID !== suggestionToRemove.suggestionID) + ); + } + } + } + + public onChartSelect(event: any): void { + console.log("Chart event:", event); } } diff --git a/core/gui/src/app/workspace/component/left-panel/left-panel.component.scss b/core/gui/src/app/workspace/component/left-panel/left-panel.component.scss index 045deb84208..b1e5e6f18a0 100644 --- a/core/gui/src/app/workspace/component/left-panel/left-panel.component.scss +++ b/core/gui/src/app/workspace/component/left-panel/left-panel.component.scss @@ -42,6 +42,9 @@ background: white; width: calc(100% - 33px); z-index: 2; + white-space: nowrap; + overflow: hidden; + line-height: 22px; } #dock { diff --git a/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts b/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts index 9659e6c4a3b..103b04d4512 100644 --- a/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts +++ b/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts @@ -111,7 +111,7 @@ export class LeftPanelComponent implements OnDestroy, OnInit, AfterViewInit { if (selectedInfo && this.columnProfilePanelIndex !== -1) { const columnProfileItem = this.items[this.columnProfilePanelIndex]; if (columnProfileItem) { - columnProfileItem.title = `Profile: ${selectedInfo.columnProfile.columnName.substring(0, 10)}${selectedInfo.columnProfile.columnName.length > 10 ? "..." : ""}`; + columnProfileItem.title = `Profile: ${selectedInfo.columnProfile.columnName}`; } this.openFrame(this.columnProfilePanelIndex, true); } else if (!selectedInfo && this.currentIndex === this.columnProfilePanelIndex) { From 37970b7129f92451bab267ab56bb6e70c7263f21 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 21 May 2025 13:16:40 -0700 Subject: [PATCH 085/104] add the schema passing --- .../column-profile-frame.component.ts | 16 ++++++++-- .../result-table-frame.component.ts | 29 ++----------------- .../column-profile/column-profile.service.ts | 2 ++ .../workflow-suggestion.service.ts | 6 +++- core/suggestion-service/model/web/input.py | 3 ++ .../suggestion_engine/generator.py | 14 +-------- 6 files changed, 26 insertions(+), 44 deletions(-) diff --git a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts index 2857255d506..11958f1f8d9 100644 --- a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts +++ b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts @@ -13,6 +13,7 @@ import { WorkflowDataCleaningSuggestion, WorkflowDataCleaningSuggestionList, } from "src/app/workspace/types/workflow-suggestion.interface"; +import { SchemaAttribute } from "src/app/workspace/types/workflow-compiling.interface"; interface DisplayableDataCleaningSuggestion extends WorkflowDataCleaningSuggestion { isExpanded?: boolean; @@ -28,7 +29,8 @@ interface DisplayableDataCleaningSuggestion extends WorkflowDataCleaningSuggesti export class ColumnProfileFrameComponent implements OnInit { public selectedColumnInfo: SelectedColumnInfo | null = null; public columnProfile: ColumnProfile | undefined; - public tableProfile: TableProfile | undefined; // To access global stats like total row count + public tableProfile: TableProfile | undefined; + public tableSchema: ReadonlyArray | undefined; public columnNumericStatsForTable: Array<{ metric: string; value: string | number | undefined }> = []; public barChartData: Array<{ name: string; value: number }> = []; @@ -69,10 +71,12 @@ export class ColumnProfileFrameComponent implements OnInit { if (selectedInfo) { this.columnProfile = selectedInfo.columnProfile; this.tableProfile = selectedInfo.tableProfile; + this.tableSchema = selectedInfo.schema; this.loadDisplayData(); } else { this.columnProfile = undefined; this.tableProfile = undefined; + this.tableSchema = undefined; this.resetDisplayData(); } this.changeDetectorRef.detectChanges(); @@ -132,6 +136,7 @@ export class ColumnProfileFrameComponent implements OnInit { this.dataCleaningSuggestions = []; this.isLoadingDataCleaningSuggestions = false; this.lastFetchedSuggestionsForColumn = null; + this.tableSchema = undefined; } public prepareColumnNumericStats(profile: ColumnProfile | undefined): void { @@ -204,7 +209,7 @@ export class ColumnProfileFrameComponent implements OnInit { } public fetchDataCleaningSuggestions(columnProfile: ColumnProfile | undefined) { - if (!columnProfile || !this.tableProfile || !this.selectedColumnInfo?.operatorId) { + if (!columnProfile || !this.tableProfile || !this.selectedColumnInfo?.operatorId || !this.tableSchema) { this.dataCleaningSuggestions = []; this.isLoadingDataCleaningSuggestions = false; return; @@ -213,7 +218,12 @@ export class ColumnProfileFrameComponent implements OnInit { this.isLoadingDataCleaningSuggestions = true; this.dataCleaningSuggestions = []; this.workflowSuggestionService - .getDataCleaningSuggestions(this.selectedColumnInfo.operatorId, this.tableProfile, columnProfile.columnName) + .getDataCleaningSuggestions( + this.selectedColumnInfo.operatorId, + this.tableSchema, + this.tableProfile, + columnProfile.columnName + ) .pipe( finalize(() => { this.isLoadingDataCleaningSuggestions = false; diff --git a/core/gui/src/app/workspace/component/result-panel/result-table-frame/result-table-frame.component.ts b/core/gui/src/app/workspace/component/result-panel/result-table-frame/result-table-frame.component.ts index ff90834eb7c..48b9a5b70ae 100644 --- a/core/gui/src/app/workspace/component/result-panel/result-table-frame/result-table-frame.component.ts +++ b/core/gui/src/app/workspace/component/result-panel/result-table-frame/result-table-frame.component.ts @@ -86,34 +86,9 @@ export class ResultTableFrameComponent implements OnInit, OnChanges { private schema: ReadonlyArray = []; tableProfile: TableProfile | undefined; - // For Column Details Modal - @ViewChild("columnDetailModalContent") columnDetailModalContent!: TemplateRef; - selectedColumnProfileForModal: ColumnProfile | undefined; - columnNumericStatsForTable: Array<{ metric: string; value: string | number | undefined }> = []; - barChartData: Array<{ name: string; value: number }> = []; - // ngx-charts options - view: [number, number] = [550, 300]; - showXAxis = true; - showYAxis = true; - gradient = false; - showLegend = false; - showXAxisLabel = true; - xAxisLabel = "Category"; - showYAxisLabel = true; - yAxisLabel = "Count"; - colorScheme = { - domain: ["#5AA454", "#A10A28", "#C7B42C", "#AAAAAA"], - }; - // For Global Stats Modal @ViewChild("globalStatsModalContent") globalStatsModalContent!: TemplateRef; - // For Data Cleaning Suggestions - dataCleaningSuggestions: WorkflowDataCleaningSuggestion[] = []; - isLoadingDataCleaningSuggestions: boolean = false; - // Keep track of the column for which suggestions were last fetched to avoid redundant calls if modal isn't fully closed/reopened. - lastFetchedSuggestionsForColumn: string | null = null; - constructor( private modalService: NzModalService, private workflowActionService: WorkflowActionService, @@ -418,16 +393,16 @@ export class ResultTableFrameComponent implements OnInit, OnChanges { return; } - // Announce the selected column. LeftPanelComponent will listen to this. + // Announce the selected column, now including the schema this.columnProfileService.selectColumn({ operatorId: this.operatorId, columnProfile: columnProfile, tableProfile: this.tableProfile, + schema: this.schema, }); // The LeftPanelComponent is responsible for opening the correct frame // when it detects a change from columnProfileService. - // No direct call to open a panel from here is needed. } showGlobalStats(): void { diff --git a/core/gui/src/app/workspace/service/column-profile/column-profile.service.ts b/core/gui/src/app/workspace/service/column-profile/column-profile.service.ts index e93a375a27d..5d521fd26d6 100644 --- a/core/gui/src/app/workspace/service/column-profile/column-profile.service.ts +++ b/core/gui/src/app/workspace/service/column-profile/column-profile.service.ts @@ -4,11 +4,13 @@ import { ColumnProfile, TableProfile, } from "../../../common/type/proto/edu/uci/ics/amber/engine/architecture/worker/tableprofile"; +import { SchemaAttribute } from "../../types/workflow-compiling.interface"; export interface SelectedColumnInfo { operatorId: string; columnProfile: ColumnProfile; tableProfile: TableProfile; // Pass the whole table profile for context (e.g. global row count) + schema: ReadonlyArray; } @Injectable({ diff --git a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts index 4251cc6a5f0..49319f19cc6 100644 --- a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts +++ b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts @@ -7,7 +7,7 @@ import { Workflow } from "../../../common/type/workflow"; import { ExecutionStateInfo } from "../../types/execute-workflow.interface"; import { WorkflowDataCleaningSuggestionList, WorkflowSuggestionList } from "../../types/workflow-suggestion.interface"; import { v4 as uuid } from "uuid"; -import { CompilationStateInfo } from "../../types/workflow-compiling.interface"; +import { CompilationStateInfo, SchemaAttribute } from "../../types/workflow-compiling.interface"; import { TableProfile } from "../../../common/type/proto/edu/uci/ics/amber/engine/architecture/worker/tableprofile"; // endpoint for workflow suggestions @@ -18,6 +18,7 @@ export const DATA_CLEANING_SUGGESTION_ENDPOINT = "data-cleaning-suggestion"; // Define the request interface if not already globally available export interface TableProfileSuggestionRequest { focusingOperatorID: string; + tableSchema: ReadonlyArray; tableProfile: TableProfile; targetColumnName: string; } @@ -173,17 +174,20 @@ export class WorkflowSuggestionService implements OnDestroy { /** * Requests data cleaning suggestions from the backend service based on table profile and target column. * @param focusingOperatorID + * @param tableSchema * @param tableProfile The complete table profile. * @param targetColumnName The name of the column for which to get suggestions. * @returns Observable of SuggestionList */ public getDataCleaningSuggestions( focusingOperatorID: string, + tableSchema: ReadonlyArray, tableProfile: TableProfile, targetColumnName: string ): Observable { const requestPayload: TableProfileSuggestionRequest = { focusingOperatorID: focusingOperatorID, + tableSchema: tableSchema, tableProfile: tableProfile, targetColumnName: targetColumnName, }; diff --git a/core/suggestion-service/model/web/input.py b/core/suggestion-service/model/web/input.py index 92e79fb9435..0fe1f9609ad 100644 --- a/core/suggestion-service/model/web/input.py +++ b/core/suggestion-service/model/web/input.py @@ -7,6 +7,8 @@ from typing import Dict, List, Any, Optional +from model.llm.interpretation import AttributeInterpretation + # Import all relevant classes from the proto definition from model.proto.edu.uci.ics.amber.engine.architecture.worker import ( TableProfile, @@ -83,6 +85,7 @@ def _deep_snake(d: Any) -> Any: class TableProfileSuggestionRequest(BaseModel): tableProfile: TableProfile + tableSchema: List[AttributeInterpretation] targetColumnName: str focusingOperatorID: str diff --git a/core/suggestion-service/suggestion_engine/generator.py b/core/suggestion-service/suggestion_engine/generator.py index 7a72fbf7b68..0f67ace2dfd 100644 --- a/core/suggestion-service/suggestion_engine/generator.py +++ b/core/suggestion-service/suggestion_engine/generator.py @@ -218,23 +218,11 @@ def generate_data_cleaning_suggestions( if target_profile is None: return DataCleaningSuggestionList(suggestions=[]) - # -------------------- 2️⃣ derive a SchemaInterpretation -------------- - # We expose the *entire* table schema (name + inferred type for each col) - attributes = [] - for col in tp.column_profiles: - attr = AttributeInterpretation( - attributeName=getattr(col, "column_name"), - attributeType=getattr(col, "data_type"), - ) - attributes.append(attr) - - schema_interp = SchemaInterpretation(attributes=attributes) - # -------------------- 3️⃣ compose the cleaning prompt ---------------- prompt_obj = DataCleaningSuggestionPrompt( focusingOperatorID=request.focusingOperatorID, # not available in this request columnProfile=target_profile, - tableSchema=schema_interp, + tableSchema=SchemaInterpretation(attributes=request.tableSchema), ) prompt_json = prompt_obj.model_dump_json(indent=2) From 209a41ac708ba0963d82299e56716371652df2b0 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 21 May 2025 13:36:26 -0700 Subject: [PATCH 086/104] extract out the suggestion action service --- .../suggestion-frame.component.ts | 389 +++--------------- .../suggestion-action.service.ts | 232 +++++++++++ 2 files changed, 282 insertions(+), 339 deletions(-) create mode 100644 core/gui/src/app/workspace/service/suggestion-action/suggestion-action.service.ts diff --git a/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts index ccc39729588..93b90c6fafd 100644 --- a/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts +++ b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts @@ -1,19 +1,13 @@ -import { Component, OnInit, HostListener, OnDestroy, ChangeDetectorRef, NgZone } from "@angular/core"; +import { Component, OnInit, OnDestroy, ChangeDetectorRef, NgZone } from "@angular/core"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; import { WorkflowActionService } from "../../../service/workflow-graph/model/workflow-action.service"; import { WorkflowSuggestionService } from "../../../service/workflow-suggestion/workflow-suggestion.service"; -import { WorkflowUtilService } from "../../../service/workflow-graph/util/workflow-util.service"; import { NzMessageService } from "ng-zorro-antd/message"; -import { WorkflowPersistService } from "../../../../common/service/workflow-persist/workflow-persist.service"; -import { Workflow } from "../../../../common/type/workflow"; -import { cloneDeep } from "lodash"; import { ExecuteWorkflowService } from "../../../service/execute-workflow/execute-workflow.service"; -import { ExecutionState } from "../../../types/execute-workflow.interface"; -import { filter } from "rxjs/operators"; import { WorkflowCompilingService } from "../../../service/compile-workflow/workflow-compiling.service"; -import { Subscription, interval } from "rxjs"; +import { Subscription, interval, Observable } from "rxjs"; import { WorkflowSuggestion, WorkflowSuggestionList } from "../../../types/workflow-suggestion.interface"; -import { OperatorPredicate } from "../../../types/workflow-common.interface"; +import { SuggestionActionService } from "../../../service/suggestion-action/suggestion-action.service"; /** * SuggestionFrameComponent is a wrapper for the workflow suggestion functionality @@ -26,81 +20,81 @@ import { OperatorPredicate } from "../../../types/workflow-common.interface"; styleUrls: ["./suggestion-frame.component.scss"], }) export class SuggestionFrameComponent implements OnInit, OnDestroy { - // Variables needed for suggestion functionality public suggestions: WorkflowSuggestion[] = []; - public activePreviewId: string | null = null; public canModify = true; public loadingSuggestions = false; public intentionText = ""; - // Store the workflow state before a preview is applied - private workflowBeforePreview: Workflow | null = null; - private viewStateBeforeRestore: { zoom: number; tx: number; ty: number } | null = null; - - // Store original operator properties to compare with changed properties - private originalOperatorProperties: Map = new Map(); - // Store property style maps for highlighting changed properties - private propertyStyleMaps: Map> = new Map(); + // Observables from SuggestionActionService for template binding + public activePreviewId: string | null = null; + public isInPreviewMode: boolean = false; - // Track if we're in preview mode to prevent tab changes - public isInPreviewMode = false; - // Track the tab focus interval private tabFocusInterval: Subscription | null = null; - // Store click listeners to prevent unnecessary event binding private boundHandleDocumentClick: any; constructor( private workflowActionService: WorkflowActionService, - private workflowUtilService: WorkflowUtilService, private workflowCompilingService: WorkflowCompilingService, private workflowExecuteService: ExecuteWorkflowService, private messageService: NzMessageService, - private workflowPersistService: WorkflowPersistService, private workflowSuggestionService: WorkflowSuggestionService, - private executeWorkflowService: ExecuteWorkflowService, + private suggestionActionService: SuggestionActionService, private cdr: ChangeDetectorRef, private ngZone: NgZone ) { + this.suggestionActionService.activePreviewId$ + .pipe(untilDestroyed(this)) + .subscribe(id => { + this.activePreviewId = id; + this.cdr.detectChanges(); + }); + + this.suggestionActionService.isInPreviewMode$ + .pipe(untilDestroyed(this)) + .subscribe(inPreview => { + this.isInPreviewMode = inPreview; + if (inPreview) { + this.addDocumentClickListener(); + } else { + this.removeDocumentClickListener(); + } + this.cdr.detectChanges(); + }); this.boundHandleDocumentClick = this.handleDocumentClick.bind(this); } ngOnInit(): void { - // Get initial permission state this.workflowActionService .getWorkflowModificationEnabledStream() .pipe(untilDestroyed(this)) .subscribe(canModify => (this.canModify = canModify)); - // Subscribe to global suggestion updates so that this frame reflects the latest results this.workflowSuggestionService .getSuggestionsListStream() .pipe(untilDestroyed(this)) .subscribe(list => { this.suggestions = list.suggestions; + this.cdr.detectChanges(); }); - // Track loading state this.workflowSuggestionService .getLoadingStream() .pipe(untilDestroyed(this)) - .subscribe(isLoading => (this.loadingSuggestions = isLoading)); + .subscribe(isLoading => { + this.loadingSuggestions = isLoading; + this.cdr.detectChanges(); + }); } ngOnDestroy(): void { - // Clean up event listener when component is destroyed this.removeDocumentClickListener(); - this.restoreCompilationListeners(); - // Clean up the tab focus interval if it exists if (this.tabFocusInterval) { this.tabFocusInterval.unsubscribe(); this.tabFocusInterval = null; } } - /** - * Add document click listener when entering preview mode - */ private addDocumentClickListener(): void { if (!document.hasOwnProperty("suggestionsClickListener")) { document.addEventListener("click", this.boundHandleDocumentClick, true); @@ -109,39 +103,24 @@ export class SuggestionFrameComponent implements OnInit, OnDestroy { } } - /** - * Remove document click listener when exiting preview mode - */ private removeDocumentClickListener(): void { document.removeEventListener("click", this.boundHandleDocumentClick, true); // @ts-ignore delete document.suggestionsClickListener; } - /** - * Event handler to prevent tab changes during preview mode - */ private handleDocumentClick(event: MouseEvent): void { if (this.isInPreviewMode) { - // Get the closest button within our component to avoid blocking action buttons const actionButton = (event.target as HTMLElement).closest(".suggestion-actions button"); - // Get the event target const target = event.target as HTMLElement; - // Allow clicks within our component actions area - if (actionButton) { - return; - } + if (actionButton) return; - // Check if the click target is a tab or a link that would change focus if (target && (target.closest(".ant-tabs-tab") || target.closest("a[href]"))) { - // Only prevent default if it's not within our suggestion component if (!target.closest("texera-suggestion-frame")) { event.preventDefault(); event.stopPropagation(); this.messageService.warning("Please cancel the preview first before changing tabs"); - - // Refocus the suggestion tab this.focusSuggestionTab(); } } @@ -149,10 +128,15 @@ export class SuggestionFrameComponent implements OnInit, OnDestroy { } public refreshSuggestions(): void { - if (this.isInPreviewMode) return; + if (this.isInPreviewMode) { + this.messageService.info("Please cancel the active preview before refreshing suggestions."); + return; + } const operators = this.workflowActionService.getTexeraGraph().getAllOperators(); - if (operators.length === 0) return; - + if (operators.length === 0) { + this.messageService.info("Cannot generate suggestions for an empty workflow."); + return; + } const focusedOperatorIDs = this.workflowActionService.getJointGraphWrapper().getCurrentHighlightedOperatorIDs(); const intention = this.intentionText.trim(); @@ -165,106 +149,34 @@ export class SuggestionFrameComponent implements OnInit, OnDestroy { this.workflowActionService.getJointGraphWrapper().getCurrentHighlightedOperatorIDs() ) .pipe(untilDestroyed(this)) - .subscribe((suggestionList: WorkflowSuggestionList) => { - console.log("Received suggestions:", suggestionList); - this.suggestions = suggestionList.suggestions; - }); + .subscribe(); } public togglePreview(suggestion: WorkflowSuggestion): void { - // Case 1: the clicked suggestion is the one currently in preview –> cancel preview. - if (this.activePreviewId === suggestion.suggestionID) { - this.clearPreviewAndRestoreWorkflow(); - return; - } - - // Case 2: a different suggestion is already in preview –> restore workflow first. - if (this.activePreviewId) { - this.clearPreviewAndRestoreWorkflow(); - } - - // Case 3: no preview is active –> start preview for the newly-clicked suggestion. - this.saveWorkflowState(); - this.propertyStyleMaps.clear(); - this.originalOperatorProperties.clear(); - this.isInPreviewMode = true; - this.addDocumentClickListener(); - this.workflowSuggestionService.setPreviewActive(true); - this.activePreviewId = suggestion.suggestionID; - this.cdr.detectChanges(); - - // Defer the heavy preview creation so the view (and action buttons) can render first. - // Using a micro-task ensures users immediately see the buttons on the first click. - this.ngZone.runOutsideAngular(() => { - setTimeout(() => { - this.createPreview(suggestion); - }); - }); - this.focusSuggestionTab(); - this.messageService.info("Preview mode active. Any compilation errors will be ignored."); - } - - /** - * Disables compilation listeners to prevent tab switching during preview - */ - private disableCompilationListeners(): void { - // We no longer need to intercept compilation changes since the service is now notified - // about preview mode and will handle this internally - } - - /** - * Restores original compilation listeners - */ - private restoreCompilationListeners(): void { - // We no longer need to restore compilation listeners since the service handles this - - // Just clear the tab focus interval if it exists - if (this.tabFocusInterval) { - this.tabFocusInterval.unsubscribe(); - this.tabFocusInterval = null; + this.suggestionActionService.togglePreview(suggestion); + if (this.isInPreviewMode) { + this.focusSuggestionTab(); } } - /** - * Focuses on the suggestion tab to prevent tab changes - */ private focusSuggestionTab(): void { - // Find the suggestion tab in the result panel and focus on it initially setTimeout(() => { const suggestionTab = document.querySelector(".ant-tabs-tab[aria-controls*=\"Suggestions\"]") as HTMLElement; if (suggestionTab) { suggestionTab.click(); - console.log("Focused on suggestion tab"); - - // Set up an interval to keep the suggestion tab focused during preview mode if (this.isInPreviewMode) { - // Clear any existing interval first - if (this.tabFocusInterval) { - this.tabFocusInterval.unsubscribe(); - } - - // Start a new interval that checks and refocuses the suggestion tab if needed + if (this.tabFocusInterval) this.tabFocusInterval.unsubscribe(); this.tabFocusInterval = interval(300) .pipe(untilDestroyed(this)) .subscribe(() => { if (this.isInPreviewMode) { const activeTab = document.querySelector(".ant-tabs-tab-active"); - const isSuggestionTabActive = - activeTab && - (activeTab.textContent?.includes("Suggestions") || - activeTab.getAttribute("aria-controls")?.includes("Suggestions")); - + const isSuggestionTabActive = activeTab && (activeTab.textContent?.includes("Suggestions") || activeTab.getAttribute("aria-controls")?.includes("Suggestions")); if (!isSuggestionTabActive) { - console.log("Refocusing suggestion tab..."); - const suggestionTab = document.querySelector( - ".ant-tabs-tab[aria-controls*=\"Suggestions\"]" - ) as HTMLElement; - if (suggestionTab) { - suggestionTab.click(); - } + const currentSuggestionTab = document.querySelector(".ant-tabs-tab[aria-controls*=\"Suggestions\"]") as HTMLElement; + if (currentSuggestionTab) currentSuggestionTab.click(); } } else if (this.tabFocusInterval) { - // If we're no longer in preview mode, clear the interval this.tabFocusInterval.unsubscribe(); this.tabFocusInterval = null; } @@ -274,220 +186,19 @@ export class SuggestionFrameComponent implements OnInit, OnDestroy { }, 50); } - /** - * Removes a suggestion from the list via service so all subscribers update - */ public dislikeSuggestion(suggestion: WorkflowSuggestion): void { - // If this is the active suggestion, restore the workflow first if (this.activePreviewId === suggestion.suggestionID) { - this.clearPreviewAndRestoreWorkflow(); + this.suggestionActionService.clearPreviewAndRestoreWorkflow(); } - - // Remove the suggestion from the list via service so all subscribers update this.workflowSuggestionService.removeSuggestionById(suggestion.suggestionID); - - // Show a message to confirm the action this.messageService.info("Suggestion removed from the list."); } - /** - * Cancels the preview of a suggestion - */ public cancelPreview(): void { - if (this.activePreviewId) { - this.clearPreviewAndRestoreWorkflow(); - this.messageService.info("Preview cancelled"); - } - } - - /** - * Saves the current workflow state - */ - private saveWorkflowState(): void { - const currentWorkflow = this.workflowActionService.getWorkflow(); - // Create a deep copy of the workflow to ensure we don't have references to the original - this.workflowBeforePreview = cloneDeep(currentWorkflow); - } - - /** - * Clears the preview and restores the original workflow - */ - private clearPreviewAndRestoreWorkflow(): void { - // Reset active preview ID - this.activePreviewId = null; - - // Exit preview mode - this.isInPreviewMode = false; - - // Remove the document click listener - this.removeDocumentClickListener(); - - // Notify the suggestion service that preview is no longer active - this.workflowSuggestionService.setPreviewActive(false); - - // Save current view zoom and offset before any reload resets them - const wrapper = this.workflowActionService.getJointGraphWrapper(); - try { - const paper = wrapper.getMainJointPaper(); - const translate = paper.translate(); - this.viewStateBeforeRestore = { zoom: wrapper.getZoomRatio(), tx: translate.tx, ty: translate.ty }; - } catch { - this.viewStateBeforeRestore = null; - } - - // Restore the workflow to its state before the preview - this.restoreWorkflowState(); - - // Restore previous view if captured - if (this.viewStateBeforeRestore) { - const wrapper2 = this.workflowActionService.getJointGraphWrapper(); - const paper2 = wrapper2.getMainJointPaper(); - wrapper2.setZoomProperty(this.viewStateBeforeRestore.zoom); - paper2.translate(this.viewStateBeforeRestore.tx, this.viewStateBeforeRestore.ty); - this.viewStateBeforeRestore = null; - } - } - - /** - * Restores the workflow to its state before the preview - */ - private restoreWorkflowState(): void { - // Reload the entire workflow snapshot to fully restore metadata and shared model - const snapshot = this.workflowBeforePreview; - this.workflowBeforePreview = null; - - if (snapshot) { - this.workflowActionService.reloadWorkflow(cloneDeep(snapshot)); - } - - this.propertyStyleMaps.clear(); - this.originalOperatorProperties.clear(); - } - - /** - * Internal helper to apply workflow changes described by a suggestion. - * Used by both applySuggestion and createPreview. - */ - private applyWorkflowSuggestion(suggestion: WorkflowSuggestion, options: { preview: boolean }): Map { - const texeraGraph = this.workflowActionService.getTexeraGraph(); - const jointGraph = this.workflowActionService.getJointGraph(); - const jointGraphWrapper = this.workflowActionService.getJointGraphWrapper(); - const operatorIDMap = new Map(); - const operatorsAndPositions: { op: OperatorPredicate; pos: { x: number; y: number } }[] = []; - - suggestion.changes.operatorsToAdd.forEach((op, index) => { - const isExisting = texeraGraph.hasOperator(op.operatorID); - - if (isExisting) { - if (options.preview) { - // Apply the property changes so users can preview new settings. - this.workflowActionService.setOperatorProperty(op.operatorID, { - ...op.operatorProperties, - }); - - // Highlight the operator with a blue stroke and light yellow fill. - const cell = jointGraph.getCell(op.operatorID); - if (cell) { - cell.attr({ - rect: { - fill: "rgba(255, 255, 204, 0.6)", - stroke: "#1890ff", - "stroke-width": 2, - }, - }); - } - } else { - // Permanently apply property changes when user accepts. - this.workflowActionService.setOperatorProperty(op.operatorID, { - ...op.operatorProperties, - }); - } - return; - } - - const newOp = this.workflowUtilService.getNewOperatorPredicate(op.operatorType); - Object.assign(newOp.operatorProperties, op.operatorProperties); - operatorIDMap.set(op.operatorID, newOp.operatorID); - - let pos = { x: 100, y: 100 + index * 100 }; - const anchorLink = suggestion.changes.linksToAdd.find(l => l.target.operatorID === op.operatorID); - if (anchorLink) { - const anchorID = texeraGraph.hasOperator(anchorLink.source.operatorID) - ? anchorLink.source.operatorID - : operatorIDMap.get(anchorLink.source.operatorID); - if (anchorID && texeraGraph.hasOperator(anchorID)) { - const anchorPos = jointGraphWrapper.getElementPosition(anchorID); - pos = { x: anchorPos.x + 200, y: anchorPos.y }; - } - } - - operatorsAndPositions.push({ op: newOp, pos }); - - if (options.preview) { - this.workflowActionService.addOperator(newOp, pos); - const cell = jointGraph.getCell(newOp.operatorID); - if (cell) { - cell.attr({ - ".": { opacity: 0.6 }, - rect: { stroke: "#1890ff", "stroke-width": 2 }, - }); - } - } - }); - - if (!options.preview) { - if (suggestion.changes.operatorsToDelete.length > 0) { - this.workflowActionService.deleteOperatorsAndLinks(suggestion.changes.operatorsToDelete); - } - this.workflowActionService.addOperatorsAndLinks(operatorsAndPositions); - } - - suggestion.changes.linksToAdd.forEach(link => { - const sourceID = operatorIDMap.get(link.source.operatorID) ?? link.source.operatorID; - const targetID = operatorIDMap.get(link.target.operatorID) ?? link.target.operatorID; - - if (texeraGraph.hasOperator(sourceID) && texeraGraph.hasOperator(targetID)) { - const linkObject = { - linkID: options.preview ? `link-preview-${Date.now()}` : `link-${Date.now()}`, - source: { operatorID: sourceID, portID: link.source.portID }, - target: { operatorID: targetID, portID: link.target.portID }, - }; - this.workflowActionService.addLink(linkObject); - - if (options.preview) { - const cell = jointGraph.getCell(linkObject.linkID); - if (cell) { - cell.attr({ - ".connection": { opacity: 0.6, stroke: "#1890ff" }, - ".marker-target": { opacity: 0.6, fill: "#1890ff" }, - }); - } - } - } - }); - - return operatorIDMap; + this.suggestionActionService.clearPreviewAndRestoreWorkflow(); } public applySuggestion(suggestion: WorkflowSuggestion): void { - this.clearPreviewAndRestoreWorkflow(); - try { - this.applyWorkflowSuggestion(suggestion, { preview: false }); - const workflow = this.workflowActionService.getWorkflow(); - this.workflowPersistService - .persistWorkflow(workflow) - .pipe(untilDestroyed(this)) - .subscribe(() => { - this.messageService.success("Successfully applied and saved the suggestion!"); - this.workflowSuggestionService.removeSuggestionById(suggestion.suggestionID); - }); - } catch (error) { - console.error("Error applying suggestion:", error); - this.messageService.error("Failed to apply the suggestion."); - } - } - - private createPreview(suggestion: WorkflowSuggestion): void { - this.applyWorkflowSuggestion(suggestion, { preview: true }); + this.suggestionActionService.applySuggestion(suggestion); } } diff --git a/core/gui/src/app/workspace/service/suggestion-action/suggestion-action.service.ts b/core/gui/src/app/workspace/service/suggestion-action/suggestion-action.service.ts new file mode 100644 index 00000000000..471d520e8cc --- /dev/null +++ b/core/gui/src/app/workspace/service/suggestion-action/suggestion-action.service.ts @@ -0,0 +1,232 @@ +import { Injectable, NgZone } from "@angular/core"; +import { BehaviorSubject, Observable } from "rxjs"; +import { Workflow } from "../../../common/type/workflow"; +import { WorkflowSuggestion } from "../../types/workflow-suggestion.interface"; +import { WorkflowActionService } from "../workflow-graph/model/workflow-action.service"; +import { WorkflowUtilService } from "../workflow-graph/util/workflow-util.service"; +import { WorkflowPersistService } from "../../../common/service/workflow-persist/workflow-persist.service"; +import { NzMessageService } from "ng-zorro-antd/message"; +import { cloneDeep } from "lodash"; +import { OperatorPredicate } from "../../types/workflow-common.interface"; +import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; +import {WorkflowSuggestionService} from "../workflow-suggestion/workflow-suggestion.service"; // For managing subscriptions in the service if needed + +// @UntilDestroy() // Add if service has long-lived subscriptions to self-manage +@Injectable({ + providedIn: "root", +}) +export class SuggestionActionService { + private activePreviewIdSubject = new BehaviorSubject(null); + public readonly activePreviewId$: Observable = this.activePreviewIdSubject.asObservable(); + + private isInPreviewModeSubject = new BehaviorSubject(false); + public readonly isInPreviewMode$: Observable = this.isInPreviewModeSubject.asObservable(); + + private workflowBeforePreview: Workflow | null = null; + private viewStateBeforeRestore: { zoom: number; tx: number; ty: number } | null = null; + + constructor( + private workflowActionService: WorkflowActionService, + private workflowUtilService: WorkflowUtilService, + private workflowPersistService: WorkflowPersistService, + private messageService: NzMessageService, + private workflowSuggestionService: WorkflowSuggestionService, + private ngZone: NgZone + ) {} + + public getActivePreviewId(): string | null { + return this.activePreviewIdSubject.getValue(); + } + + public isInPreviewMode(): boolean { + return this.isInPreviewModeSubject.getValue(); + } + + private saveWorkflowState(): void { + this.workflowBeforePreview = cloneDeep(this.workflowActionService.getWorkflow()); + const wrapper = this.workflowActionService.getJointGraphWrapper(); + try { + const paper = wrapper.getMainJointPaper(); + const translate = paper.translate(); + this.viewStateBeforeRestore = { zoom: wrapper.getZoomRatio(), tx: translate.tx, ty: translate.ty }; + } catch { + this.viewStateBeforeRestore = null; + } + } + + private restoreWorkflowState(): void { + const snapshot = this.workflowBeforePreview; + this.workflowBeforePreview = null; + + if (snapshot) { + this.workflowActionService.reloadWorkflow(cloneDeep(snapshot)); // Use a deep copy to prevent modifications + } + + if (this.viewStateBeforeRestore) { + // It's better to run this after the graph has been reloaded and rendered. + // Using a timeout ensures that the DOM and JointJS have settled. + setTimeout(() => { + this.ngZone.run(() => { // Ensure running within Angular's zone if it involves UI updates triggered by JointJS + const wrapper = this.workflowActionService.getJointGraphWrapper(); + const paper = wrapper.getMainJointPaper(); // Get a fresh reference + if (this.viewStateBeforeRestore) { // Check again as it might be cleared by another async operation + wrapper.setZoomProperty(this.viewStateBeforeRestore.zoom); + paper.translate(this.viewStateBeforeRestore.tx, this.viewStateBeforeRestore.ty); + this.viewStateBeforeRestore = null; + } + }); + }, 0); + } + } + + private applyWorkflowSuggestionChanges( + suggestion: WorkflowSuggestion, + options: { preview: boolean } + ): Map { + const texeraGraph = this.workflowActionService.getTexeraGraph(); + const jointGraph = this.workflowActionService.getJointGraph(); + const jointGraphWrapper = this.workflowActionService.getJointGraphWrapper(); + const operatorIDMap = new Map(); // Maps original suggested ID to newly created ID + const operatorsAndPositions: { op: OperatorPredicate; pos: { x: number; y: number } }[] = []; + + // Handle operators to add or modify properties + suggestion.changes.operatorsToAdd.forEach((opDetails, index) => { + const isExisting = texeraGraph.hasOperator(opDetails.operatorID); + + if (isExisting) { + // Operator exists, so we're modifying its properties + this.workflowActionService.setOperatorProperty(opDetails.operatorID, { ...opDetails.operatorProperties }); + if (options.preview) { + const cell = jointGraph.getCell(opDetails.operatorID); + if (cell) { + cell.attr({ rect: { fill: "rgba(255, 255, 204, 0.6)", stroke: "#1890ff", "stroke-width": 2 } }); + } + } + operatorIDMap.set(opDetails.operatorID, opDetails.operatorID); // Map to itself as it exists + return; + } + + // Operator does not exist, create and add it + const newOp = this.workflowUtilService.getNewOperatorPredicate(opDetails.operatorType); + Object.assign(newOp.operatorProperties, opDetails.operatorProperties); + // Important: Use the newOp.operatorID for mapping and further operations + operatorIDMap.set(opDetails.operatorID, newOp.operatorID); + + let pos = { x: 100, y: 100 + index * 100 }; // Default position + const anchorLink = suggestion.changes.linksToAdd.find(l => l.target.operatorID === opDetails.operatorID); + if (anchorLink) { + const sourceOpInGraphID = texeraGraph.hasOperator(anchorLink.source.operatorID) + ? anchorLink.source.operatorID + : operatorIDMap.get(anchorLink.source.operatorID); + + if (sourceOpInGraphID && texeraGraph.hasOperator(sourceOpInGraphID)) { + const anchorPos = jointGraphWrapper.getElementPosition(sourceOpInGraphID); + pos = { x: anchorPos.x + 200, y: anchorPos.y }; + } + } + operatorsAndPositions.push({ op: newOp, pos }); // Use newOp here + }); + + // Apply additions and deletions + if (!options.preview) { + // For permanent application, delete operators first + if (suggestion.changes.operatorsToDelete.length > 0) { + this.workflowActionService.deleteOperatorsAndLinks(suggestion.changes.operatorsToDelete); // Assuming linksToDelete is empty or handled separately + } + // Then add new/modified ones + this.workflowActionService.addOperatorsAndLinks(operatorsAndPositions, []); // Assuming linksToAdd handled next + } else { + // For preview, just add with visual styling + operatorsAndPositions.forEach(({op, pos}) => { + this.workflowActionService.addOperator(op, pos); + const cell = jointGraph.getCell(op.operatorID); // Use the actual ID of the added operator + if (cell) { + cell.attr({ ".": { opacity: 0.6 }, rect: { stroke: "#1890ff", "stroke-width": 2 } }); + } + }); + } + + + // Handle links to add + suggestion.changes.linksToAdd.forEach(linkDetails => { + // IMPORTANT: Use the operatorIDMap to get the *actual* IDs in the graph + const sourceIDInGraph = operatorIDMap.get(linkDetails.source.operatorID) ?? linkDetails.source.operatorID; + const targetIDInGraph = operatorIDMap.get(linkDetails.target.operatorID) ?? linkDetails.target.operatorID; + + if (texeraGraph.hasOperator(sourceIDInGraph) && texeraGraph.hasOperator(targetIDInGraph)) { + const linkObject = { + linkID: options.preview ? `link-preview-${Date.now()}-${Math.random().toString(36).substring(2,7)}` : `link-${Date.now()}-${Math.random().toString(36).substring(2,7)}`, // Ensure unique ID + source: { operatorID: sourceIDInGraph, portID: linkDetails.source.portID }, + target: { operatorID: targetIDInGraph, portID: linkDetails.target.portID }, + }; + this.workflowActionService.addLink(linkObject); + + if (options.preview) { + const cell = jointGraph.getCell(linkObject.linkID); + if (cell) { + cell.attr({ + ".connection": { opacity: 0.6, stroke: "#1890ff" }, + ".marker-target": { opacity: 0.6, fill: "#1890ff" }, + }); + } + } + } else { + console.warn("Could not add link, source or target operator not found in graph or ID map:", linkDetails, "Mapped IDs:", sourceIDInGraph, targetIDInGraph); + } + }); + return operatorIDMap; + } + + public togglePreview(suggestion: WorkflowSuggestion): void { + const currentPreviewId = this.activePreviewIdSubject.getValue(); + + if (currentPreviewId === suggestion.suggestionID) { + this.clearPreviewAndRestoreWorkflow(); + } else { + if (currentPreviewId) { + this.clearPreviewAndRestoreWorkflow(); // Clear existing preview first + } + this.saveWorkflowState(); + this.activePreviewIdSubject.next(suggestion.suggestionID); + this.isInPreviewModeSubject.next(true); + this.workflowSuggestionService.setPreviewActive(true); // Notify other services + + this.ngZone.runOutsideAngular(() => { + setTimeout(() => { // Defer to allow UI updates + this.ngZone.run(() => { // Ensure graph operations run in Angular zone if they trigger changes + this.applyWorkflowSuggestionChanges(suggestion, { preview: true }); + this.messageService.info("Suggestion preview active. Some functionalities might be limited."); + }); + }); + }); + } + } + + public clearPreviewAndRestoreWorkflow(): void { + if (!this.isInPreviewModeSubject.getValue()) return; + + this.restoreWorkflowState(); // Restore graph first + this.activePreviewIdSubject.next(null); + this.isInPreviewModeSubject.next(false); + this.workflowSuggestionService.setPreviewActive(false); // Notify other services + this.messageService.info("Suggestion preview cancelled."); + } + + public applySuggestion(suggestion: WorkflowSuggestion): void { + if (this.isInPreviewModeSubject.getValue()) { + this.clearPreviewAndRestoreWorkflow(); // Clear preview before applying + } + try { + this.applyWorkflowSuggestionChanges(suggestion, { preview: false }); + const workflow = this.workflowActionService.getWorkflow(); + // No need for untilDestroyed(this) if this service is root-provided and lives for app duration + this.workflowPersistService.persistWorkflow(workflow).subscribe(() => { + this.messageService.success("Suggestion applied and workflow saved!"); + this.workflowSuggestionService.removeSuggestionById(suggestion.suggestionID); + }); + } catch (error) { + console.error("Error applying suggestion:", error); + this.messageService.error("Failed to apply the suggestion."); + } + } +} From 02773cc07d5c33b83d866b1be9af9ab91b72e829 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 21 May 2025 14:48:40 -0700 Subject: [PATCH 087/104] add the schema passing to the suggestion service --- .../column-profile-frame.component.html | 1 + .../column-profile-frame.component.ts | 82 ++++++++++++++++--- .../operator-property-edit-frame.component.ts | 3 +- .../error-frame/error-frame.component.ts | 3 +- .../suggestion-frame.component.ts | 44 +++++----- .../suggestion-action.service.ts | 43 ++++++---- .../workflow-suggestion.service.ts | 3 +- 7 files changed, 126 insertions(+), 53 deletions(-) diff --git a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.html b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.html index 5616d2d4926..793e221c433 100644 --- a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.html +++ b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.html @@ -121,6 +121,7 @@

    Data Cleaning Suggestions

    nzSize="small" class="action-btn accept-btn" (click)="acceptSuggestion(suggestion, $event)" + [nzLoading]="suggestion.isAccepting" title="Accept suggestion"> ({ ...s, isExpanded: s.isExpanded || false })); + this.dataCleaningSuggestions = cached.map(s => ({ ...s, isExpanded: s.isExpanded || false, isAccepting: false })); this.lastFetchedSuggestionsForColumn = columnProfile.columnName; this.isLoadingDataCleaningSuggestions = false; } else { @@ -236,7 +246,8 @@ export class ColumnProfileFrameComponent implements OnInit { this.dataCleaningSuggestions = response.suggestions.map(s => ({ ...s, isExpanded: false, - details: s.details || "No specific changes detailed.", + details: s.details, + isAccepting: false, })); this.dataCleaningSuggestionsCache.set(columnProfile.columnName, this.dataCleaningSuggestions); this.lastFetchedSuggestionsForColumn = columnProfile.columnName; @@ -262,20 +273,64 @@ export class ColumnProfileFrameComponent implements OnInit { public acceptSuggestion(suggestion: DisplayableDataCleaningSuggestion, event: MouseEvent): void { event.stopPropagation(); - console.log("Suggestion accepted (placeholder):", suggestion); + if (!this.selectedColumnInfo || !this.selectedColumnInfo.tableProfile || suggestion.isAccepting) { + if (!this.selectedColumnInfo) console.warn("acceptSuggestion: selectedColumnInfo is null."); + else if (!this.selectedColumnInfo.schema) console.warn("acceptSuggestion: selectedColumnInfo.schema is null."); + else if (suggestion.isAccepting) console.warn("acceptSuggestion: suggestion is already being accepted."); + return; + } + + suggestion.isAccepting = true; + this.messageService.loading(`Attempting to apply action for: "${suggestion.suggestion}"...`, { nzDuration: 0 }); + + const operatorIDToSchema: { [key: string]: ReadonlyArray } = {}; + operatorIDToSchema[this.selectedColumnInfo.operatorId] = this.selectedColumnInfo.schema; + + this.workflowSuggestionService + .getSuggestions( + this.workflowActionService.getWorkflow(), + this.workflowCompilingService.getWorkflowCompilationStateInfo(), + this.executeWorkflowService.getExecutionState(), + suggestion.suggestion, + [this.selectedColumnInfo.operatorId], + operatorIDToSchema + ) + .pipe( + finalize(() => { + suggestion.isAccepting = false; + this.messageService.remove(); + this.changeDetectorRef.detectChanges(); + }), + untilDestroyed(this) + ) + .subscribe( + (actionableSuggestions: WorkflowSuggestionList) => { + if (actionableSuggestions.suggestions && actionableSuggestions.suggestions.length > 0) { + const firstActionableSuggestion = actionableSuggestions.suggestions[0]; + this.messageService.success( + `Applying action: "${firstActionableSuggestion.suggestion}" based on your request.` + ); + this.suggestionActionService.applySuggestion(firstActionableSuggestion); + this.removeSuggestionFromList(suggestion); + } else { + this.messageService.warning( + `Could not find an actionable Texera suggestion for: "${suggestion.suggestion}"` + ); + } + }, + (error: unknown) => { + console.error("Error getting actionable suggestions:", error); + this.messageService.error("Failed to get actionable suggestions."); + } + ); } public rejectSuggestion(suggestion: DisplayableDataCleaningSuggestion, event: MouseEvent): void { event.stopPropagation(); - console.log("Suggestion rejected:", suggestion); - this.dataCleaningSuggestions = this.dataCleaningSuggestions.filter(s => s.suggestionID !== suggestion.suggestionID); - if (this.columnProfile) { - this.dataCleaningSuggestionsCache.set(this.columnProfile.columnName, this.dataCleaningSuggestions); - } - this.changeDetectorRef.detectChanges(); + this.removeSuggestionFromList(suggestion); } - private removeSuggestion(suggestionToRemove: DisplayableDataCleaningSuggestion): void { + private removeSuggestionFromList(suggestionToRemove: DisplayableDataCleaningSuggestion): void { this.dataCleaningSuggestions = this.dataCleaningSuggestions.filter( s => s.suggestionID !== suggestionToRemove.suggestionID ); @@ -288,6 +343,7 @@ export class ColumnProfileFrameComponent implements OnInit { ); } } + this.changeDetectorRef.detectChanges(); } public onChartSelect(event: any): void { diff --git a/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts b/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts index 10f948ab986..4d44baa436c 100644 --- a/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts +++ b/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts @@ -824,7 +824,8 @@ export class OperatorPropertyEditFrameComponent implements OnInit, OnChanges, On this.workflowCompilingService.getWorkflowCompilationStateInfo(), this.executeWorkflowService.getExecutionState(), this.intentionText.trim(), - this.workflowActionService.getJointGraphWrapper().getCurrentHighlightedOperatorIDs() + this.workflowActionService.getJointGraphWrapper().getCurrentHighlightedOperatorIDs(), + {} ) .pipe(untilDestroyed(this)) .subscribe(() => { diff --git a/core/gui/src/app/workspace/component/result-panel/error-frame/error-frame.component.ts b/core/gui/src/app/workspace/component/result-panel/error-frame/error-frame.component.ts index 7e3cecb85a9..16b5ba4d280 100644 --- a/core/gui/src/app/workspace/component/result-panel/error-frame/error-frame.component.ts +++ b/core/gui/src/app/workspace/component/result-panel/error-frame/error-frame.component.ts @@ -97,7 +97,8 @@ export class ErrorFrameComponent implements OnInit { this.workflowCompilingService.getWorkflowCompilationStateInfo(), this.executeWorkflowService.getExecutionState(), "Please fix this error", - focusingIDs + focusingIDs, + {} ) .pipe(untilDestroyed(this)) .subscribe(_ => { diff --git a/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts index 93b90c6fafd..14eaea9ec23 100644 --- a/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts +++ b/core/gui/src/app/workspace/component/result-panel/suggestion-frame/suggestion-frame.component.ts @@ -42,24 +42,20 @@ export class SuggestionFrameComponent implements OnInit, OnDestroy { private cdr: ChangeDetectorRef, private ngZone: NgZone ) { - this.suggestionActionService.activePreviewId$ - .pipe(untilDestroyed(this)) - .subscribe(id => { - this.activePreviewId = id; - this.cdr.detectChanges(); - }); - - this.suggestionActionService.isInPreviewMode$ - .pipe(untilDestroyed(this)) - .subscribe(inPreview => { - this.isInPreviewMode = inPreview; - if (inPreview) { - this.addDocumentClickListener(); - } else { - this.removeDocumentClickListener(); - } - this.cdr.detectChanges(); - }); + this.suggestionActionService.activePreviewId$.pipe(untilDestroyed(this)).subscribe(id => { + this.activePreviewId = id; + this.cdr.detectChanges(); + }); + + this.suggestionActionService.isInPreviewMode$.pipe(untilDestroyed(this)).subscribe(inPreview => { + this.isInPreviewMode = inPreview; + if (inPreview) { + this.addDocumentClickListener(); + } else { + this.removeDocumentClickListener(); + } + this.cdr.detectChanges(); + }); this.boundHandleDocumentClick = this.handleDocumentClick.bind(this); } @@ -146,7 +142,8 @@ export class SuggestionFrameComponent implements OnInit, OnDestroy { this.workflowCompilingService.getWorkflowCompilationStateInfo(), this.workflowExecuteService.getExecutionState(), this.intentionText.trim(), - this.workflowActionService.getJointGraphWrapper().getCurrentHighlightedOperatorIDs() + this.workflowActionService.getJointGraphWrapper().getCurrentHighlightedOperatorIDs(), + {} ) .pipe(untilDestroyed(this)) .subscribe(); @@ -171,9 +168,14 @@ export class SuggestionFrameComponent implements OnInit, OnDestroy { .subscribe(() => { if (this.isInPreviewMode) { const activeTab = document.querySelector(".ant-tabs-tab-active"); - const isSuggestionTabActive = activeTab && (activeTab.textContent?.includes("Suggestions") || activeTab.getAttribute("aria-controls")?.includes("Suggestions")); + const isSuggestionTabActive = + activeTab && + (activeTab.textContent?.includes("Suggestions") || + activeTab.getAttribute("aria-controls")?.includes("Suggestions")); if (!isSuggestionTabActive) { - const currentSuggestionTab = document.querySelector(".ant-tabs-tab[aria-controls*=\"Suggestions\"]") as HTMLElement; + const currentSuggestionTab = document.querySelector( + ".ant-tabs-tab[aria-controls*=\"Suggestions\"]" + ) as HTMLElement; if (currentSuggestionTab) currentSuggestionTab.click(); } } else if (this.tabFocusInterval) { diff --git a/core/gui/src/app/workspace/service/suggestion-action/suggestion-action.service.ts b/core/gui/src/app/workspace/service/suggestion-action/suggestion-action.service.ts index 471d520e8cc..31f083b71f9 100644 --- a/core/gui/src/app/workspace/service/suggestion-action/suggestion-action.service.ts +++ b/core/gui/src/app/workspace/service/suggestion-action/suggestion-action.service.ts @@ -9,7 +9,7 @@ import { NzMessageService } from "ng-zorro-antd/message"; import { cloneDeep } from "lodash"; import { OperatorPredicate } from "../../types/workflow-common.interface"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; -import {WorkflowSuggestionService} from "../workflow-suggestion/workflow-suggestion.service"; // For managing subscriptions in the service if needed +import { WorkflowSuggestionService } from "../workflow-suggestion/workflow-suggestion.service"; // For managing subscriptions in the service if needed // @UntilDestroy() // Add if service has long-lived subscriptions to self-manage @Injectable({ @@ -66,13 +66,15 @@ export class SuggestionActionService { // It's better to run this after the graph has been reloaded and rendered. // Using a timeout ensures that the DOM and JointJS have settled. setTimeout(() => { - this.ngZone.run(() => { // Ensure running within Angular's zone if it involves UI updates triggered by JointJS + this.ngZone.run(() => { + // Ensure running within Angular's zone if it involves UI updates triggered by JointJS const wrapper = this.workflowActionService.getJointGraphWrapper(); const paper = wrapper.getMainJointPaper(); // Get a fresh reference - if (this.viewStateBeforeRestore) { // Check again as it might be cleared by another async operation - wrapper.setZoomProperty(this.viewStateBeforeRestore.zoom); - paper.translate(this.viewStateBeforeRestore.tx, this.viewStateBeforeRestore.ty); - this.viewStateBeforeRestore = null; + if (this.viewStateBeforeRestore) { + // Check again as it might be cleared by another async operation + wrapper.setZoomProperty(this.viewStateBeforeRestore.zoom); + paper.translate(this.viewStateBeforeRestore.tx, this.viewStateBeforeRestore.ty); + this.viewStateBeforeRestore = null; } }); }, 0); @@ -127,26 +129,25 @@ export class SuggestionActionService { operatorsAndPositions.push({ op: newOp, pos }); // Use newOp here }); - // Apply additions and deletions + // Apply additions and deletions if (!options.preview) { // For permanent application, delete operators first if (suggestion.changes.operatorsToDelete.length > 0) { this.workflowActionService.deleteOperatorsAndLinks(suggestion.changes.operatorsToDelete); // Assuming linksToDelete is empty or handled separately } - // Then add new/modified ones + // Then add new/modified ones this.workflowActionService.addOperatorsAndLinks(operatorsAndPositions, []); // Assuming linksToAdd handled next } else { // For preview, just add with visual styling - operatorsAndPositions.forEach(({op, pos}) => { + operatorsAndPositions.forEach(({ op, pos }) => { this.workflowActionService.addOperator(op, pos); const cell = jointGraph.getCell(op.operatorID); // Use the actual ID of the added operator if (cell) { - cell.attr({ ".": { opacity: 0.6 }, rect: { stroke: "#1890ff", "stroke-width": 2 } }); + cell.attr({ ".": { opacity: 0.6 }, rect: { stroke: "#1890ff", "stroke-width": 2 } }); } }); } - // Handle links to add suggestion.changes.linksToAdd.forEach(linkDetails => { // IMPORTANT: Use the operatorIDMap to get the *actual* IDs in the graph @@ -155,7 +156,9 @@ export class SuggestionActionService { if (texeraGraph.hasOperator(sourceIDInGraph) && texeraGraph.hasOperator(targetIDInGraph)) { const linkObject = { - linkID: options.preview ? `link-preview-${Date.now()}-${Math.random().toString(36).substring(2,7)}` : `link-${Date.now()}-${Math.random().toString(36).substring(2,7)}`, // Ensure unique ID + linkID: options.preview + ? `link-preview-${Date.now()}-${Math.random().toString(36).substring(2, 7)}` + : `link-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, // Ensure unique ID source: { operatorID: sourceIDInGraph, portID: linkDetails.source.portID }, target: { operatorID: targetIDInGraph, portID: linkDetails.target.portID }, }; @@ -171,7 +174,13 @@ export class SuggestionActionService { } } } else { - console.warn("Could not add link, source or target operator not found in graph or ID map:", linkDetails, "Mapped IDs:", sourceIDInGraph, targetIDInGraph); + console.warn( + "Could not add link, source or target operator not found in graph or ID map:", + linkDetails, + "Mapped IDs:", + sourceIDInGraph, + targetIDInGraph + ); } }); return operatorIDMap; @@ -192,10 +201,12 @@ export class SuggestionActionService { this.workflowSuggestionService.setPreviewActive(true); // Notify other services this.ngZone.runOutsideAngular(() => { - setTimeout(() => { // Defer to allow UI updates - this.ngZone.run(() => { // Ensure graph operations run in Angular zone if they trigger changes + setTimeout(() => { + // Defer to allow UI updates + this.ngZone.run(() => { + // Ensure graph operations run in Angular zone if they trigger changes this.applyWorkflowSuggestionChanges(suggestion, { preview: true }); - this.messageService.info("Suggestion preview active. Some functionalities might be limited."); + this.messageService.info("Suggestion preview active. Some functionalities might be limited."); }); }); }); diff --git a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts index 49319f19cc6..4d5ada3596e 100644 --- a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts +++ b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts @@ -81,7 +81,8 @@ export class WorkflowSuggestionService implements OnDestroy { compilationState: CompilationStateInfo, executionState: ExecutionStateInfo, intention: string, - focusingOperatorIDs: readonly string[] + focusingOperatorIDs: readonly string[], + operatorIDToTableSchemaMap: Record> ): Observable { // indicate loading started this.suggestionsLoadingSubject.next(true); From e76f7970262d700b554e035a451c34c2f07543de Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 21 May 2025 22:28:34 -0700 Subject: [PATCH 088/104] make the e2e work --- core/suggestion-service/app.py | 8 +---- .../instruction_for_data_cleaning_agent.md | 28 ++++++++++++++- .../files/instruction_for_function_call.md | 36 ++++++++++++++++++- .../model/llm/interpretation.py | 5 ++- core/suggestion-service/model/web/input.py | 1 + .../suggestion_engine/generator.py | 29 +++++---------- 6 files changed, 76 insertions(+), 31 deletions(-) diff --git a/core/suggestion-service/app.py b/core/suggestion-service/app.py index 37a41819239..f9294580393 100644 --- a/core/suggestion-service/app.py +++ b/core/suggestion-service/app.py @@ -53,13 +53,7 @@ async def generate_suggestions(request: SuggestionRequest): """ try: # Generate suggestions using the suggestion engine - suggestions = suggestion_generator.generate_suggestions( - request.workflow, - request.compilationState, - request.executionState, - request.intention, - request.focusingOperatorIDs, - ) + suggestions = suggestion_generator.generate_suggestions(request) return suggestions diff --git a/core/suggestion-service/files/instruction_for_data_cleaning_agent.md b/core/suggestion-service/files/instruction_for_data_cleaning_agent.md index a31dec24290..7a39560244b 100644 --- a/core/suggestion-service/files/instruction_for_data_cleaning_agent.md +++ b/core/suggestion-service/files/instruction_for_data_cleaning_agent.md @@ -59,7 +59,33 @@ An object containing multiple fields about the statistics of the column: * Generate **1-2** suggestions. Each suggestion **must** have: - **`suggestion`** – a short, imperative headline (e.g., “Impute missing values with median”). - - **`details`** – 2-3 sentences explaining *why* and the detailed action plan (method / parameter hints). + - **`details`** – 2-3 sentences explaining *why* and the detailed action plan (method / parameter hints). + - In the details, you MUST mention the operator type you want to use and describe the parameters you will set. The available operator types you can use are: + - Filter: performs a filter of the column with certain threshold. Filter should be used for very simple cleaning with single comparison condition. + - PythonUDFV2: performs the customized data cleaning logic. PythonUDFV2 should be used for complex data manipulation. You MUST describe which API to use and the the logics in the python code. + There are 2 APIs to process the data in different units. + 1. Tuple API. + + ```python + class ProcessTupleOperator(UDFOperatorV2): + + def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]: + yield tuple_ + + ``` + Tuple API takes one input tuple from a port at a time. It returns an iterator of optional `TupleLike` instances. A `TupleLike` is any data structure that supports key-value pairs, such as `pytexera.Tuple`, `dict`, `defaultdict`, `NamedTuple`, etc. + Tuple API is useful for implementing functional operations which are applied to tuples one by one, such as map, reduce, and filter. + + 2. Table API. + + ```python + class ProcessTableOperator(UDFTableOperator): + + def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]: + yield table + ``` + Table API consumes a `Table` at a time, which consists of all the whole table from a port. It returns an iterator of optional `TableLike` instances. A `TableLike ` is a collection of `TupleLike`, and currently, we support `pytexera.Table` and `pandas.DataFrame` as a `TableLike` instance. + Table API is useful for implementing blocking operations that will consume the whole column to do operations. * **Respond with exactly one JSON object** that conforms to the schema `DataCleaningSuggestionList`: diff --git a/core/suggestion-service/files/instruction_for_function_call.md b/core/suggestion-service/files/instruction_for_function_call.md index a3f8cf35916..2223a56c55c 100644 --- a/core/suggestion-service/files/instruction_for_function_call.md +++ b/core/suggestion-service/files/instruction_for_function_call.md @@ -157,4 +157,38 @@ You are an AI assistant that helps users improve their Texera workflows. Your ta * When deleting the operators, you MUST make sure the operatorIDs exist in the given workflow json. * When adding the links, you MUST make sure the operatorID in each link exists either in given workflow json, or in the new operator list * When deleting the links, you MUST make sure the linkIDs exist in the given workflow json. -* suggestion field in each suggestion should be high level. You do NOT need to explain the detail like add `X` after `Y`. \ No newline at end of file +* suggestion field in each suggestion should be high level. You do NOT need to explain the detail like add `X` after `Y`. +* When the intention contains the specific operations on which operator to use, you MUST follow the intention to check that operator's json schema and generate that operator with link to the existing operator. + +# Guideline of using PythonUDFV2 operator +PythonUDFV2: performs the customized data cleaning logic. There are 2 APIs to process the data in different units. +1. Tuple API. + +```python +from pytexera import * + +class ProcessTupleOperator(UDFOperatorV2): + + def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]: + yield tuple_ + +``` +Tuple API takes one input tuple from a port at a time. It returns an iterator of optional `TupleLike` instances. A `TupleLike` is any data structure that supports key-value pairs, such as `pytexera.Tuple`, `dict`, `defaultdict`, `NamedTuple`, etc. +Tuple API is useful for implementing functional operations which are applied to tuples one by one, such as map, reduce, and filter. + +2. Table API. + +```python +from pytexera import * + +class ProcessTableOperator(UDFTableOperator): + + def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]: + yield table +``` +Table API consumes a `Table` at a time, which consists of all the whole table from a port. It returns an iterator of optional `TableLike` instances. A `TableLike ` is a collection of `TupleLike`, and currently, we support `pytexera.Table` and `pandas.DataFrame` as a `TableLike` instance. +Table API is useful for implementing blocking operations that will consume the whole column to do operations. + +* When writing the udf code, you MUST NOT change the class name +* You can import pandas, numpy, sklearn and other common python packages +* You don't need to import typing for the type annotations. \ No newline at end of file diff --git a/core/suggestion-service/model/llm/interpretation.py b/core/suggestion-service/model/llm/interpretation.py index dfe6bb4ec5a..37065aacc2a 100644 --- a/core/suggestion-service/model/llm/interpretation.py +++ b/core/suggestion-service/model/llm/interpretation.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from enum import Enum -from pydantic import BaseModel +from pydantic import BaseModel, Field from typing import Dict, List, Optional, Any @@ -33,6 +33,9 @@ class OperatorInterpretation(BaseModel): operatorProperties: Dict[str, Any] error: Optional[ErrorInterpretation] inputSchemas: Dict[str, PortInterpretation] + outputSchema: SchemaInterpretation = Field( + default_factory=lambda: SchemaInterpretation(attributes=[]) + ) class LinkEndInterpretation(BaseModel): diff --git a/core/suggestion-service/model/web/input.py b/core/suggestion-service/model/web/input.py index 0fe1f9609ad..1a0e48f8db2 100644 --- a/core/suggestion-service/model/web/input.py +++ b/core/suggestion-service/model/web/input.py @@ -60,6 +60,7 @@ class SuggestionRequest(BaseModel): focusingOperatorIDs: Optional[List[str]] = Field( default_factory=list, description="Operator IDs that the user wants to focus on" ) + operatorIDToTableSchemaMap: Dict[str, List[AttributeInterpretation]] def _camel_to_snake(name: str) -> str: diff --git a/core/suggestion-service/suggestion_engine/generator.py b/core/suggestion-service/suggestion_engine/generator.py index 0f67ace2dfd..6c7f5e79f6e 100644 --- a/core/suggestion-service/suggestion_engine/generator.py +++ b/core/suggestion-service/suggestion_engine/generator.py @@ -9,6 +9,7 @@ CompilationStateInfo, ExecutionStateInfo, TableProfileSuggestionRequest, + SuggestionRequest, ) from workflow_interpretation.interpreter import ( WorkflowInterpreter, @@ -26,7 +27,6 @@ from llm_agent.base import LLMAgentFactory from distutils.util import strtobool # stdlib helper - # Load environment variables from .env file if present load_dotenv() @@ -50,11 +50,6 @@ class SuggestionGenerator: def __init__(self): """ Initialize the suggestion generator. - - Args: - llm_provider: The LLM provider to use (defaults to environment variable LLM_PROVIDER) - llm_model: The LLM model to use (defaults to environment variable LLM_MODEL) - llm_api_key: The API key for the LLM provider (defaults to environment variable based on provider) """ self.workflow_interpretation_method = InterpretationMethod( os.environ.get("INTERPRETATION_METHOD") @@ -100,45 +95,37 @@ def __init__(self): def generate_suggestions( self, - workflow: str, - compilation_state: CompilationStateInfo, - execution_state: ExecutionStateInfo, - intention: str, - focusing_operator_ids: List[str], + request: SuggestionRequest, enable_logging: bool = True, ) -> SuggestionList: """ Generate workflow suggestions based on the current workflow, compilation state, execution state, and result tables. - Args: - workflow: The current workflow configuration - compilation_state: Compilation information and errors - execution_state: Current execution state of the workflow - intention: The intention of the workflow - focusing_operator_ids: List of operator IDs to focus on - Returns: A list of workflow suggestions """ # If LLM generation failed or agent is not available, return mock suggestions if not self.llm_agent: return SuggestionList(suggestions=[]) - workflow_json = json.loads(workflow) + workflow_json = json.loads(request.workflow) # Generate natural language description of the workflow interpretation = self.workflow_interpreter.interpret_workflow( workflow_json, - compilation_state, + request.compilationState, ) # Determine intention (fallback) + intention = request.intention if not intention: intention = "Recommend improvements and fixes of current workflows" workflow_intp = interpretation.get_base_workflow_interpretation() focusing_operators: List[OperatorInterpretation] = [] - for oid in focusing_operator_ids: + for oid in request.focusingOperatorIDs: op = workflow_intp.operators.get(oid) if op: + output_attributes = request.operatorIDToTableSchemaMap.get(oid, []) + op.outputSchema = SchemaInterpretation(attributes=output_attributes) focusing_operators.append(op) prompt_obj = SuggestionPrompt( From c99207399ab3bf05f08b90c7c4f033848d651e6d Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 21 May 2025 22:29:03 -0700 Subject: [PATCH 089/104] have the e2e work --- .../service/workflow-suggestion/workflow-suggestion.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts index 4d5ada3596e..8cf7ec1421f 100644 --- a/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts +++ b/core/gui/src/app/workspace/service/workflow-suggestion/workflow-suggestion.service.ts @@ -112,6 +112,7 @@ export class WorkflowSuggestionService implements OnDestroy { executionState: executionState, intention: intention, focusingOperatorIDs: focusingOperatorIDs, + operatorIDToTableSchemaMap: operatorIDToTableSchemaMap, }) .pipe( map(suggestionList => { From c5793930e320c183798a6371f3537aa5dc2e6326 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 21 May 2025 22:45:28 -0700 Subject: [PATCH 090/104] fix op type and operator icon --- .../operator_images/IntelligentCSVReader.png | Bin 0 -> 81043 bytes .../scan/csv/PythonBasedCSVReaderOpDesc.scala | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 core/gui/src/assets/operator_images/IntelligentCSVReader.png diff --git a/core/gui/src/assets/operator_images/IntelligentCSVReader.png b/core/gui/src/assets/operator_images/IntelligentCSVReader.png new file mode 100644 index 0000000000000000000000000000000000000000..d0dfd1a8f9a19ff37553d0e3331e376536692845 GIT binary patch literal 81043 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

    OF}@!)tP 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 rB_ne2qTI!c Date: Wed, 21 May 2025 23:05:11 -0700 Subject: [PATCH 091/104] improve the performance of the csv reader --- .../QueryWorkerStatisticsHandler.scala | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/amber/engine/architecture/controller/promisehandlers/QueryWorkerStatisticsHandler.scala b/core/amber/src/main/scala/edu/uci/ics/amber/engine/architecture/controller/promisehandlers/QueryWorkerStatisticsHandler.scala index 87ce691a6bb..6cd603f8d9f 100644 --- a/core/amber/src/main/scala/edu/uci/ics/amber/engine/architecture/controller/promisehandlers/QueryWorkerStatisticsHandler.scala +++ b/core/amber/src/main/scala/edu/uci/ics/amber/engine/architecture/controller/promisehandlers/QueryWorkerStatisticsHandler.scala @@ -19,32 +19,23 @@ package edu.uci.ics.amber.engine.architecture.controller.promisehandlers -import com.twitter.util.Future +import com.twitter.util.{Future, Promise} import edu.uci.ics.amber.core.virtualidentity.ActorVirtualIdentity -import edu.uci.ics.amber.engine.architecture.controller.{ - ControllerAsyncRPCHandlerInitializer, - ExecutionStatsUpdate -} -import edu.uci.ics.amber.engine.architecture.rpc.controlcommands.{ - AsyncRPCContext, - EmptyRequest, - QueryStatisticsRequest -} +import edu.uci.ics.amber.engine.architecture.controller.{ControllerAsyncRPCHandlerInitializer, ExecutionStatsUpdate} +import edu.uci.ics.amber.engine.architecture.rpc.controlcommands.{AsyncRPCContext, EmptyRequest, QueryStatisticsRequest} import edu.uci.ics.amber.engine.architecture.rpc.controlreturns.EmptyReturn +import edu.uci.ics.amber.engine.architecture.worker.statistics.WorkerState +import edu.uci.ics.amber.engine.architecture.worker.tableprofile.TableProfile import edu.uci.ics.amber.util.VirtualIdentityUtils -/** Get statistics from all the workers - * - * possible sender: controller(by statusUpdateAskHandle) - */ trait QueryWorkerStatisticsHandler { this: ControllerAsyncRPCHandlerInitializer => override def controllerInitiateQueryStatistics( - msg: QueryStatisticsRequest, - ctx: AsyncRPCContext - ): Future[EmptyReturn] = { + msg: QueryStatisticsRequest, + ctx: AsyncRPCContext + ): Future[EmptyReturn] = { - // decide whom to contact + // 1. decide whom to contact val workers: Iterable[ActorVirtualIdentity] = if (msg.filterByWorkers.nonEmpty) msg.filterByWorkers else @@ -52,24 +43,34 @@ trait QueryWorkerStatisticsHandler { this: ControllerAsyncRPCHandlerInitializer .flatMap(_.getAllOperatorExecutions.map(_._2)) .flatMap(_.getWorkerIds) - // kick off both RPCs for every worker in parallel + // 2. fire queries in parallel val requests: Seq[Future[Unit]] = workers.map { wid => val exec = cp.workflowExecution .getLatestOperatorExecution(VirtualIdentityUtils.getPhysicalOpId(wid)) .getWorkerExecution(wid) - val statF = workerInterface.queryStatistics(EmptyRequest(), wid) - val profF = workerInterface.queryTableProfile(EmptyRequest(), wid) + // query metrics first + workerInterface.queryStatistics(EmptyRequest(), wid).flatMap { stat => + // update state & basic stats immediately + exec.setState(stat.metrics.workerState) + exec.setStats(stat.metrics.workerStatistics) - statF.join(profF).map { - case (stat, prof) => - exec.setState(stat.metrics.workerState) - exec.setStats(stat.metrics.workerStatistics) - exec.setTableProfile(prof.tableProfiles) + if (stat.metrics.workerState == WorkerState.COMPLETED) { + // worker finished – fetch its profile too + workerInterface + .queryTableProfile(EmptyRequest(), wid) + .map { prof => + exec.setTableProfile(prof.tableProfiles) + } + } else { + // not completed – record an empty profile + exec.setTableProfile(new TableProfile(None, Seq.empty)) + Future.Unit + } } }.toSeq - // after everyone replied, push aggregated view to the UI + // 3. when all workers replied, push aggregated view to UI Future.collect(requests).map { _ => sendToClient( ExecutionStatsUpdate( @@ -81,4 +82,4 @@ trait QueryWorkerStatisticsHandler { this: ControllerAsyncRPCHandlerInitializer EmptyReturn() } } -} +} \ No newline at end of file From 7d26d6f97367a96f975601977f9251bda81433fe Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 21 May 2025 23:41:30 -0700 Subject: [PATCH 092/104] rename operator file names --- .../QueryWorkerStatisticsHandler.scala | 19 +++++++++++++------ .../uci/ics/amber/operator/LogicalOp.scala | 4 ++-- ...scala => IntelligentCSVReaderOpDesc.scala} | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) rename core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/source/scan/csv/{PythonBasedCSVReaderOpDesc.scala => IntelligentCSVReaderOpDesc.scala} (99%) diff --git a/core/amber/src/main/scala/edu/uci/ics/amber/engine/architecture/controller/promisehandlers/QueryWorkerStatisticsHandler.scala b/core/amber/src/main/scala/edu/uci/ics/amber/engine/architecture/controller/promisehandlers/QueryWorkerStatisticsHandler.scala index 6cd603f8d9f..97867066f67 100644 --- a/core/amber/src/main/scala/edu/uci/ics/amber/engine/architecture/controller/promisehandlers/QueryWorkerStatisticsHandler.scala +++ b/core/amber/src/main/scala/edu/uci/ics/amber/engine/architecture/controller/promisehandlers/QueryWorkerStatisticsHandler.scala @@ -21,8 +21,15 @@ package edu.uci.ics.amber.engine.architecture.controller.promisehandlers import com.twitter.util.{Future, Promise} import edu.uci.ics.amber.core.virtualidentity.ActorVirtualIdentity -import edu.uci.ics.amber.engine.architecture.controller.{ControllerAsyncRPCHandlerInitializer, ExecutionStatsUpdate} -import edu.uci.ics.amber.engine.architecture.rpc.controlcommands.{AsyncRPCContext, EmptyRequest, QueryStatisticsRequest} +import edu.uci.ics.amber.engine.architecture.controller.{ + ControllerAsyncRPCHandlerInitializer, + ExecutionStatsUpdate +} +import edu.uci.ics.amber.engine.architecture.rpc.controlcommands.{ + AsyncRPCContext, + EmptyRequest, + QueryStatisticsRequest +} import edu.uci.ics.amber.engine.architecture.rpc.controlreturns.EmptyReturn import edu.uci.ics.amber.engine.architecture.worker.statistics.WorkerState import edu.uci.ics.amber.engine.architecture.worker.tableprofile.TableProfile @@ -31,9 +38,9 @@ import edu.uci.ics.amber.util.VirtualIdentityUtils trait QueryWorkerStatisticsHandler { this: ControllerAsyncRPCHandlerInitializer => override def controllerInitiateQueryStatistics( - msg: QueryStatisticsRequest, - ctx: AsyncRPCContext - ): Future[EmptyReturn] = { + msg: QueryStatisticsRequest, + ctx: AsyncRPCContext + ): Future[EmptyReturn] = { // 1. decide whom to contact val workers: Iterable[ActorVirtualIdentity] = @@ -82,4 +89,4 @@ trait QueryWorkerStatisticsHandler { this: ControllerAsyncRPCHandlerInitializer EmptyReturn() } } -} \ No newline at end of file +} diff --git a/core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/LogicalOp.scala b/core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/LogicalOp.scala index e38669d703b..11ede3a8e79 100644 --- a/core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/LogicalOp.scala +++ b/core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/LogicalOp.scala @@ -73,7 +73,7 @@ import edu.uci.ics.amber.operator.source.apis.twitter.v2.{ import edu.uci.ics.amber.operator.source.fetcher.URLFetcherOpDesc import edu.uci.ics.amber.operator.source.scan.FileScanSourceOpDesc import edu.uci.ics.amber.operator.source.scan.arrow.ArrowSourceOpDesc -import edu.uci.ics.amber.operator.source.scan.csv.{CSVScanSourceOpDesc, PythonBasedCSVReaderOpDesc} +import edu.uci.ics.amber.operator.source.scan.csv.{CSVScanSourceOpDesc, IntelligentCSVReaderOpDesc} import edu.uci.ics.amber.operator.source.scan.csvOld.CSVOldScanSourceOpDesc import edu.uci.ics.amber.operator.source.scan.json.JSONLScanSourceOpDesc import edu.uci.ics.amber.operator.source.scan.text.TextInputSourceOpDesc @@ -144,7 +144,7 @@ trait StateTransferFunc new Type(value = classOf[IfOpDesc], name = "If"), new Type(value = classOf[SankeyDiagramOpDesc], name = "SankeyDiagram"), new Type(value = classOf[IcicleChartOpDesc], name = "IcicleChart"), - new Type(value = classOf[PythonBasedCSVReaderOpDesc], name = "IntelligentCSVReader"), + new Type(value = classOf[IntelligentCSVReaderOpDesc], name = "IntelligentCSVReader"), new Type(value = classOf[CSVScanSourceOpDesc], name = "CSVFileScan"), // disabled the ParallelCSVScanSourceOpDesc so that it does not confuse user. it can be re-enabled when doing experiments. // new Type(value = classOf[ParallelCSVScanSourceOpDesc], name = "ParallelCSVFileScan"), diff --git a/core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/source/scan/csv/PythonBasedCSVReaderOpDesc.scala b/core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/source/scan/csv/IntelligentCSVReaderOpDesc.scala similarity index 99% rename from core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/source/scan/csv/PythonBasedCSVReaderOpDesc.scala rename to core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/source/scan/csv/IntelligentCSVReaderOpDesc.scala index 5cfef9b140c..de82f5f44e4 100644 --- a/core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/source/scan/csv/PythonBasedCSVReaderOpDesc.scala +++ b/core/workflow-operator/src/main/scala/edu/uci/ics/amber/operator/source/scan/csv/IntelligentCSVReaderOpDesc.scala @@ -34,7 +34,7 @@ import play.api.libs.json.{JsObject, JsString, Json} import java.io.InputStreamReader import java.net.URI -class PythonBasedCSVReaderOpDesc extends ScanSourceOpDesc { +class IntelligentCSVReaderOpDesc extends ScanSourceOpDesc { @JsonProperty(defaultValue = ",") @JsonSchemaTitle("Delimiter") @JsonPropertyDescription("delimiter to separate each line into fields") From dae6d46ae932e97277b38f94c16fff760ebae922 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Wed, 21 May 2025 23:41:43 -0700 Subject: [PATCH 093/104] fix date type convertion issue --- .../pytexera/storage/dataset_file_document.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/amber/src/main/python/pytexera/storage/dataset_file_document.py b/core/amber/src/main/python/pytexera/storage/dataset_file_document.py index 2dd1698fce3..38fd5c77772 100644 --- a/core/amber/src/main/python/pytexera/storage/dataset_file_document.py +++ b/core/amber/src/main/python/pytexera/storage/dataset_file_document.py @@ -167,9 +167,16 @@ def read_as_table(self, schema: dict[str, str] | None = None, **pandas_kwargs) - elif amber_type == "STRING": df[col] = df[col].astype(str) elif amber_type == "TIMESTAMP": - # keep as ISO string for Amber compatibility - df[col] = pd.to_datetime(df[col], errors="coerce").astype(str) + # → real datetimes expected by Amber + # - `errors="coerce"` turns bad values into NaT + # - `.dt.tz_localize(None)` drops tz so JVM parsing is trivial + # - `.dt.to_pydatetime()` converts the ndarray to plain Python + df[col] = ( + pd.to_datetime(df[col], errors="coerce") + .dt.tz_localize(None) + .dt.to_pydatetime() + ) else: # BINARY, ANY, or unknown - df[col] = df[col].astype(str) + raise Exception(f"Unsupported type: {amber_type}") return Table(df) From 7ce41a1a62d6c92b353a26579d2d7542837e9d88 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 22 May 2025 12:18:58 -0700 Subject: [PATCH 094/104] make the sanitization on port better --- .../llm_agent/openai_agent.py | 64 +++++++++++++++++-- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/core/suggestion-service/llm_agent/openai_agent.py b/core/suggestion-service/llm_agent/openai_agent.py index 5fb43f44161..c960f103bcc 100644 --- a/core/suggestion-service/llm_agent/openai_agent.py +++ b/core/suggestion-service/llm_agent/openai_agent.py @@ -188,8 +188,41 @@ def _sanitize_suggestions( ) -> SuggestionList: """ Validate / repair suggestions according to the project rules. - Invalid suggestions are dropped. + Invalid suggestions are dropped. In addition, port IDs in + `linksToAdd` are normalised to the Texera conventions: + • sources → "output-" (default index = 0) + • targets → "input-" (default index = 0) """ + + # ---------- helper -------------------------------------------------- + def _normalise_port_id(raw_pid: Any, direction: str) -> str: + """ + Coerce various user/LLM styles into the canonical form. + + direction ∈ {"input", "output"} + """ + if raw_pid is None: + return f"{direction}-0" + + pid = str(raw_pid).strip().lower() + + # already canonical + if pid.startswith(f"{direction}-"): + return pid + + # just a bare integer (e.g. "0") + if pid.isdigit(): + return f"{direction}-{pid}" + + # naked "input"/"output" → assume 0 + if pid in {"input", "output"}: + return f"{direction}-0" + + # anything else – fall back to 0 + return f"{direction}-0" + + # -------------------------------------------------------------------- + try: workflow_dict = ( workflow_intp.get_base_workflow_interpretation().model_dump() @@ -219,22 +252,35 @@ def _sanitize_suggestions( break # invalid operator type -> drop suggestion # if updating an existing op, ID must exist in workflow - if op["operatorID"] in existing_ops: - pass # allowed – treated as in-place update + # (otherwise it's a brand-new op, which is fine) else: # 2) linksToAdd ------------------------------------------------------ valid_link_add = True for link in ch["linksToAdd"]: + # ---------- NEW: fix / fill port IDs -------------------------- + # NB: we *mutate* the dict in-place so later checks see the + # corrected value. + src_port = link.setdefault("source", {}) + tgt_port = link.setdefault("target", {}) + src_port["portID"] = _normalise_port_id( + src_port.get("portID"), direction="output" + ) + tgt_port["portID"] = _normalise_port_id( + tgt_port.get("portID"), direction="input" + ) + # -------------------------------------------------------------- + # ensure linkID exists (generate if missing) if not link.get("linkID"): link["linkID"] = f"link-{uuid.uuid4()}" + # ensure endpoints refer to real or newly-added ops - src_ok = link["source"]["operatorID"] in existing_ops or any( - op["operatorID"] == link["source"]["operatorID"] + src_ok = src_port["operatorID"] in existing_ops or any( + op["operatorID"] == src_port["operatorID"] for op in ch["operatorsToAdd"] ) - tgt_ok = link["target"]["operatorID"] in existing_ops or any( - op["operatorID"] == link["target"]["operatorID"] + tgt_ok = tgt_port["operatorID"] in existing_ops or any( + op["operatorID"] == tgt_port["operatorID"] for op in ch["operatorsToAdd"] ) if not (src_ok and tgt_ok): @@ -261,6 +307,10 @@ def _sanitize_suggestions( print(f"Error sanitizing suggestions: {e}") return SuggestionList(suggestions=[]) + except Exception as e: + print(f"Error sanitizing suggestions: {e}") + return SuggestionList(suggestions=[]) + def _call_function(self, name, args): if name == "extract_json_schemas": return extract_json_schemas(**args) From f750148d691bbc7ee068eaa4d47cf59ee194e606 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 22 May 2025 12:30:01 -0700 Subject: [PATCH 095/104] temproraily disable the suggestion panel --- .../result-panel/result-panel.component.ts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/core/gui/src/app/workspace/component/result-panel/result-panel.component.ts b/core/gui/src/app/workspace/component/result-panel/result-panel.component.ts index bc5a9760b4c..ee60a158044 100644 --- a/core/gui/src/app/workspace/component/result-panel/result-panel.component.ts +++ b/core/gui/src/app/workspace/component/result-panel/result-panel.component.ts @@ -101,7 +101,7 @@ export class ResultPanelComponent implements OnInit, OnDestroy { ngOnInit(): void { // Add suggestions tab to the result panel first - this.displaySuggestions(); + // this.displaySuggestions(); const style = localStorage.getItem("result-panel-style"); if (style) document.getElementById("result-container")!.style.cssText = style; @@ -230,11 +230,11 @@ export class ResultPanelComponent implements OnInit, OnDestroy { this.operatorTitle = ""; } } - - // Make sure suggestions tab is always available - if (!this.frameComponentConfigs.has("Suggestions")) { - this.displaySuggestions(); - } + // + // // Make sure suggestions tab is always available + // if (!this.frameComponentConfigs.has("Suggestions")) { + // this.displaySuggestions(); + // } if ( this.executeWorkflowService.getExecutionState().state === ExecutionState.Failed || @@ -365,10 +365,10 @@ export class ResultPanelComponent implements OnInit, OnDestroy { this.height = DEFAULT_HEIGHT; this.width = DEFAULT_WIDTH; - // Ensure suggestions tab is available when panel is opened - if (!this.frameComponentConfigs.has("Suggestions")) { - this.displaySuggestions(); - } + // // Ensure suggestions tab is available when panel is opened + // if (!this.frameComponentConfigs.has("Suggestions")) { + // this.displaySuggestions(); + // } } closePanel() { @@ -424,13 +424,13 @@ export class ResultPanelComponent implements OnInit, OnDestroy { this.returnPosition = { x: this.returnPosition.x, y: this.returnPosition.y + prevHeight - newHeight }; } - /** - * Displays the workflow suggestions in the result panel - */ - displaySuggestions() { - this.frameComponentConfigs.set("Suggestions", { - component: SuggestionFrameComponent, - componentInputs: {}, - }); - } + // /** + // * Displays the workflow suggestions in the result panel + // */ + // displaySuggestions() { + // this.frameComponentConfigs.set("Suggestions", { + // component: SuggestionFrameComponent, + // componentInputs: {}, + // }); + // } } From c5ccf019a27e9938d3e267ef17922fd0491837dc Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 22 May 2025 13:11:55 -0700 Subject: [PATCH 096/104] fix some issues of table profiling and data loading --- .../managers/table_profile_manager.py | 9 ++-- .../pytexera/storage/dataset_file_document.py | 48 +++++++++---------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/core/amber/src/main/python/core/architecture/managers/table_profile_manager.py b/core/amber/src/main/python/core/architecture/managers/table_profile_manager.py index f95d0f53ecc..715ef48dfce 100644 --- a/core/amber/src/main/python/core/architecture/managers/table_profile_manager.py +++ b/core/amber/src/main/python/core/architecture/managers/table_profile_manager.py @@ -67,9 +67,12 @@ def _dp_column_to_proto(cjs: Dict[str, Any]) -> ColumnProfile: categorical=bool(cjs.get("categorical")), order=cjs.get("order", "") ) - - if "samples" in cjs: - sample_str = cjs["samples"].strip("[]") + samples = cjs.get("samples", []) + if isinstance(samples, list): + cp.samples.extend([str(s) for s in samples[:10]]) + elif isinstance(samples, str): + # compact-format DP report returns a JSON list-as-string + sample_str = samples.strip("[]") cp.samples.extend([s.strip(" '") for s in sample_str.split(",")[:10]]) stats = cjs.get("statistics", {}) diff --git a/core/amber/src/main/python/pytexera/storage/dataset_file_document.py b/core/amber/src/main/python/pytexera/storage/dataset_file_document.py index 38fd5c77772..2bf01f58b88 100644 --- a/core/amber/src/main/python/pytexera/storage/dataset_file_document.py +++ b/core/amber/src/main/python/pytexera/storage/dataset_file_document.py @@ -126,57 +126,57 @@ def read_as_table(self, schema: dict[str, str] | None = None, **pandas_kwargs) - proper service layer. """ + # Pull the bytes from object storage # Pull the bytes from object storage file_bytes = self.read_file() # Infer file format from the extension ext = self.file_relative_path.rsplit(".", 1)[-1].lower() + # ---- 1) load the file ----------------------------------------- if ext in {"csv", "tsv", "txt"}: # default separator if caller didn't pass one - if "sep" not in pandas_kwargs: - pandas_kwargs["sep"] = "," if ext == "csv" else "\t" + pandas_kwargs.setdefault("sep", "," if ext == "csv" else "\t") + # keep blank rows if they exist; treat empty cells as NA + pandas_kwargs.setdefault("skip_blank_lines", False) + pandas_kwargs.setdefault("keep_default_na", True) df = pd.read_csv(file_bytes, **pandas_kwargs) + elif ext in {"json", "ndjson"}: df = pd.read_json(file_bytes, lines=(ext == "ndjson"), **pandas_kwargs) - elif ext in {"parquet"}: + elif ext == "parquet": df = pd.read_parquet(file_bytes, **pandas_kwargs) else: raise ValueError(f"Unsupported file type: .{ext}") - # # --- tidy up numeric / datetime heuristics ----------------- - # for col in df.columns: - # if is_float_dtype(df[col]): - # mask = df[col].notna() - # if mask.any() and (df.loc[mask, col] % 1).abs().lt(1e-12).all(): - # df[col] = df[col].astype("Int64") - # elif is_datetime64_any_dtype(df[col]): - # df[col] = df[col].astype(str) - - # --- hard-cast according to Amber schema -------------------- + # ---- 2) hard-cast columns according to Amber schema ----------- if schema: for col, amber_type in schema.items(): if col not in df.columns: continue - if amber_type in ("INTEGER", "LONG"): - df[col] = df[col].astype("Int64") + + s = df[col] # shorthand + + if amber_type in {"INTEGER", "LONG"}: + df[col] = pd.to_numeric(s, errors="coerce").astype("Int64") + elif amber_type == "DOUBLE": - df[col] = df[col].astype("float64") + df[col] = pd.to_numeric(s, errors="coerce").astype("float64") + elif amber_type == "BOOLEAN": - df[col] = df[col].astype("boolean") + df[col] = s.astype("boolean") # nullable boolean + elif amber_type == "STRING": - df[col] = df[col].astype(str) + # nullable *string* dtype – keeps pd.NA for empty cells + df[col] = s.astype(pd.StringDtype()) + elif amber_type == "TIMESTAMP": - # → real datetimes expected by Amber - # - `errors="coerce"` turns bad values into NaT - # - `.dt.tz_localize(None)` drops tz so JVM parsing is trivial - # - `.dt.to_pydatetime()` converts the ndarray to plain Python df[col] = ( - pd.to_datetime(df[col], errors="coerce") + pd.to_datetime(s, errors="coerce") .dt.tz_localize(None) .dt.to_pydatetime() ) - else: # BINARY, ANY, or unknown + else: # BINARY, ANY, unknown raise Exception(f"Unsupported type: {amber_type}") return Table(df) From ba264a230cafe9dea630019018bfb4e1e58dbd4b Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 22 May 2025 13:37:50 -0700 Subject: [PATCH 097/104] make the copilot on udf work --- .../operator-property-edit-frame.component.ts | 58 +++++++++++++++---- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts b/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts index 4d44baa436c..7c2b23f449a 100644 --- a/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts +++ b/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts @@ -29,7 +29,7 @@ import { } from "@angular/core"; import { ExecuteWorkflowService } from "../../../service/execute-workflow/execute-workflow.service"; import { WorkflowStatusService } from "../../../service/workflow-status/workflow-status.service"; -import { Subject } from "rxjs"; +import {finalize, Subject} from "rxjs"; import { AbstractControl, FormGroup } from "@angular/forms"; import { FormlyFieldConfig, FormlyFormOptions } from "@ngx-formly/core"; import Ajv from "ajv"; @@ -65,9 +65,11 @@ import Quill from "quill"; import QuillCursors from "quill-cursors"; import * as Y from "yjs"; import { OperatorSchema } from "src/app/workspace/types/operator-schema.interface"; -import { AttributeType, PortSchema } from "../../../types/workflow-compiling.interface"; +import { AttributeType, PortSchema, SchemaAttribute } from "../../../types/workflow-compiling.interface"; import { GuiConfigService } from "../../../../common/service/gui-config.service"; import { WorkflowSuggestionService } from "../../../service/workflow-suggestion/workflow-suggestion.service"; +import {ColumnProfileService, SelectedColumnInfo} from "../../../service/column-profile/column-profile.service"; +import {SuggestionActionService} from "../../../service/suggestion-action/suggestion-action.service"; Quill.register("modules/cursors", QuillCursors); @@ -158,8 +160,11 @@ export class OperatorPropertyEditFrameComponent implements OnInit, OnChanges, On /** Whether the Copilot intention box is visible */ copilotEnabled: boolean = false; + public selectedColumnInfo: SelectedColumnInfo | null = null; + constructor( private formlyJsonschema: FormlyJsonschema, + private columnProfileService: ColumnProfileService, private workflowActionService: WorkflowActionService, public executeWorkflowService: ExecuteWorkflowService, private dynamicSchemaService: DynamicSchemaService, @@ -168,8 +173,9 @@ export class OperatorPropertyEditFrameComponent implements OnInit, OnChanges, On private changeDetectorRef: ChangeDetectorRef, private workflowVersionService: WorkflowVersionService, private workflowStatusSerivce: WorkflowStatusService, + private workflowSuggestionService: WorkflowSuggestionService, + private suggestionActionService: SuggestionActionService, private config: GuiConfigService, - private workflowSuggestionService: WorkflowSuggestionService ) {} ngOnChanges(changes: SimpleChanges): void { @@ -207,6 +213,13 @@ export class OperatorPropertyEditFrameComponent implements OnInit, OnChanges, On this.currentOperatorStatus = update[this.currentOperatorId]; } }); + + this.columnProfileService + .getSelectedColumnStream() + .pipe(untilDestroyed(this)) + .subscribe(selectedInfo => { + this.selectedColumnInfo = selectedInfo; + }); } async ngOnDestroy() { @@ -808,13 +821,22 @@ export class OperatorPropertyEditFrameComponent implements OnInit, OnChanges, On return; } - // There might be no operators in the graph or no highlighted operator; in such cases we - // simply do nothing. const operators = this.workflowActionService.getTexeraGraph().getAllOperators(); if (operators.length === 0) { return; } + const highlighted = this.workflowActionService.getJointGraphWrapper().getCurrentHighlightedOperatorIDs(); + if (highlighted.length === 0) { + this.notificationService.warning("No operator selected."); + return; + } + + const operatorIDToSchema: { [key: string]: ReadonlyArray } = {}; + if (this.selectedColumnInfo) { + operatorIDToSchema[this.selectedColumnInfo.operatorId] = this.selectedColumnInfo.schema; + } + this.notificationService.info("Asking copilot to fill out the properties..."); this.loadingSuggestions = true; @@ -824,15 +846,27 @@ export class OperatorPropertyEditFrameComponent implements OnInit, OnChanges, On this.workflowCompilingService.getWorkflowCompilationStateInfo(), this.executeWorkflowService.getExecutionState(), this.intentionText.trim(), - this.workflowActionService.getJointGraphWrapper().getCurrentHighlightedOperatorIDs(), - {} + highlighted, + operatorIDToSchema ) - .pipe(untilDestroyed(this)) - .subscribe(() => { - // The SuggestionFrameComponent will be updated via the shared service stream, we only need - // to clear the loading flag here. + .pipe(finalize(() => { this.loadingSuggestions = false; - this.notificationService.success("Received suggestions from the copilot"); + this.notificationService.remove(); + })) + .subscribe({ + next: suggestionsList => { + const suggestions = suggestionsList.suggestions; + if (suggestions.length > 0) { + const suggestion = suggestions[0]; + this.notificationService.success(`Applying suggestion: "${suggestion.suggestion}"`); + this.suggestionActionService.applySuggestion(suggestion); + } else { + this.notificationService.warning("No actionable suggestion was found."); + } + }, + error: unknown => { + this.notificationService.error("Failed to get actionable suggestions."); + }, }); } } From 4298f28eee69e1695186ba238472ee620155b9de Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 22 May 2025 14:11:12 -0700 Subject: [PATCH 098/104] fix the left panel display --- .../component/left-panel/left-panel.component.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts b/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts index 103b04d4512..d88fbb9cafe 100644 --- a/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts +++ b/core/gui/src/app/workspace/component/left-panel/left-panel.component.ts @@ -182,13 +182,14 @@ export class LeftPanelComponent implements OnDestroy, OnInit, AfterViewInit { } openFrame(i: number, forceOpen: boolean = false) { - if (!i) { + if (i === 0) { this.width = 0; this.height = 65; - } else if (!this.width && !forceOpen) { - } else if (!this.width || (forceOpen && this.width < LeftPanelComponent.MIN_PANEL_WIDTH)) { - this.width = LeftPanelComponent.MIN_PANEL_WIDTH; - this.height = this.minPanelHeight; + } else { + if (this.width === 0 || (forceOpen && this.width < LeftPanelComponent.MIN_PANEL_WIDTH)) { + this.width = LeftPanelComponent.MIN_PANEL_WIDTH; + this.height = this.minPanelHeight; + } } if (i >= 0 && i < this.items.length) { From 72ec71f6d9cf4f949c1e9cb253be72aa35142008 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 22 May 2025 14:11:45 -0700 Subject: [PATCH 099/104] fix the datetime convertion --- .../amber/core/tuple/AttributeTypeUtils.scala | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/core/workflow-core/src/main/scala/edu/uci/ics/amber/core/tuple/AttributeTypeUtils.scala b/core/workflow-core/src/main/scala/edu/uci/ics/amber/core/tuple/AttributeTypeUtils.scala index 2c660a14f50..0805d82bc9b 100644 --- a/core/workflow-core/src/main/scala/edu/uci/ics/amber/core/tuple/AttributeTypeUtils.scala +++ b/core/workflow-core/src/main/scala/edu/uci/ics/amber/core/tuple/AttributeTypeUtils.scala @@ -200,11 +200,21 @@ object AttributeTypeUtils extends Serializable { def parseTimestamp(fieldValue: Any): Timestamp = { val attempt: Try[Timestamp] = Try { fieldValue match { - case str: String => new Timestamp(DateParserUtils.parseDate(str.trim).getTime) - case long: java.lang.Long => new Timestamp(long) - case timestamp: Timestamp => timestamp - case date: java.util.Date => new Timestamp(date.getTime) - // Integer, Double, Boolean, Binary are considered to be illegal here. + case str: String => + new Timestamp(DateParserUtils.parseDate(str.trim).getTime) + + case ldt: java.time.LocalDateTime => + // Converts using the JVM default zone; change to a specific ZoneId if you need UTC + Timestamp.valueOf(ldt) + + case instant: java.time.Instant => + Timestamp.from(instant) + + case long: java.lang.Long => new Timestamp(long) + case ts: Timestamp => ts + case date: java.util.Date => new Timestamp(date.getTime) + + // Unsupported kinds fall through case _ => throw new AttributeTypeException( s"Unsupported type for parsing to Timestamp: ${fieldValue.getClass.getName}" @@ -219,7 +229,6 @@ object AttributeTypeUtils extends Serializable { e ) }.get - } @throws[AttributeTypeException] From 7a98e61d20614a379efa144f4aed3ef283f2796b Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 22 May 2025 15:24:21 -0700 Subject: [PATCH 100/104] remove the table stats button --- .../result-table-frame/result-table-frame.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/gui/src/app/workspace/component/result-panel/result-table-frame/result-table-frame.component.html b/core/gui/src/app/workspace/component/result-panel/result-table-frame/result-table-frame.component.html index d101195eb39..028c77e0fa3 100644 --- a/core/gui/src/app/workspace/component/result-panel/result-table-frame/result-table-frame.component.html +++ b/core/gui/src/app/workspace/component/result-panel/result-table-frame/result-table-frame.component.html @@ -25,7 +25,7 @@

    Empty result set

    -
    -
    +
    -->
    Date: Thu, 22 May 2025 15:24:59 -0700 Subject: [PATCH 101/104] improve the function call document --- .../files/instruction_for_function_call.md | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/core/suggestion-service/files/instruction_for_function_call.md b/core/suggestion-service/files/instruction_for_function_call.md index 2223a56c55c..12a47a1909e 100644 --- a/core/suggestion-service/files/instruction_for_function_call.md +++ b/core/suggestion-service/files/instruction_for_function_call.md @@ -173,8 +173,8 @@ class ProcessTupleOperator(UDFOperatorV2): yield tuple_ ``` -Tuple API takes one input tuple from a port at a time. It returns an iterator of optional `TupleLike` instances. A `TupleLike` is any data structure that supports key-value pairs, such as `pytexera.Tuple`, `dict`, `defaultdict`, `NamedTuple`, etc. -Tuple API is useful for implementing functional operations which are applied to tuples one by one, such as map, reduce, and filter. +* Tuple API takes one input tuple from a port at a time. It returns an iterator of optional `TupleLike` instances. A `TupleLike` is any data structure that supports key-value pairs, such as `pytexera.Tuple`, `dict`, `defaultdict`, `NamedTuple`, etc. +* Tuple API is useful for implementing functional operations which are applied to tuples one by one, such as map, reduce, and filter. 2. Table API. @@ -186,9 +186,54 @@ class ProcessTableOperator(UDFTableOperator): def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]: yield table ``` -Table API consumes a `Table` at a time, which consists of all the whole table from a port. It returns an iterator of optional `TableLike` instances. A `TableLike ` is a collection of `TupleLike`, and currently, we support `pytexera.Table` and `pandas.DataFrame` as a `TableLike` instance. -Table API is useful for implementing blocking operations that will consume the whole column to do operations. +* Table API consumes a `Table` at a time, which consists of all the whole table from a port. It returns an iterator of optional `TableLike` instances. A `TableLike ` is a collection of `TupleLike`, and currently, we support `pytexera.Table` and `pandas.DataFrame` as a `TableLike` instance. +* Table API is useful for implementing blocking operations that will consume the whole column to do operations. + +* Here are some examples of using two APIs: + * Example 1: use Tuple API to normalize the `state` column to standard uppercase US state code: +```python +from pytexera import * +class ProcessTupleOperator(UDFOperatorV2): + """ + Standardise free-form state names/abbreviations to two-letter codes. + Unknown values are upper-cased unchanged. + """ + def process_tuple(self, tuple_: Tuple, port: int) -> Iterator[Optional[TupleLike]]: + _STATE_MAP = { + "california": "CA", "ca": "CA", + "new york": "NY", "ny": "NY", + "texas": "TX", "tx": "TX", + } + + raw = str(tuple_["BILLINGCOMPANYCODE"]).strip().lower() + tuple_["BILLINGCOMPANYCODE"] = _STATE_MAP.get(raw, raw.upper()) + yield tuple_ +``` + * Example 2: use Table API to convert the `CREATIONTIME` column into datetime, remove all the rows that have null timestamp and do a filtering using the cutoff date +```python +from pytexera import * +import pandas as pd + +class ProcessTableOperator(UDFTableOperator): + _CUTOFF = pd.Timestamp("2020-01-01", tz="UTC") + + def process_table(self, table: Table, port: int): + df = table + + # 1. Parse date strings; bad parses become NaT + df["CREATIONTIME"] = pd.to_datetime(df["CREATIONTIME"], errors="coerce", utc=True) + + # 2. Drop rows where conversion failed + df = df.dropna(subset=["CREATIONTIME"]) + + # 3. Time-window filter + df = df[df["CREATIONTIME"] >= self._CUTOFF].reset_index(drop=True) + + yield df +``` * When writing the udf code, you MUST NOT change the class name -* You can import pandas, numpy, sklearn and other common python packages -* You don't need to import typing for the type annotations. \ No newline at end of file +* You should import pandas, numpy, sklearn and other common python packages when you want too use them +* You don't need to import typing for the type annotations. +* Tuple you can think it as the 'Dict' type. You should only use `[]` to do tuple's field's read & write. DO NOT use methods like `tuple_.get()` or `tuple_.set()` +* Table you can think it as the `pandas.Dataframe`. \ No newline at end of file From 6a42e393446d93e6a57f956cf920a8c922e76575 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Sat, 24 May 2025 15:43:14 -0700 Subject: [PATCH 102/104] finishing up the demo frontend --- .../column-profile-frame.component.html | 143 ++++++----- .../column-profile-frame.component.scss | 237 ++++++++++-------- .../column-profile-frame.component.ts | 2 +- ...perator-property-edit-frame.component.html | 19 +- .../result-table-frame.component.html | 25 -- 5 files changed, 221 insertions(+), 205 deletions(-) diff --git a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.html b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.html index 793e221c433..3c656606f6b 100644 --- a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.html +++ b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.html @@ -59,7 +59,7 @@

    Top 10 Categorical Distribution

    -
    +

    Data Cleaning Suggestions

    - -
    - Loading data cleaning suggestions... +
    + + + Powered by Data Copilot +
    +
    -
    - No data cleaning suggestions available for this column. -
    +
    + + Loading data cleaning suggestions... +
    -
      -
    • -
      - {{ suggestion.suggestion }} - -
      -
      - -
      - - -
      + > +
      + +
      -
    • -
    -
    +
    +
  • + diff --git a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.scss b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.scss index 6d2177243c5..06636306885 100644 --- a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.scss +++ b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.scss @@ -35,6 +35,37 @@ .data-cleaning-suggestions-section { margin-top: 16px; + .suggestions-header-main-line { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + margin-bottom: 2px; + } + + .copilot-attribution-wrapper { + margin-bottom: 8px; + } + + .copilot-attribution { + display: flex; + align-items: center; + font-size: 0.75em; + color: #777; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + font-style: italic; + + i[nz-icon] { + margin-right: 5px; + font-size: 1.2em; + color: #007bff; + } + } + + h4 { + margin-bottom: 0; + } + .no-suggestions-message { padding: 10px; background-color: #f0f0f0; @@ -44,114 +75,98 @@ color: #555; font-style: italic; } + } - .suggestions-list { - // Remove background and border from the list container itself - // background-color: #fffbe6; - // border: 1px solid #ffe58f; - padding: 0; // Remove padding if items will have their own - border-radius: 4px; - list-style-type: none; - margin: 0; + // ========================= + // Suggestion List & Items + // ========================= + .suggestions-list { + padding: 0; + list-style-type: none; + margin: 0; + } - .suggestion-item { - padding: 8px 12px; // Adjusted padding for better spacing within item - margin-bottom: 6px; // Space between items - color: #333; + // Style each suggestion item regardless of parent container + .suggestion-item { + margin-bottom: 6px; + color: #333; + border-radius: 3px; + cursor: pointer; + display: flex; + flex-direction: column; + // Apply transitions for hover/active effects + transition: background-color 0.25s ease, border-color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease; + + // Default appearance + background-color: #fffbe6; // light yellow + border: 1px solid #ffe58f; + padding: 10px 14px; + + &:last-child { + margin-bottom: 0; + } + + // Hover (not expanded) + &:not(.expanded):hover { + background-color: #ffecb3; + border-color: #ffd666; + transform: translateY(-2px); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); + color: #000; + } + + // Expanded state + &.expanded { + background-color: #fff9db; + border-color: #ffe58f; + padding-bottom: 12px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + + &:hover { + background-color: #ffefc2; + } + + .suggestion-details-wrapper { + padding-top: 10px; + margin-top: 10px; + border-top: 1px dashed #dcdcdc; + } + + .suggestion-details-area { + width: 100%; + box-sizing: border-box; + min-height: 160px; + font-family: monospace; + font-size: 0.85em; + padding: 8px; + border: 1px solid #ccc; border-radius: 3px; - cursor: pointer; - transition: - background-color 0.2s ease-in-out, - color 0.2s ease-in-out, - border-color 0.2s ease-in-out; // Added border-color transition + resize: vertical; + background-color: #fdfdfd; + color: #333; + margin-bottom: 10px; + } + + .suggestion-actions { display: flex; - flex-direction: column; - - // Default state - yellow background for each item - background-color: #fffbe6; - border: 1px solid #ffe58f; - - &:last-child { - margin-bottom: 0; - } - - &:hover { - background-color: #ffe58f; - border-color: #ffd700; // Darker border on hover - color: #000; - } - - .suggestion-type { - // This style was outside .suggestion-item before, moved in - font-size: 0.85em; - color: #777; - margin-left: 4px; - } - - .suggestion-summary { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - } - - .expand-icon { - margin-left: 8px; - font-size: 0.8em; - color: #555; - } - - // Styles when expanded - &.expanded { - background-color: #fff9e0; // Slightly lighter or different yellow for expanded - border-color: #ffecb3; // Lighter border for expanded - - &:hover { - background-color: #fff0b3; // Hover for expanded item - border-color: #ffe58f; - } - } - - .suggestion-details-wrapper { - padding-top: 10px; // Increased padding - margin-top: 10px; // Increased margin - border-top: 1px dashed #dcdcdc; // Slightly darker dashed line - } - - .suggestion-details-area { - width: 100%; - min-height: 80px; // Increased min-height for the textarea - // Consider adding max-height or using 'rows' attribute if needed - // max-height: 150px; - // overflow-y: auto; // if max-height is set - font-family: monospace; - font-size: 0.85em; - padding: 8px; // Increased padding - border: 1px solid #ccc; - border-radius: 3px; - resize: vertical; - background-color: #fdfdfd; // Slightly off-white background for textarea - color: #333; - margin-bottom: 10px; // Increased margin - } - - .suggestion-actions { - display: flex; - justify-content: flex-end; - gap: 8px; - - .action-btn { - &.accept-btn { - // Default primary button style is usually fine - } - - &.reject-btn { - // Default danger button style is usually fine - } - } - } + justify-content: flex-end; + gap: 8px; } } + + // Inner elements common to both collapsed & expanded states + .suggestion-summary { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + + .expand-icon { + margin-left: 8px; + font-size: 0.8em; + color: #555; + } } } @@ -181,3 +196,21 @@ // font-size: 9px !important; // } // } + +.loading-suggestions-message { + padding: 10px; + text-align: center; + background-color: #fffbe6; // light yellow background + border: 1px solid #ffe58f; // yellow border to match suggestion items + color: #ad8b00; // dark yellow/brown text for contrast + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + font-weight: 500; + + .loading-icon { + font-size: 16px; + } +} diff --git a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts index 168f00767ee..4f10d636e6a 100644 --- a/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts +++ b/core/gui/src/app/workspace/component/left-panel/column-profile-frame/column-profile-frame.component.ts @@ -137,7 +137,7 @@ export class ColumnProfileFrameComponent implements OnInit { } else { this.barChartData = []; } - this.fetchOrGetCachedDataCleaningSuggestions(this.columnProfile); + // this.fetchOrGetCachedDataCleaningSuggestions(this.columnProfile); } private resetDisplayData(): void { diff --git a/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.html b/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.html index 31dfcee4690..67f50ce811d 100644 --- a/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.html +++ b/core/gui/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.html @@ -94,17 +94,8 @@ (focusout)="disconnectQuillFromText()" (keyup.enter)="disconnectQuillFromText()"> - -
    - - Use Copilot -
    - +