diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/DotPlot/DotPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/DotPlot/DotPlotOpDesc.scala index 7ca95928441..c6da62bb8d4 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/DotPlot/DotPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/DotPlot/DotPlotOpDesc.scala @@ -21,22 +21,20 @@ package org.apache.texera.amber.operator.visualization.DotPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import javax.validation.constraints.NotNull -class DotPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class DotPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "Count Attribute", required = true) @JsonSchemaTitle("Count Attribute") @@ -45,14 +43,6 @@ class DotPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { @NotNull(message = "Count Attribute column cannot be empty") var countAttribute: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Dot Plot", @@ -104,10 +94,6 @@ class DotPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { finalCode.encode } - // Output is an HTML chart, not a tabular DataFrame. - // The translator skips it in the leaf-DataFrame print block. - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val countLit = pyStringLiteral(countAttribute) // The error page is written to the same file a plotted chart lands in, so a diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/IcicleChart/IcicleChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/IcicleChart/IcicleChartOpDesc.scala index faeafba2ab8..189111204fc 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/IcicleChart/IcicleChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/IcicleChart/IcicleChartOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.IcicleChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.operator.visualization.hierarchychart.HierarchySection @@ -47,7 +45,7 @@ import javax.validation.constraints.{NotEmpty, NotNull} } } """) -class IcicleChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class IcicleChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(required = true) @JsonSchemaTitle("Hierarchy Path") @JsonPropertyDescription( @@ -63,14 +61,6 @@ class IcicleChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo @NotNull(message = "Value Column cannot be empty") var value: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Icicle Chart", @@ -136,10 +126,6 @@ class IcicleChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo finalCode.encode } - // Output is an HTML chart, not a tabular DataFrame. - // The translator skips it in the leaf-DataFrame print block. - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val attributes = hierarchy.map(section => pyStringLiteral(section.attributeName)).mkString(", ") val valueLit = pyStringLiteral(value) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ImageViz/ImageVisualizerOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ImageViz/ImageVisualizerOpDesc.scala index dfb7c2c1d51..ea14fd547f9 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ImageViz/ImageVisualizerOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ImageViz/ImageVisualizerOpDesc.scala @@ -28,13 +28,13 @@ import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.{PythonOperatorDescriptor, StandaloneCodeGenerator} +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import javax.validation.constraints.NotNull -class ImageVisualizerOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenerator { +class ImageVisualizerOpDesc extends PythonVisualizationOpDesc { @JsonProperty(required = true) @JsonSchemaTitle("image content column") @@ -43,14 +43,6 @@ class ImageVisualizerOpDesc extends PythonOperatorDescriptor with StandaloneCode @NotNull(message = "image content column cannot be empty") var binaryContent: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Image Visualizer", @@ -101,10 +93,6 @@ class ImageVisualizerOpDesc extends PythonOperatorDescriptor with StandaloneCode finalCode.encode } - // Output is an HTML visualization, not a tabular DataFrame. - // The translator skips it in the leaf-DataFrame print block. - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = generateStandaloneCode(Map.empty) /** The executor hands `b64encode` whatever the column holds, so a column of diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/PythonVisualizationOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/PythonVisualizationOpDesc.scala new file mode 100644 index 00000000000..1a02de4bf48 --- /dev/null +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/PythonVisualizationOpDesc.scala @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.texera.amber.operator.visualization + +import org.apache.texera.amber.core.tuple.{AttributeType, Schema} +import org.apache.texera.amber.core.workflow.PortIdentity +import org.apache.texera.amber.operator.{PythonOperatorDescriptor, StandaloneCodeGenerator} + +/** + * A chart drawn by Python code. + * + * Its one output port carries a single `html-content` STRING column, whatever + * the input schemas are: that column is where the frontend finds the rendered + * chart. Its exported script writes the chart to a file rather than producing + * a table, so the translator does not print it as a leaf DataFrame. Both + * members are final, so a chart cannot opt out of either. + */ +trait PythonVisualizationOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenerator { + + final override def getOutputSchemas( + inputSchemas: Map[PortIdentity, Schema] + ): Map[PortIdentity, Schema] = + Map(operatorInfo.outputPorts.head.id -> Schema().add("html-content", AttributeType.STRING)) + + final override def producesDataFrame(): Boolean = false +} diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ScatterMatrixChart/ScatterMatrixChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ScatterMatrixChart/ScatterMatrixChartOpDesc.scala index 65ae629b956..592a2bf50a9 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ScatterMatrixChart/ScatterMatrixChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ScatterMatrixChart/ScatterMatrixChartOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.ScatterMatrixChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{ AutofillAttributeName, AutofillAttributeNameList @@ -50,7 +48,7 @@ import javax.validation.constraints.{NotEmpty, NotNull} } } """) -class ScatterMatrixChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class ScatterMatrixChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "Selected Attributes", required = true) @JsonSchemaTitle("Selected Attributes") @@ -66,14 +64,6 @@ class ScatterMatrixChartOpDesc extends PythonOperatorDescriptor with PlotlyStand @NotNull(message = "Color Column cannot be empty") var color: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Scatter Matrix Chart", @@ -118,10 +108,6 @@ class ScatterMatrixChartOpDesc extends PythonOperatorDescriptor with PlotlyStand finalcode.encode } - // Output is an HTML visualization, not a tabular DataFrame. - // The translator skips it in the leaf-DataFrame print block. - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val dimensions = selectedAttributes.map(pyStringLiteral).mkString(", ") // No empty-input guard, matching generatePythonCode: it goes straight to diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/barChart/BarChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/barChart/BarChartOpDesc.scala index f7013513210..2092c75a046 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/barChart/BarChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/barChart/BarChartOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.barChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -46,7 +44,7 @@ import javax.validation.constraints.NotNull } } """) -class BarChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class BarChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "value", required = true) @JsonSchemaTitle("Value Column") @@ -79,14 +77,6 @@ class BarChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode @AutofillAttributeName var pattern: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Bar Chart", @@ -159,10 +149,6 @@ class BarChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode finalCode.encode } - // Output is an HTML chart, not a tabular DataFrame. - // The translator skips it in the leaf-DataFrame print block. - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val hasCategory = categoryColumn.nonEmpty && categoryColumn != "No Selection" val colorArg = if (hasCategory) pyStringLiteral(categoryColumn) else "None" diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/boxViolinPlot/BoxViolinPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/boxViolinPlot/BoxViolinPlotOpDesc.scala index 1b1200b0e8a..b596b015b23 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/boxViolinPlot/BoxViolinPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/boxViolinPlot/BoxViolinPlotOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.boxViolinPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription, JsonPropertyOrder} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -46,7 +44,7 @@ import javax.validation.constraints.NotNull } } """) -class BoxViolinPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class BoxViolinPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "value", required = true) @JsonSchemaTitle("Value Column") @@ -74,14 +72,6 @@ class BoxViolinPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandalone ) var violinPlot: Boolean = _ - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Box/Violin Plot", @@ -156,10 +146,6 @@ class BoxViolinPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandalone finalCode.encode } - // Output is a Plotly visualization, not a tabular DataFrame. - // The translator skips it in the leaf-DataFrame print block. - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val horizontal = if (horizontalOrientation) "True" else "False" val violin = if (violinPlot) "True" else "False" diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/bubbleChart/BubbleChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/bubbleChart/BubbleChartOpDesc.scala index be05f4dfec9..d77111365a6 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/bubbleChart/BubbleChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/bubbleChart/BubbleChartOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.bubbleChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -53,7 +51,7 @@ import javax.validation.constraints.NotNull } } """) -class BubbleChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class BubbleChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "xValue", required = true) @JsonSchemaTitle("X-Column") @@ -90,14 +88,6 @@ class BubbleChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo @AutofillAttributeName var colorCategory: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Bubble Chart", @@ -162,10 +152,6 @@ class BubbleChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo finalCode.encode } - // Output is an HTML chart, not a tabular DataFrame. - // The translator skips it in the leaf-DataFrame print block. - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { // Same rule as the platform path: an unset column is "no color" even with the // toggle on, else px.scatter(color='') fails. diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/bulletChart/BulletChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/bulletChart/BulletChartOpDesc.scala index 14277cece25..51dbd3e4f4f 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/bulletChart/BulletChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/bulletChart/BulletChartOpDesc.scala @@ -22,15 +22,13 @@ package org.apache.texera.amber.operator.visualization.bulletChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.{EncodableString, PythonLiteral} -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} @@ -52,7 +50,7 @@ import scala.jdk.CollectionConverters._ } } """) -class BulletChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class BulletChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "value", required = true) @JsonSchemaTitle("Value") @@ -81,13 +79,6 @@ class BulletChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo @JsonPropertyDescription("Optional: Each step includes a start and end value e.g., 0, 100.") var steps: JList[BulletChartStepDefinition] = new util.ArrayList[BulletChartStepDefinition]() - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema().add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Bullet Chart", @@ -247,8 +238,6 @@ class BulletChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { assertRequiredFields() diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/candlestickChart/CandlestickChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/candlestickChart/CandlestickChartOpDesc.scala index 138253f8666..4ecfa34f5b0 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/candlestickChart/CandlestickChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/candlestickChart/CandlestickChartOpDesc.scala @@ -21,21 +21,19 @@ package org.apache.texera.amber.operator.visualization.candlestickChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import javax.validation.constraints.NotNull -class CandlestickChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class CandlestickChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "date", required = true) @JsonSchemaTitle("Date Column") @@ -77,14 +75,6 @@ class CandlestickChartOpDesc extends PythonOperatorDescriptor with PlotlyStandal @NotNull(message = "Closing Price Column cannot be empty") var close: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Candlestick Chart", @@ -122,8 +112,6 @@ class CandlestickChartOpDesc extends PythonOperatorDescriptor with PlotlyStandal |""".encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = s"""fig = go.Figure(data=[go.Candlestick( | x=in1df[${pyStringLiteral(date)}], diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/carpetPlot/CarpetPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/carpetPlot/CarpetPlotOpDesc.scala index 4c4b43447bd..71132042784 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/carpetPlot/CarpetPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/carpetPlot/CarpetPlotOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.carpetPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import javax.validation.constraints.NotNull @@ -45,7 +43,7 @@ import javax.validation.constraints.NotNull } } """) -class CarpetPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class CarpetPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "a", required = true) @NotNull(message = "A-axis Attribute cannot be empty") @@ -68,14 +66,6 @@ class CarpetPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod @AutofillAttributeName var y: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Carpet Plot", @@ -136,8 +126,6 @@ class CarpetPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val aLit = pyStringLiteral(a) val bLit = pyStringLiteral(b) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/choroplethMap/ChoroplethMapOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/choroplethMap/ChoroplethMapOpDesc.scala index 1dee0093f82..ec7f5b4be74 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/choroplethMap/ChoroplethMapOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/choroplethMap/ChoroplethMapOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.choroplethMap import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -48,7 +46,7 @@ import javax.validation.constraints.NotNull } } """) -class ChoroplethMapOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class ChoroplethMapOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "locations", required = true) @JsonSchemaTitle("Locations Column") @@ -69,14 +67,6 @@ class ChoroplethMapOpDesc extends PythonOperatorDescriptor with PlotlyStandalone @NotNull(message = "Color Column cannot be empty") var color: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Choropleth Map", @@ -134,8 +124,6 @@ class ChoroplethMapOpDesc extends PythonOperatorDescriptor with PlotlyStandalone finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val locationsLit = pyStringLiteral(locations) val colorLit = pyStringLiteral(color) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/continuousErrorBands/ContinuousErrorBandsOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/continuousErrorBands/ContinuousErrorBandsOpDesc.scala index cf4d8bf53b5..a664e2bca5c 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/continuousErrorBands/ContinuousErrorBandsOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/continuousErrorBands/ContinuousErrorBandsOpDesc.scala @@ -21,22 +21,20 @@ package org.apache.texera.amber.operator.visualization.continuousErrorBands import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import java.util import javax.validation.constraints.NotEmpty import scala.jdk.CollectionConverters.ListHasAsScala -class ContinuousErrorBandsOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class ContinuousErrorBandsOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "xLabel", required = false, defaultValue = "X Axis") @JsonSchemaTitle("X Label") @@ -52,14 +50,6 @@ class ContinuousErrorBandsOpDesc extends PythonOperatorDescriptor with PlotlySta @NotEmpty(message = "Bands cannot be empty") var bands: util.List[BandConfig] = new util.ArrayList[BandConfig]() - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Continuous Error Bands", @@ -158,8 +148,6 @@ class ContinuousErrorBandsOpDesc extends PythonOperatorDescriptor with PlotlySta finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val traces = bands.asScala diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/contourPlot/ContourPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/contourPlot/ContourPlotOpDesc.scala index 88adfc2f93e..a4eff0a8c47 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/contourPlot/ContourPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/contourPlot/ContourPlotOpDesc.scala @@ -22,15 +22,13 @@ package org.apache.texera.amber.operator.visualization.contourPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.{EncodableString, PythonLiteral} -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} @@ -47,7 +45,7 @@ import javax.validation.constraints.NotNull } } """) -class ContourPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class ContourPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "x", required = true) @JsonSchemaTitle("x") @@ -90,14 +88,6 @@ class ContourPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo ) var coloringMethod: ContourPlotColoringFunction = _ - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Contour Plot", @@ -162,8 +152,6 @@ class ContourPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo |""".encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = // render_error's continuation line keeps the runtime path's indentation — the // HTML is triple-quoted, so those spaces reach the browser. diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/dendrogram/DendrogramOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/dendrogram/DendrogramOpDesc.scala index c8f4d728d71..cd551cbb970 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/dendrogram/DendrogramOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/dendrogram/DendrogramOpDesc.scala @@ -22,14 +22,12 @@ package org.apache.texera.amber.operator.visualization.dendrogram import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.{EncodableString, PythonLiteral} -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.{PythonOperatorDescriptor, StandaloneCodeGenerator} +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -46,7 +44,7 @@ import javax.validation.constraints.NotNull } } """) -class DendrogramOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenerator { +class DendrogramOpDesc extends PythonVisualizationOpDesc { @JsonProperty(value = "xVal", required = true) @JsonSchemaTitle("Value X Column") @JsonPropertyDescription("The x values of points in dendrogram") @@ -76,14 +74,6 @@ class DendrogramOpDesc extends PythonOperatorDescriptor with StandaloneCodeGener @JsonDeserialize(contentAs = classOf[java.lang.Double]) var threshold: Option[Double] = None - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Dendrogram", @@ -153,8 +143,6 @@ class DendrogramOpDesc extends PythonOperatorDescriptor with StandaloneCodeGener finalcode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { // render_error's continuation line keeps the runtime path's indentation — the diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/dumbbellPlot/DumbbellPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/dumbbellPlot/DumbbellPlotOpDesc.scala index f27a4d74f91..127e01d30ca 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/dumbbellPlot/DumbbellPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/dumbbellPlot/DumbbellPlotOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.dumbbellPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -47,7 +45,7 @@ import scala.jdk.CollectionConverters.CollectionHasAsScala } } """) -class DumbbellPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class DumbbellPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "categoryColumnName", required = true) @JsonSchemaTitle("Category Column Name") @@ -90,14 +88,6 @@ class DumbbellPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneC @JsonPropertyDescription("whether to show legends in the graph") var showLegends: Boolean = false - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Dumbbell Plot", @@ -201,8 +191,6 @@ class DumbbellPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneC |""".encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { // Typed-in values and column names become escaped Python literals; the runtime // path splices them as decode expressions, which a standalone script cannot use. diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ecdfPlot/ECDFPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ecdfPlot/ECDFPlotOpDesc.scala index ff5e1328c59..cad20709df8 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ecdfPlot/ECDFPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ecdfPlot/ECDFPlotOpDesc.scala @@ -21,10 +21,8 @@ package org.apache.texera.amber.operator.visualization.ecdfPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString @@ -39,7 +37,7 @@ import javax.validation.constraints.NotNull @JsonSchemaInject( json = """{"attributeTypeRules":{"valueColumn":{"enum":["integer","long","double"]}}}""" ) -class ECDFPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class ECDFPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(required = true) @JsonSchemaTitle("Value Column") @@ -105,13 +103,6 @@ class ECDFPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode OperatorGroupConstants.VISUALIZATION_STATISTICAL_GROUP ) - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema().add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - def manipulateTable(): PythonTemplateBuilder = { assert(valueColumn.nonEmpty, "Value Column cannot be empty") val requiredCols = @@ -188,8 +179,6 @@ class ECDFPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val requiredCols = List(valueColumn, colorColumn, separateBy).filter(_.nonEmpty) val requiredColsLiteral = requiredCols.map(pyStringLiteral).mkString("[", ", ", "]") diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDesc.scala index 4c94d322f78..36c2790c5fa 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/figureFactoryTable/FigureFactoryTableOpDesc.scala @@ -21,19 +21,17 @@ package org.apache.texera.amber.operator.visualization.figureFactoryTable import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.{PythonOperatorDescriptor, StandaloneCodeGenerator} +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import javax.validation.constraints.{DecimalMin, NotEmpty} -class FigureFactoryTableOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenerator { +class FigureFactoryTableOpDesc extends PythonVisualizationOpDesc { @JsonProperty(required = false) @JsonSchemaTitle("Font Size") @@ -144,16 +142,6 @@ class FigureFactoryTableOpDesc extends PythonOperatorDescriptor with StandaloneC ) } - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val attributes = columns.map(c => pyStringLiteral(c.attributeName)).mkString(", ") s"""import plotly.figure_factory as ff diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/filledAreaPlot/FilledAreaPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/filledAreaPlot/FilledAreaPlotOpDesc.scala index e35862aa12a..541fe609812 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/filledAreaPlot/FilledAreaPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/filledAreaPlot/FilledAreaPlotOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.filledAreaPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -57,7 +55,7 @@ import javax.validation.constraints.NotNull ] } """) -class FilledAreaPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class FilledAreaPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(required = true) @JsonSchemaTitle("X-axis Attribute") @@ -105,14 +103,6 @@ class FilledAreaPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandalon @AutofillAttributeName var pattern: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Filled Area Plot", @@ -206,8 +196,6 @@ class FilledAreaPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandalon finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val xLit = pyStringLiteral(x) val yLit = pyStringLiteral(y) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/funnelPlot/FunnelPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/funnelPlot/FunnelPlotOpDesc.scala index 805afbde974..1e0c6d81cdc 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/funnelPlot/FunnelPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/funnelPlot/FunnelPlotOpDesc.scala @@ -21,21 +21,19 @@ package org.apache.texera.amber.operator.visualization.funnelPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import javax.validation.constraints.NotNull -class FunnelPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class FunnelPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(required = true) @JsonSchemaTitle("X Column") @@ -57,14 +55,6 @@ class FunnelPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod @AutofillAttributeName var color: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Funnel Plot", @@ -119,8 +109,6 @@ class FunnelPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod finalcode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val colorArg = if (color.nonEmpty) s""", color=${pyStringLiteral(color)}""" else "" val xLit = pyStringLiteral(x) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ganttChart/GanttChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ganttChart/GanttChartOpDesc.scala index 680e14509d8..1c74e8e3771 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ganttChart/GanttChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ganttChart/GanttChartOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.ganttChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -48,7 +46,7 @@ import javax.validation.constraints.NotNull } } """) -class GanttChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class GanttChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "start", required = true) @JsonSchemaTitle("Start Datetime Column") @@ -83,14 +81,6 @@ class GanttChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod @AutofillAttributeName var pattern: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Gantt Chart", @@ -150,8 +140,6 @@ class GanttChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val colorLit = pyStringLiteral(color) val startLit = pyStringLiteral(start) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/gaugeChart/GaugeChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/gaugeChart/GaugeChartOpDesc.scala index 324d7c68403..956553d662e 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/gaugeChart/GaugeChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/gaugeChart/GaugeChartOpDesc.scala @@ -21,21 +21,19 @@ package org.apache.texera.amber.operator.visualization.gaugeChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.{EncodableString, PythonLiteral} -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import javax.validation.constraints.NotNull -class GaugeChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class GaugeChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "value", required = true) @JsonSchemaTitle("Gauge Value") @JsonPropertyDescription("The primary value displayed on the gauge chart") @@ -62,13 +60,6 @@ class GaugeChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod @JsonPropertyDescription("List of step ranges for the gauge") var steps: List[GaugeChartSteps] = List() - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema().add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Gauge Chart", @@ -195,8 +186,6 @@ class GaugeChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod |""".encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { // The column name is typed-in text, so it is emitted as a properly escaped // Python literal — hand-written quotes would let a quote, backslash or newline diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/heatMap/HeatMapOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/heatMap/HeatMapOpDesc.scala index 22cf2b4f349..8d0ae09e5ab 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/heatMap/HeatMapOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/heatMap/HeatMapOpDesc.scala @@ -21,21 +21,19 @@ package org.apache.texera.amber.operator.visualization.heatMap import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import javax.validation.constraints.NotNull -class HeatMapOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class HeatMapOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "x", required = true) @JsonSchemaTitle("Value X Column") @@ -58,14 +56,6 @@ class HeatMapOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { @NotNull(message = "Values cannot be empty") var value: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Heatmap", @@ -115,8 +105,6 @@ class HeatMapOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { finalcode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val valueLit = pyStringLiteral(value) val xLit = pyStringLiteral(x) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/hierarchychart/HierarchyChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/hierarchychart/HierarchyChartOpDesc.scala index b57aa440f9c..cded24bc5c9 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/hierarchychart/HierarchyChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/hierarchychart/HierarchyChartOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.hierarchychart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -46,7 +44,7 @@ import javax.validation.constraints.{NotEmpty, NotNull} } } """) -class HierarchyChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class HierarchyChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(required = true) @JsonSchemaTitle("Chart Type") @JsonPropertyDescription("Treemap or Sunburst") @@ -68,14 +66,6 @@ class HierarchyChartOpDesc extends PythonOperatorDescriptor with PlotlyStandalon @NotNull(message = "Value Column cannot be empty") var value: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Hierarchy Chart", @@ -141,10 +131,6 @@ class HierarchyChartOpDesc extends PythonOperatorDescriptor with PlotlyStandalon finalCode.encode } - // Output is an HTML chart, not a tabular DataFrame. - // The translator skips it in the leaf-DataFrame print block. - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val attributes = hierarchy.map(section => pyStringLiteral(section.attributeName)).mkString(", ") val valueLit = pyStringLiteral(value) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/histogram/HistogramChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/histogram/HistogramChartOpDesc.scala index bfdfbb28485..2546a514c46 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/histogram/HistogramChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/histogram/HistogramChartOpDesc.scala @@ -21,21 +21,19 @@ package org.apache.texera.amber.operator.visualization.histogram import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import javax.validation.constraints.NotNull -class HistogramChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class HistogramChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "value", required = true) @JsonSchemaTitle("Value Column") @JsonPropertyDescription("Column for counting values.") @@ -126,16 +124,6 @@ class HistogramChartOpDesc extends PythonOperatorDescriptor with PlotlyStandalon finalCode.encode } - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val colorParam = if (color.nonEmpty) s""", color=${pyStringLiteral(color)}""" else "" val categoryParam = diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/histogram2d/Histogram2DOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/histogram2d/Histogram2DOpDesc.scala index 1da0ffa4f53..12388ed1b0f 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/histogram2d/Histogram2DOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/histogram2d/Histogram2DOpDesc.scala @@ -20,21 +20,19 @@ package org.apache.texera.amber.operator.visualization.histogram2d import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import javax.validation.constraints.NotNull -class Histogram2DOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class Histogram2DOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(required = true) @JsonSchemaTitle("X Column") @@ -74,13 +72,6 @@ class Histogram2DOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo OperatorGroupConstants.VISUALIZATION_STATISTICAL_GROUP ) - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema().add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def generatePythonCode(): String = { assert(xBins > 0, s"X Bins must be > 0, but got $xBins") assert(yBins > 0, s"Y Bins must be > 0, but got $yBins") @@ -125,8 +116,6 @@ class Histogram2DOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo |""".encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { assert(xBins > 0, s"X Bins must be > 0, but got $xBins") assert(yBins > 0, s"Y Bins must be > 0, but got $yBins") diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/lineChart/LineChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/lineChart/LineChartOpDesc.scala index 983bfef961f..e9178bf66d5 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/lineChart/LineChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/lineChart/LineChartOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.lineChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -37,7 +35,7 @@ import java.util import javax.validation.constraints.NotEmpty import scala.jdk.CollectionConverters.ListHasAsScala -class LineChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class LineChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "yLabel", required = false, defaultValue = "Y Axis") @JsonSchemaTitle("Y Label") @@ -53,14 +51,6 @@ class LineChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode @NotEmpty(message = "At least one line must be configured") var lines: util.List[LineConfig] = new util.ArrayList[LineConfig]() - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Line Chart", @@ -133,8 +123,6 @@ class LineChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val traces = lines.asScala .map { lineConf => diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/nestedTable/NestedTableOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/nestedTable/NestedTableOpDesc.scala index 7da846c1bfc..878046892c2 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/nestedTable/NestedTableOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/nestedTable/NestedTableOpDesc.scala @@ -19,10 +19,8 @@ package org.apache.texera.amber.operator.visualization.nestedTable import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.{PythonOperatorDescriptor, StandaloneCodeGenerator} +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.pyStringLiteral @@ -31,7 +29,7 @@ import java.util import javax.validation.constraints.NotEmpty import scala.jdk.CollectionConverters.ListHasAsScala -class NestedTableOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenerator { +class NestedTableOpDesc extends PythonVisualizationOpDesc { @JsonPropertyDescription( "List of columns to include in the nested table chart and their subgroup" @@ -40,14 +38,6 @@ class NestedTableOpDesc extends PythonOperatorDescriptor with StandaloneCodeGene @NotEmpty(message = "Included Columns cannot be empty") var includedColumns: util.List[NestedTableConfig] = new util.ArrayList[NestedTableConfig]() - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Nested Table", @@ -139,8 +129,6 @@ class NestedTableOpDesc extends PythonOperatorDescriptor with StandaloneCodeGene finalcode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val sortedColumns = includedColumns.asScala.sortBy(_.attributeGroup) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/networkGraph/NetworkGraphOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/networkGraph/NetworkGraphOpDesc.scala index fc6502f19d7..23abd3a9b0c 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/networkGraph/NetworkGraphOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/networkGraph/NetworkGraphOpDesc.scala @@ -21,12 +21,10 @@ package org.apache.texera.amber.operator.visualization.networkGraph import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -34,7 +32,7 @@ import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.pyStringLiteral import javax.validation.constraints.NotNull -class NetworkGraphOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class NetworkGraphOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(required = true) @JsonSchemaTitle("Source Column") @JsonPropertyDescription("Source node for edge in graph") @@ -53,14 +51,6 @@ class NetworkGraphOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneC @JsonSchemaTitle("Title") var title: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Network Graph", @@ -206,8 +196,6 @@ class NetworkGraphOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneC finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val sourceLit = pyStringLiteral(source) val destinationLit = pyStringLiteral(destination) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/parallelCoordinatesPlot/ParallelCoordinatesPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/parallelCoordinatesPlot/ParallelCoordinatesPlotOpDesc.scala index e67217ae621..58347b3867b 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/parallelCoordinatesPlot/ParallelCoordinatesPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/parallelCoordinatesPlot/ParallelCoordinatesPlotOpDesc.scala @@ -21,10 +21,8 @@ package org.apache.texera.amber.operator.visualization.parallelCoordinatesPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{ AutofillAttributeName, AutofillAttributeNameList @@ -54,7 +52,7 @@ import javax.validation.constraints.{NotNull, Size} } } """) -class ParallelCoordinatesPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class ParallelCoordinatesPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "dimensions", required = true) @JsonSchemaTitle("Dimensions") @@ -77,14 +75,6 @@ class ParallelCoordinatesPlotOpDesc extends PythonOperatorDescriptor with Plotly OperatorGroupConstants.VISUALIZATION_SCIENTIFIC_GROUP ) - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - def manipulateTable(): PythonTemplateBuilder = { val dimCols = dimensions.map(c => pyb"$c").mkString(",") val colorFilter = @@ -139,8 +129,6 @@ class ParallelCoordinatesPlotOpDesc extends PythonOperatorDescriptor with Plotly finalcode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val dimCols = dimensions.map(pyStringLiteral).mkString("[", ", ", "]") val colorLit = pyStringLiteral(color) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/pieChart/PieChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/pieChart/PieChartOpDesc.scala index d6c47308d75..00c942584dc 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/pieChart/PieChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/pieChart/PieChartOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.pieChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -46,7 +44,7 @@ import javax.validation.constraints.NotNull } } """) -class PieChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class PieChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "value", required = true) @JsonSchemaTitle("Value Column") @@ -63,14 +61,6 @@ class PieChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode @NotNull(message = "Name Column cannot be empty") var name: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Pie Chart", @@ -133,8 +123,6 @@ class PieChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode finalcode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val nameLit = pyStringLiteral(name) val valueLit = pyStringLiteral(value) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/polarChart/PolarChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/polarChart/PolarChartOpDesc.scala index 470e58b1027..4c023c6dbf3 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/polarChart/PolarChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/polarChart/PolarChartOpDesc.scala @@ -21,11 +21,10 @@ package org.apache.texera.amber.operator.visualization.polarChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.core.workflow.OutputPort.OutputMode -import org.apache.texera.amber.core.workflow.{InputPort, OutputPort, PortIdentity} -import org.apache.texera.amber.operator.PythonOperatorDescriptor +import org.apache.texera.amber.core.workflow.{InputPort, OutputPort} import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString @@ -47,7 +46,7 @@ import javax.validation.constraints.NotNull } } """) -class PolarChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class PolarChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "r", required = true) @JsonSchemaTitle("r") @@ -63,15 +62,6 @@ class PolarChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod @NotNull(message = "theta cannot be empty") var theta: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo( "Polar Chart", @@ -130,8 +120,6 @@ class PolarChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val rLit = pyStringLiteral(r) val thetaLit = pyStringLiteral(theta) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDesc.scala index 99201de207f..0147c5b9322 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/quiverPlot/QuiverPlotOpDesc.scala @@ -21,11 +21,9 @@ package org.apache.texera.amber.operator.visualization.quiverPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.{PythonOperatorDescriptor, StandaloneCodeGenerator} +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -51,7 +49,7 @@ import javax.validation.constraints.NotNull } } """) -class QuiverPlotOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenerator { +class QuiverPlotOpDesc extends PythonVisualizationOpDesc { //property panel variable: 4 requires: {x,y,u,v}, all columns should only contain numerical data @@ -85,14 +83,6 @@ class QuiverPlotOpDesc extends PythonOperatorDescriptor with StandaloneCodeGener @NotNull(message = "v cannot be empty") var v: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Quiver Plot", @@ -162,8 +152,6 @@ class QuiverPlotOpDesc extends PythonOperatorDescriptor with StandaloneCodeGener finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val xLit = pyStringLiteral(x) val yLit = pyStringLiteral(y) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/radarChart/RadarChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/radarChart/RadarChartOpDesc.scala index 920909993a9..e964e6b7823 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/radarChart/RadarChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/radarChart/RadarChartOpDesc.scala @@ -21,10 +21,8 @@ package org.apache.texera.amber.operator.visualization.radarChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{ AutofillAttributeName, AutofillAttributeNameList @@ -49,7 +47,7 @@ import javax.validation.constraints.{NotEmpty, NotNull} } } """) -class RadarChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class RadarChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "nameColumn", required = true) @JsonSchemaTitle("Name Column") @@ -80,14 +78,6 @@ class RadarChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod OperatorGroupConstants.VISUALIZATION_SCIENTIFIC_GROUP ) - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - def manipulateTable(): PythonTemplateBuilder = { assert(nameColumn.nonEmpty, "Name Column cannot be empty") assert(valueColumns != null && valueColumns.nonEmpty, "Value Columns cannot be empty") @@ -167,8 +157,6 @@ class RadarChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod finalcode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val valueColsList = valueColumns.map(pyStringLiteral).mkString("[", ", ", "]") val requiredCols = diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/radarPlot/RadarPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/radarPlot/RadarPlotOpDesc.scala index 659e41097db..86ec94ce1c1 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/radarPlot/RadarPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/radarPlot/RadarPlotOpDesc.scala @@ -21,15 +21,13 @@ package org.apache.texera.amber.operator.visualization.radarPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.operator.metadata.annotations.{ AutofillAttributeName, AutofillAttributeNameList } import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode -import org.apache.texera.amber.core.workflow.PortIdentity +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ @@ -48,7 +46,7 @@ import javax.validation.constraints.NotEmpty } } """) -class RadarPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class RadarPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "selectedAttributes", required = true) @JsonSchemaTitle("Axes") @JsonPropertyDescription("Numeric columns to use as radar axes") @@ -102,14 +100,6 @@ class RadarPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode ) var showLegend: Boolean = true - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Radar Plot", @@ -236,8 +226,6 @@ class RadarPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val attributes = Option(selectedAttributes).getOrElse(Nil) val attrList = attributes.map(pyStringLiteral).mkString(", ") diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDesc.scala index 05ea9f91d2e..cf96fa725ab 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/rangeSlider/RangeSliderOpDesc.scala @@ -21,12 +21,10 @@ package org.apache.texera.amber.operator.visualization.rangeSlider import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -45,7 +43,7 @@ import javax.validation.constraints.NotNull } } """) -class RangeSliderOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class RangeSliderOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "Y-axis", required = true) @JsonSchemaTitle("Y-axis") @JsonPropertyDescription("The name of the column to represent y-axis") @@ -65,14 +63,6 @@ class RangeSliderOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo @JsonPropertyDescription("How to handle duplicate values in y-axis") var duplicateType: RangeSliderHandleDuplicateFunction = RangeSliderHandleDuplicateFunction.NOTHING - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Range Slider", @@ -145,8 +135,6 @@ class RangeSliderOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo finalcode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val functionType = duplicateType.getFunctionType val xAxisLit = pyStringLiteral(xAxis) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/sankeyDiagram/SankeyDiagramOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/sankeyDiagram/SankeyDiagramOpDesc.scala index 6d8cbdde01e..dc9e2cce015 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/sankeyDiagram/SankeyDiagramOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/sankeyDiagram/SankeyDiagramOpDesc.scala @@ -21,12 +21,10 @@ package org.apache.texera.amber.operator.visualization.sankeyDiagram import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -45,7 +43,7 @@ import javax.validation.constraints.NotNull } } """) -class SankeyDiagramOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class SankeyDiagramOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "Source Attribute", required = true) @JsonSchemaTitle("Source Attribute") @@ -71,14 +69,6 @@ class SankeyDiagramOpDesc extends PythonOperatorDescriptor with PlotlyStandalone @NotNull(message = "Value Attribute cannot be empty") var valueAttribute: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Sankey Diagram", @@ -149,8 +139,6 @@ class SankeyDiagramOpDesc extends PythonOperatorDescriptor with PlotlyStandalone finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val sourceLit = pyStringLiteral(sourceAttribute) val targetLit = pyStringLiteral(targetAttribute) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/scatter3DChart/Scatter3dChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/scatter3DChart/Scatter3dChartOpDesc.scala index ea410612e53..89f643815e7 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/scatter3DChart/Scatter3dChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/scatter3DChart/Scatter3dChartOpDesc.scala @@ -21,19 +21,17 @@ package org.apache.texera.amber.operator.visualization.scatter3DChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.pyStringLiteral import javax.validation.constraints.NotNull -class Scatter3dChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class Scatter3dChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "x", required = true) @JsonSchemaTitle("X Column") @JsonPropertyDescription("Data column for the x-axis") @@ -55,14 +53,6 @@ class Scatter3dChartOpDesc extends PythonOperatorDescriptor with PlotlyStandalon @NotNull(message = "Z Column cannot be empty") var z: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Scatter3D Chart", @@ -129,8 +119,6 @@ class Scatter3dChartOpDesc extends PythonOperatorDescriptor with PlotlyStandalon finalcode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val xLit = pyStringLiteral(x) val yLit = pyStringLiteral(y) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/scatterplot/ScatterplotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/scatterplot/ScatterplotOpDesc.scala index 29f928dcc69..b5358b4f4b6 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/scatterplot/ScatterplotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/scatterplot/ScatterplotOpDesc.scala @@ -21,12 +21,10 @@ package org.apache.texera.amber.operator.visualization.scatterplot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -47,7 +45,7 @@ import javax.validation.constraints.NotNull " }" + "}" ) -class ScatterplotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class ScatterplotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(required = true) @JsonSchemaTitle("X-Column") @@ -93,14 +91,6 @@ class ScatterplotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo @AutofillAttributeName var hoverName: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Scatter Plot", @@ -178,8 +168,6 @@ class ScatterplotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val dropCols = List(xColumn, yColumn, colorColumn) .filter(_.nonEmpty) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/stripChart/StripChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/stripChart/StripChartOpDesc.scala index 36507659eb3..9d38910e2ac 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/stripChart/StripChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/stripChart/StripChartOpDesc.scala @@ -21,19 +21,17 @@ package org.apache.texera.amber.operator.visualization.stripChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.pyStringLiteral import javax.validation.constraints.NotNull -class StripChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class StripChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "x", required = true) @JsonSchemaTitle("X-Axis Column") @@ -61,14 +59,6 @@ class StripChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod @AutofillAttributeName var facetColumn: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Strip Chart", @@ -124,8 +114,6 @@ class StripChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod |""".encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val xLit = pyStringLiteral(x) val yLit = pyStringLiteral(y) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDesc.scala index 3c5a18d04a1..d3710dfe346 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/tablesChart/TablesPlotOpDesc.scala @@ -20,17 +20,15 @@ package org.apache.texera.amber.operator.visualization.tablesChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.pyStringLiteral import javax.validation.constraints.NotEmpty -class TablesPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class TablesPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonPropertyDescription("List of columns to include in the table chart") @JsonProperty(value = "add attribute", required = true) @@ -109,16 +107,6 @@ class TablesPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod ) } - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { assert(includedColumns.nonEmpty) // Mirror getAttributes: a Python list literal of the selected column names. diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ternaryContour/TernaryContourOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ternaryContour/TernaryContourOpDesc.scala index b0b54ccbc4c..71f691d6f12 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ternaryContour/TernaryContourOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ternaryContour/TernaryContourOpDesc.scala @@ -21,15 +21,14 @@ package org.apache.texera.amber.operator.visualization.ternaryContour import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.core.workflow.OutputPort.OutputMode import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.{ PythonTemplateBuilderStringContext, pyStringLiteral } import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.{InputPort, OutputPort, PortIdentity} -import org.apache.texera.amber.operator.{PythonOperatorDescriptor, StandaloneCodeGenerator} +import org.apache.texera.amber.core.workflow.{InputPort, OutputPort} +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -55,7 +54,7 @@ import javax.validation.constraints.NotNull } } """) -class TernaryContourOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenerator { +class TernaryContourOpDesc extends PythonVisualizationOpDesc { // Add annotations for the first variable @JsonProperty(value = "firstVariable", required = true) @@ -103,14 +102,6 @@ class TernaryContourOpDesc extends PythonOperatorDescriptor with StandaloneCodeG outputPorts = List(OutputPort(mode = OutputMode.SINGLE_SNAPSHOT)) ) - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - /** Returns a Python string that drops any tuples with missing values */ def manipulateTable(): PythonTemplateBuilder = { // Check for any empty data field names @@ -177,8 +168,6 @@ class TernaryContourOpDesc extends PythonOperatorDescriptor with StandaloneCodeG finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val firstLit = pyStringLiteral(firstVariable) val secondLit = pyStringLiteral(secondVariable) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ternaryPlot/TernaryPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ternaryPlot/TernaryPlotOpDesc.scala index e3ee55fbe9b..14ec74c334f 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ternaryPlot/TernaryPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/ternaryPlot/TernaryPlotOpDesc.scala @@ -21,12 +21,10 @@ package org.apache.texera.amber.operator.visualization.ternaryPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -53,7 +51,7 @@ import javax.validation.constraints.NotNull } } """) -class TernaryPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class TernaryPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { // Add annotations for the first variable @JsonProperty(value = "firstVariable", required = true) @@ -101,14 +99,6 @@ class TernaryPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo operatorGroupName = OperatorGroupConstants.VISUALIZATION_SCIENTIFIC_GROUP ) - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - /** Returns a Python string that drops any tuples with missing values */ def manipulateTable(): PythonTemplateBuilder = { // Check for any empty data field names @@ -165,8 +155,6 @@ class TernaryPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val colorArg = if (colorEnabled && colorDataField.nonEmpty) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/timeSeriesplot/TimeSeriesOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/timeSeriesplot/TimeSeriesOpDesc.scala index f31ab2a901d..a7ae7636d1d 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/timeSeriesplot/TimeSeriesOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/timeSeriesplot/TimeSeriesOpDesc.scala @@ -20,12 +20,10 @@ package org.apache.texera.amber.operator.visualization.timeSeriesplot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.pyStringLiteral @@ -46,7 +44,7 @@ import javax.validation.constraints.{NotBlank, NotNull} } } """) -class TimeSeriesOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class TimeSeriesOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "timeColumn", required = true) @JsonSchemaTitle("Time Column") @@ -94,13 +92,6 @@ class TimeSeriesOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod @JsonPropertyDescription("Display a range slider at the bottom of the plot.") var showRangeSlider: Boolean = _ - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema().add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Time Series Plot", @@ -165,8 +156,6 @@ class TimeSeriesOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCod |""".encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val dropnaCols = List(timeColumn, valueColumn) ++ (if (CategoryColumn != "No Selection") Some(CategoryColumn) else None) ++ diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/treeplot/TreePlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/treeplot/TreePlotOpDesc.scala index 1df3d94e4ec..49c197ebbda 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/treeplot/TreePlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/treeplot/TreePlotOpDesc.scala @@ -21,12 +21,10 @@ package org.apache.texera.amber.operator.visualization.treeplot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.pyStringLiteral @@ -40,7 +38,7 @@ import javax.validation.constraints.NotNull * to construct and visualize an interactive, top-down tree that automatically * sizes itself and supports intuitive scroll/pinch zooming. */ -class TreePlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class TreePlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "Edge List Column", required = true) @JsonSchemaTitle("Edge List Column") @@ -58,15 +56,6 @@ class TreePlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode operatorGroupName = OperatorGroupConstants.VISUALIZATION_STATISTICAL_GROUP ) - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - Map( - operatorInfo.outputPorts.head.id -> Schema() - .add("html-content", AttributeType.STRING) - ) - } - override def generatePythonCode(): String = { assert(edgeListColumn.nonEmpty, "Edge List Column cannot be empty") @@ -247,8 +236,6 @@ class TreePlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode |""".encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { s"""import ast | diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/volcanoPlot/VolcanoPlotOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/volcanoPlot/VolcanoPlotOpDesc.scala index 9ceac44472a..8e6d6dc6608 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/volcanoPlot/VolcanoPlotOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/volcanoPlot/VolcanoPlotOpDesc.scala @@ -21,12 +21,10 @@ package org.apache.texera.amber.operator.visualization.volcanoPlot import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.pyStringLiteral @@ -43,7 +41,7 @@ import javax.validation.constraints.NotNull } } """) -class VolcanoPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class VolcanoPlotOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(required = true) @JsonSchemaTitle("Effect Size (log2 Fold Change)") @@ -76,14 +74,6 @@ class VolcanoPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo operatorGroupName = OperatorGroupConstants.VISUALIZATION_SCIENTIFIC_GROUP ) - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def generatePythonCode(): String = { pyb""" |from pytexera import * @@ -129,8 +119,6 @@ class VolcanoPlotOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCo |""".encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val pvalueLit = pyStringLiteral(pvalueColumn) val effectLit = pyStringLiteral(effectColumn) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/waterfallChart/WaterfallChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/waterfallChart/WaterfallChartOpDesc.scala index e2e7b339601..2ed883ff3b3 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/waterfallChart/WaterfallChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/waterfallChart/WaterfallChartOpDesc.scala @@ -21,12 +21,10 @@ package org.apache.texera.amber.operator.visualization.waterfallChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -43,7 +41,7 @@ import javax.validation.constraints.NotNull } } """) -class WaterfallChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class WaterfallChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "xColumn", required = true) @JsonSchemaTitle("X Axis Values") @@ -59,14 +57,6 @@ class WaterfallChartOpDesc extends PythonOperatorDescriptor with PlotlyStandalon @NotNull(message = "Y Axis Values cannot be empty") var yColumn: EncodableString = "" - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Waterfall Chart", @@ -127,8 +117,6 @@ class WaterfallChartOpDesc extends PythonOperatorDescriptor with PlotlyStandalon finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { s"""def render_error(error_msg) -> str: | return '''

