This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new 1362167975 feat(visualization): declare the column types Wind Rose and 
Bubble Chart require (#7341)
1362167975 is described below

commit 1362167975e93322282908bbfbe7ea8ac2c4d2fe
Author: Kary Zheng <[email protected]>
AuthorDate: Fri Aug 7 18:15:40 2026 -0700

    feat(visualization): declare the column types Wind Rose and Bubble Chart 
require (#7341)
    
    ### What changes were proposed in this PR?
    
    Wind Rose's radial value and Bubble Chart's z column are both consumed
    as magnitudes, and neither declared an `attributeTypeRules` entry, so
    the property form offered every column and accepted a string one. Each
    now declares `integer`, `long` or `double`, the way Range Slider's
    y-axis and Radar Chart's value columns already do.
    <img width="1433" height="925" alt="Screenshot 2026-08-07 at 2 21 49 PM"
    
src="https://github.com/user-attachments/assets/61e45feb-22ca-4b7d-b447-8785a4ad3695";
    />
    <img width="1432" height="920" alt="Screenshot 2026-08-07 at 2 23 46 PM"
    
src="https://github.com/user-attachments/assets/7336c652-869f-4f92-a075-98b2b55a2c52";
    />
    
    
    The other pickers are deliberately left alone: Wind Rose's angle is a
    direction label and Bubble Chart's x and y are positions, all of which
    take any type the way a scatter plot's axes do.
    
    ### Any related issues, documentation, discussions?
    
    Closes #7324. Same class of gap as #7250, which covered a different set
    of operators.
    
    ### How was this PR tested?
    
    Each spec gains a case that generates its descriptor's schema and
    asserts the rule on it: keyed to `rColumn` and to `zValue`, each naming
    a property the schema declares, each allowing exactly `integer`, `long`
    and `double`, and each stating that `string` is not among them. Reading
    the annotation text would not have shown this — a key naming no property
    parses exactly as well and constrains nothing, which is what #7210
    collected four instances of.
    
    Removing either annotation leaves exactly those two cases failing. Each
    asserts the whole key set rather than the presence of its own key, so
    constraining the angle or the two axes later would fail here too — those
    read as any type on purpose.
    
    ```
    sbt "WorkflowOperator/testOnly 
org.apache.texera.amber.operator.visualization.windRoseChart.WindRoseChartOpDescSpec
 
org.apache.texera.amber.operator.visualization.bubbleChart.BubbleChartOpDescSpec"
    ```
    
    Thirteen cases, all passing.
    
    The behaviour each rule prevents was reproduced first: rendering the
    same three-row frame with a numeric column and with a string one, Wind
    Rose's `radialaxis.type` comes out `linear` with range 0 to 4.2 for the
    numeric column and `category` with range -0.11 to 2.11 for the string
    one — the wedges drawn at ordinal positions rather than lengths, with no
    error — and this holds even when every value is a number written as
    text. Bubble Chart raises `TypeError: unsupported operand type(s) for /:
    'str' and 'int'` on either.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Claude Opus 5)
    
    ---------
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 .../bubbleChart/BubbleChartOpDesc.scala              | 12 +++++++++++-
 .../windRoseChart/WindRoseChartOpDesc.scala          | 12 +++++++++++-
 .../bubbleChart/BubbleChartOpDescSpec.scala          | 20 ++++++++++++++++++++
 .../windRoseChart/WindRoseChartOpDescSpec.scala      | 20 ++++++++++++++++++++
 4 files changed, 62 insertions(+), 2 deletions(-)

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 d876b95769..ad31361064 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
@@ -20,7 +20,7 @@
 package org.apache.texera.amber.operator.visualization.bubbleChart
 
 import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription}
-import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle
+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
@@ -39,6 +39,16 @@ import javax.validation.constraints.NotNull
   */
 
 // type can be numerical only
+// The z column is the bubble size, which plotly express divides by a scale
+// factor, so text aborts the run. The x and y axes are positions and take any
+// type, the way a scatter plot's do.
+@JsonSchemaInject(json = """
+{
+  "attributeTypeRules": {
+    "zValue": { "enum": ["integer", "long", "double"] }
+  }
+}
+""")
 class BubbleChartOpDesc extends PythonOperatorDescriptor {
 
   @JsonProperty(value = "xValue", required = true)
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 e12011c7d3..9cb36f5882 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
@@ -20,7 +20,7 @@
 package org.apache.texera.amber.operator.visualization.windRoseChart
 
 import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription}
-import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle
+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
@@ -32,6 +32,16 @@ import 
org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, Operat
 import org.apache.texera.amber.pybuilder.PythonTemplateBuilder
 import javax.validation.constraints.NotNull
 
