aminghadersohi commented on code in PR #38402:
URL: https://github.com/apache/superset/pull/38402#discussion_r2931761153
##########
superset/mcp_service/chart/chart_utils.py:
##########
@@ -916,12 +958,24 @@ def _mixed_timeseries_what(config:
MixedTimeseriesChartConfig) -> str:
return f"{primary} + {secondary}"
+def _handlebars_chart_what(config: HandlebarsChartConfig) -> str:
+ """Build the 'what' portion for a handlebars chart name."""
+ if config.query_mode == "raw" and config.columns:
+ cols = ", ".join(col.name for col in config.columns[:3])
+ return f"Handlebars – {cols}"
+ elif config.metrics:
+ metrics = ", ".join(col.name for col in config.metrics[:3])
+ return f"Handlebars – {metrics}"
Review Comment:
Valid. Changed to parentheses: `Handlebars (col1, col2)` so context appends
cleanly. Fixed in 314d439.
##########
superset/mcp_service/chart/schemas.py:
##########
@@ -664,6 +664,88 @@ class MixedTimeseriesChartConfig(BaseModel):
filters: List[FilterConfig] | None = Field(None, description="Filters to
apply")
+class HandlebarsChartConfig(BaseModel):
+ model_config = ConfigDict(extra="forbid")
+
+ chart_type: Literal["handlebars"] = Field(
+ ...,
+ description=(
+ "Chart type discriminator - MUST be 'handlebars' for custom HTML "
+ "template charts. Handlebars charts render query results using "
+ "Handlebars templates, enabling fully custom layouts like KPI
cards, "
+ "leaderboards, and formatted reports."
+ ),
+ )
+ handlebars_template: str = Field(
+ ...,
+ description=(
+ "Handlebars HTML template string. Data is available as {{data}}
array. "
+ "Built-in helpers: {{dateFormat val format='YYYY-MM-DD'}}, "
+ "{{formatNumber val}}, {{stringify obj}}. "
+ "Example: '<ul>{{#each data}}<li>{{this.name}}:
{{this.value}}</li>"
+ "{{/each}}</ul>'"
+ ),
+ min_length=1,
+ max_length=50000,
+ )
+ query_mode: Literal["aggregate", "raw"] = Field(
+ "aggregate",
+ description=(
+ "Query mode: 'aggregate' groups data with metrics, "
+ "'raw' returns individual rows"
+ ),
+ )
+ columns: list[ColumnRef] | None = Field(
+ None,
+ description=(
+ "Columns to display in raw mode (query_mode='raw'). "
+ "Each column specifies a column name to include in the query
results."
+ ),
+ )
+ groupby: list[ColumnRef] | None = Field(
+ None,
+ description=(
+ "Columns to group by in aggregate mode (query_mode='aggregate'). "
+ "These become the dimensions for aggregation."
+ ),
+ )
+ metrics: list[ColumnRef] | None = Field(
+ None,
+ description=(
+ "Metrics to aggregate in aggregate mode. "
+ "Each must have an aggregate function (e.g., SUM, COUNT)."
+ ),
+ )
+ filters: list[FilterConfig] | None = Field(None, description="Filters to
apply")
+ row_limit: int = Field(
+ 1000,
+ description="Maximum number of rows",
+ ge=1,
+ le=50000,
+ )
+ order_desc: bool = Field(True, description="Sort in descending order")
+ style_template: str | None = Field(
+ None,
+ description="Optional CSS styles to apply to the rendered template",
+ max_length=10000,
+ )
+
+ @model_validator(mode="after")
+ def validate_query_fields(self) -> "HandlebarsChartConfig":
+ """Validate that the right fields are provided for the query mode."""
+ if self.query_mode == "raw" and not self.columns:
+ raise ValueError(
+ "Handlebars chart in 'raw' query mode requires 'columns'
field. "
+ "Specify which columns to include in the query results."
+ )
Review Comment:
Valid. Validator now raises `ValueError` if `metrics` or `groupby` provided
in raw mode. Fixed in 314d439.
##########
superset/mcp_service/chart/schemas.py:
##########
@@ -664,6 +664,88 @@ class MixedTimeseriesChartConfig(BaseModel):
filters: List[FilterConfig] | None = Field(None, description="Filters to
apply")
+class HandlebarsChartConfig(BaseModel):
+ model_config = ConfigDict(extra="forbid")
+
+ chart_type: Literal["handlebars"] = Field(
+ ...,
+ description=(
+ "Chart type discriminator - MUST be 'handlebars' for custom HTML "
+ "template charts. Handlebars charts render query results using "
+ "Handlebars templates, enabling fully custom layouts like KPI
cards, "
+ "leaderboards, and formatted reports."
+ ),
+ )
+ handlebars_template: str = Field(
+ ...,
+ description=(
+ "Handlebars HTML template string. Data is available as {{data}}
array. "
+ "Built-in helpers: {{dateFormat val format='YYYY-MM-DD'}}, "
+ "{{formatNumber val}}, {{stringify obj}}. "
+ "Example: '<ul>{{#each data}}<li>{{this.name}}:
{{this.value}}</li>"
+ "{{/each}}</ul>'"
+ ),
+ min_length=1,
+ max_length=50000,
+ )
+ query_mode: Literal["aggregate", "raw"] = Field(
+ "aggregate",
+ description=(
+ "Query mode: 'aggregate' groups data with metrics, "
+ "'raw' returns individual rows"
+ ),
+ )
+ columns: list[ColumnRef] | None = Field(
+ None,
+ description=(
+ "Columns to display in raw mode (query_mode='raw'). "
+ "Each column specifies a column name to include in the query
results."
+ ),
+ )
+ groupby: list[ColumnRef] | None = Field(
+ None,
+ description=(
+ "Columns to group by in aggregate mode (query_mode='aggregate'). "
+ "These become the dimensions for aggregation."
+ ),
+ )
+ metrics: list[ColumnRef] | None = Field(
+ None,
+ description=(
+ "Metrics to aggregate in aggregate mode. "
+ "Each must have an aggregate function (e.g., SUM, COUNT)."
+ ),
+ )
+ filters: list[FilterConfig] | None = Field(None, description="Filters to
apply")
+ row_limit: int = Field(
+ 1000,
+ description="Maximum number of rows",
+ ge=1,
+ le=50000,
+ )
+ order_desc: bool = Field(True, description="Sort in descending order")
+ style_template: str | None = Field(
+ None,
+ description="Optional CSS styles to apply to the rendered template",
+ max_length=10000,
+ )
+
+ @model_validator(mode="after")
+ def validate_query_fields(self) -> "HandlebarsChartConfig":
+ """Validate that the right fields are provided for the query mode."""
+ if self.query_mode == "raw" and not self.columns:
+ raise ValueError(
+ "Handlebars chart in 'raw' query mode requires 'columns'
field. "
+ "Specify which columns to include in the query results."
+ )
+ if self.query_mode == "aggregate" and not self.metrics:
+ raise ValueError(
+ "Handlebars chart in 'aggregate' query mode requires 'metrics'
field. "
+ "Specify at least one metric with an aggregate function."
+ )
Review Comment:
Valid. Validator now checks every metric in aggregate mode has an
`aggregate` function set. Fixed in 314d439.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]