Waterfall chart is not available.

diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/windRoseChart/WindRoseChartOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/windRoseChart/WindRoseChartOpDesc.scala index 8d98ac3909c..dcfcead4b84 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/windRoseChart/WindRoseChartOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/windRoseChart/WindRoseChartOpDesc.scala @@ -21,13 +21,12 @@ package org.apache.texera.amber.operator.visualization.windRoseChart import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} import com.kjetland.jackson.jsonSchema.annotations.{JsonSchemaInject, JsonSchemaTitle} -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.core.workflow.OutputPort.OutputMode import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.{InputPort, OutputPort, PortIdentity} -import org.apache.texera.amber.operator.PythonOperatorDescriptor +import org.apache.texera.amber.core.workflow.{InputPort, OutputPort} import org.apache.texera.amber.operator.visualization.PlotlyStandaloneCode +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.{AutofillAttributeName, SampleColumn} import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder @@ -44,7 +43,7 @@ import javax.validation.constraints.NotNull } } """) -class WindRoseChartOpDesc extends PythonOperatorDescriptor with PlotlyStandaloneCode { +class WindRoseChartOpDesc extends PythonVisualizationOpDesc with PlotlyStandaloneCode { @JsonProperty(value = "rColumn", required = true) @JsonSchemaTitle("Radial Values (r)") @@ -76,14 +75,6 @@ class WindRoseChartOpDesc extends PythonOperatorDescriptor with PlotlyStandalone outputPorts = List(OutputPort(mode = OutputMode.SINGLE_SNAPSHOT)) ) - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - def createPlotlyFigure(): PythonTemplateBuilder = { assert(rColumn.nonEmpty, "Radial Values (r) column must be selected.") assert(thetaColumn.nonEmpty, "Angular Values (θ) column must be selected.") @@ -140,8 +131,6 @@ class WindRoseChartOpDesc extends PythonOperatorDescriptor with PlotlyStandalone finalCode.encode } - override def producesDataFrame(): Boolean = false - override def generateStandaloneCode(): String = { val rLit = pyStringLiteral(rColumn) val colorArg = diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/wordCloud/WordCloudOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/wordCloud/WordCloudOpDesc.scala index 8a9fd112e0b..f43a9dcda26 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/wordCloud/WordCloudOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/wordCloud/WordCloudOpDesc.scala @@ -25,11 +25,9 @@ import com.kjetland.jackson.jsonSchema.annotations.{ JsonSchemaInt, JsonSchemaTitle } -import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.pybuilder.PythonTemplateBuilder.PythonTemplateBuilderStringContext import org.apache.texera.amber.pybuilder.PyStringTypes.EncodableString -import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.{PythonOperatorDescriptor, StandaloneCodeGenerator} +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.metadata.annotations.AutofillAttributeName import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, OperatorInfo} import org.apache.texera.amber.operator.visualization.ImageUtility @@ -47,7 +45,7 @@ import javax.validation.constraints.NotNull } } """) -class WordCloudOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenerator { +class WordCloudOpDesc extends PythonVisualizationOpDesc { @JsonProperty(required = true) @JsonSchemaTitle("Text column") @AutofillAttributeName @@ -59,14 +57,6 @@ class WordCloudOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenera @JsonSchemaInject(ints = Array(new JsonSchemaInt(path = "exclusiveMinimum", value = 0))) var topN: Integer = 100 - override def getOutputSchemas( - inputSchemas: Map[PortIdentity, Schema] - ): Map[PortIdentity, Schema] = { - val outputSchema = Schema() - .add("html-content", AttributeType.STRING) - Map(operatorInfo.outputPorts.head.id -> outputSchema) - } - override def operatorInfo: OperatorInfo = OperatorInfo.forVisualization( "Word Cloud", @@ -142,8 +132,6 @@ class WordCloudOpDesc extends PythonOperatorDescriptor with StandaloneCodeGenera |""".encode } - override def producesDataFrame(): Boolean = false - // The two guards mirror generatePythonCode's: WordCloud.generate raises on a // wordless string, so without them an input that is empty — or whose text // column survives neither the dropna nor the word filter — crashes here where diff --git a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/metadata/VisualizationOutputSchemaSpec.scala b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/metadata/VisualizationOutputSchemaSpec.scala index d1f5ee04a0a..c8ec725bbd8 100644 --- a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/metadata/VisualizationOutputSchemaSpec.scala +++ b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/metadata/VisualizationOutputSchemaSpec.scala @@ -21,27 +21,27 @@ package org.apache.texera.amber.operator.metadata import org.apache.texera.amber.core.tuple.{AttributeType, Schema} import org.apache.texera.amber.core.workflow.PortIdentity -import org.apache.texera.amber.operator.PythonOperatorDescriptor +import org.apache.texera.amber.operator.visualization.PythonVisualizationOpDesc import org.apache.texera.amber.operator.visualization.htmlviz.HtmlVizOpDesc import org.apache.texera.amber.operator.visualization.urlviz.UrlVizOpDesc import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers /** - * Guard for the visualization output-schema contract, over every registered + * Guard for the visualization output contract, over every registered * visualization descriptor. * * The frontend locates the chart HTML a visualization returns by reading a - * single `html-content` STRING column from the operator's one output port. - * Every Python visualization descriptor re-declares that contract in its own - * `getOutputSchemas` override, so a new chart (or an edit to an existing one) - * can silently ship a schema the frontend cannot render, and only that - * operator's own spec -- if it asserts the schema at all -- would notice. + * single `html-content` STRING column from the operator's one output port, and + * the exported script must not print a chart as a leaf DataFrame. + * `PythonVisualizationOpDesc` declares both as final members, but a new chart + * that does not extend it can still ship a schema the frontend cannot render, + * and only that operator's own spec -- if it asserts the schema at all -- would + * notice. * * Sweeping the registry rather than testing the descriptors one by one is - * deliberate: the contract lives in 48 identical three-line overrides, so the - * mistake is as easy to make in the next chart as in these, and a per-operator - * test would not exist for the next chart until someone remembers to write it. + * deliberate: a per-operator test would not exist for the next chart until + * someone remembers to write it. */ class VisualizationOutputSchemaSpec extends AnyFlatSpec with Matchers { @@ -62,8 +62,8 @@ class VisualizationOutputSchemaSpec extends AnyFlatSpec with Matchers { private val sweptClasses = visualizationClasses.filterNot(nonPythonVisualizations.contains) - private def instantiate(opClass: Class[_]): PythonOperatorDescriptor = - opClass.getConstructor().newInstance().asInstanceOf[PythonOperatorDescriptor] + private def instantiate(opClass: Class[_]): PythonVisualizationOpDesc = + opClass.getConstructor().newInstance().asInstanceOf[PythonVisualizationOpDesc] "the registry" should "contain the visualization descriptors this sweep guards" in { // If the selection ever comes back empty (say, the package is renamed), @@ -71,13 +71,13 @@ class VisualizationOutputSchemaSpec extends AnyFlatSpec with Matchers { sweptClasses.size should be >= 48 } - it should "register no visualization descriptor outside the Python contract except the known two" in { - val nonPython = - visualizationClasses.filterNot(classOf[PythonOperatorDescriptor].isAssignableFrom) - nonPython.toSet shouldBe nonPythonVisualizations + it should "register no visualization descriptor outside PythonVisualizationOpDesc except the known two" in { + val outsideTheTrait = + visualizationClasses.filterNot(classOf[PythonVisualizationOpDesc].isAssignableFrom) + outsideTheTrait.toSet shouldBe nonPythonVisualizations } - "every Python visualization descriptor" should "declare a single html-content STRING column on its one output port" in { + "every Python visualization descriptor" should "declare a single html-content STRING column on its one output port and not produce a DataFrame" in { sweptClasses.foreach { opClass => withClue(s"${opClass.getSimpleName}: ") { val op = instantiate(opClass) @@ -90,6 +90,7 @@ class VisualizationOutputSchemaSpec extends AnyFlatSpec with Matchers { val schema = outputSchemas(outputPortIds.head) schema.getAttributeNames shouldBe List("html-content") schema.getAttribute("html-content").getType shouldBe AttributeType.STRING + op.producesDataFrame() shouldBe false } } }