+// The radial value is the length of each wedge, so it has to be a number: 
given
+// text, plotly turns the radial axis categorical and the wedges stop meaning
+// anything. The angle is a direction label and takes any type.
+@JsonSchemaInject(json = """
+{
+  "attributeTypeRules": {
+    "rColumn": { "enum": ["integer", "long", "double"] }
+  }
+}
+""")
 class WindRoseChartOpDesc extends PythonOperatorDescriptor {
 
   @JsonProperty(value = "rColumn", required = true)
diff --git 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/bubbleChart/BubbleChartOpDescSpec.scala
 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/bubbleChart/BubbleChartOpDescSpec.scala
index b0945fd770..85fbd1fa3f 100644
--- 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/bubbleChart/BubbleChartOpDescSpec.scala
+++ 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/bubbleChart/BubbleChartOpDescSpec.scala
@@ -19,10 +19,13 @@
 
 package org.apache.texera.amber.operator.visualization.bubbleChart
 
+import org.apache.texera.amber.operator.metadata.OperatorMetadataGenerator
 import org.scalatest.BeforeAndAfter
 import org.scalatest.flatspec.AnyFlatSpec
 import org.scalatest.matchers.should.Matchers
 
+import scala.jdk.CollectionConverters._
+
 class BubbleChartOpDescSpec extends AnyFlatSpec with BeforeAndAfter with 
Matchers {
   var opDesc: BubbleChartOpDesc = _
 
@@ -99,4 +102,21 @@ class BubbleChartOpDescSpec extends AnyFlatSpec with 
BeforeAndAfter with Matcher
     plain should include("column2")
     plain should include("column3")
   }
+
+  "BubbleChartOpDesc's generated schema" should
+    "constrain the bubble size to numeric and leave the axes unconstrained" in 
{
+    // x and y are positions and read as any type, the way a scatter plot's do.
+    // Only the size is divided by a scale factor and so has to be a number.
+    val schema = 
OperatorMetadataGenerator.generateOperatorJsonSchema(classOf[BubbleChartOpDesc])
+    val rules = schema.path("attributeTypeRules")
+    rules.fieldNames().asScala.toSet shouldBe Set("zValue")
+
+    // The key has to name a property the form actually renders; one that names
+    // nothing parses fine and constrains nothing.
+    schema.path("properties").has("zValue") shouldBe true
+
+    val allowed = 
rules.path("zValue").path("enum").elements().asScala.map(_.asText()).toSet
+    allowed shouldBe Set("integer", "long", "double")
+    allowed should not contain "string"
+  }
 }
diff --git 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/windRoseChart/WindRoseChartOpDescSpec.scala
 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/windRoseChart/WindRoseChartOpDescSpec.scala
index c25093457e..d8b091eff9 100644
--- 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/windRoseChart/WindRoseChartOpDescSpec.scala
+++ 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/visualization/windRoseChart/WindRoseChartOpDescSpec.scala
@@ -19,6 +19,7 @@
 
 package org.apache.texera.amber.operator.visualization.windRoseChart
 
+import org.apache.texera.amber.operator.metadata.OperatorMetadataGenerator
 import org.scalatest.BeforeAndAfter
 import org.scalatest.flatspec.AnyFlatSpec
 import org.scalatest.matchers.should.Matchers
@@ -26,6 +27,8 @@ import org.scalatest.matchers.should.Matchers
 import java.nio.charset.StandardCharsets
 import java.util.Base64
 
+import scala.jdk.CollectionConverters._
+
 class WindRoseChartOpDescSpec extends AnyFlatSpec with BeforeAndAfter with 
Matchers {
 
   var opDesc: WindRoseChartOpDesc = _
@@ -77,4 +80,21 @@ class WindRoseChartOpDescSpec extends AnyFlatSpec with 
BeforeAndAfter with Match
     assert(carries(code, "theta_col"))
     code should include("class ProcessTableOperator(UDFTableOperator)")
   }
+
+  "WindRoseChartOpDesc's generated schema" should
+    "constrain the radial values to numeric and leave the angle unconstrained" 
in {
+    // The angle is a direction label — "N", "NE" — so a rule there would 
reject
+    // the ordinary case. Only the wedge length has to be a number.
+    val schema = 
OperatorMetadataGenerator.generateOperatorJsonSchema(classOf[WindRoseChartOpDesc])
+    val rules = schema.path("attributeTypeRules")
+    rules.fieldNames().asScala.toSet shouldBe Set("rColumn")
+
+    // The key has to name a property the form actually renders; one that names
+    // nothing parses fine and constrains nothing.
+    schema.path("properties").has("rColumn") shouldBe true
+
+    val allowed = 
rules.path("rColumn").path("enum").elements().asScala.map(_.asText()).toSet
+    allowed shouldBe Set("integer", "long", "double")
+    allowed should not contain "string"
+  }
 }

Reply via email to