Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi closed pull request #38416: feat(mcp): add popularity_score computed field for charts, dashboards, datasets URL: https://github.com/apache/superset/pull/38416 -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4252950430 Closing — replacing with a new approach using a DB-backed popularity_weights table with SQL-join scoring instead of hardcoded Python-side computation. This addresses the logs table gap in Preset production. -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4240012200 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r3075904347
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,393 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table.
+
+Degrades gracefully when the logs table is empty (e.g. Preset production
+where events are routed to external systems) or inaccessible.
+"""
+try:
+view_counts = (
+db.session.query(id_column,
sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm
>= cutoff)
+.group_by(id_column)
+.all()
+)
+except sa.exc.SQLAlchemyError:
+db.session.rollback()
+logger.warning(
+"Failed to query logs table for view counts (action=%s). "
+"Scoring will proceed without view data.",
+action,
+exc_info=True,
+)
+return
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to sc
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r3075889982
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,390 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table.
+
+Degrades gracefully when the logs table is empty (e.g. Preset production
+where events are routed to external systems) or inaccessible.
+"""
+try:
+view_counts = (
+db.session.query(id_column,
sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm
>= cutoff)
+.group_by(id_column)
+.all()
+)
+except sa.exc.SQLAlchemyError:
+logger.warning(
+"Failed to query logs table for view counts (action=%s). "
+"Scoring will proceed without view data.",
+action,
+exc_info=True,
+)
+return
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+days: Number of days for
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r3075891459
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,393 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table.
+
+Degrades gracefully when the logs table is empty (e.g. Preset production
+where events are routed to external systems) or inaccessible.
+"""
+try:
+view_counts = (
+db.session.query(id_column,
sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm
>= cutoff)
+.group_by(id_column)
+.all()
+)
+except sa.exc.SQLAlchemyError:
+db.session.rollback()
+logger.warning(
+"Failed to query logs table for view counts (action=%s). "
+"Scoring will proceed without view data.",
+action,
+exc_info=True,
+)
+return
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r3075881781
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,393 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table.
+
+Degrades gracefully when the logs table is empty (e.g. Preset production
+where events are routed to external systems) or inaccessible.
+"""
+try:
+view_counts = (
+db.session.query(id_column,
sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm
>= cutoff)
+.group_by(id_column)
+.all()
+)
+except sa.exc.SQLAlchemyError:
+db.session.rollback()
+logger.warning(
+"Failed to query logs table for view counts (action=%s). "
+"Scoring will proceed without view data.",
+action,
+exc_info=True,
+)
+return
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to sc
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4239576919 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r3075461650
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,390 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table.
+
+Degrades gracefully when the logs table is empty (e.g. Preset production
+where events are routed to external systems) or inaccessible.
+"""
+try:
+view_counts = (
+db.session.query(id_column,
sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm
>= cutoff)
+.group_by(id_column)
+.all()
+)
+except sa.exc.SQLAlchemyError:
+logger.warning(
+"Failed to query logs table for view counts (action=%s). "
+"Scoring will proceed without view data.",
+action,
+exc_info=True,
+)
+return
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+days: Number of days for
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4239093149 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4238507519 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
Copilot commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r3074538760
##
superset/mcp_service/dashboard/tool/list_dashboards.py:
##
@@ -165,3 +212,87 @@ def _serialize_dashboard(
return result.model_dump(
mode="json", context={"select_columns": columns_to_filter}
)
+
+
+def _list_dashboards_by_popularity(
+request: ListDashboardsRequest,
+dao_class: Any,
+serializer: Callable[..., dict[str, Any] | None],
+all_columns: list[str],
+ctx: Context,
+) -> DashboardList:
+"""Two-pass listing: sort all matching dashboards by popularity score."""
+from superset.mcp_service.common.schema_discovery import (
+DASHBOARD_SORTABLE_COLUMNS,
+)
+
+sorted_ids, scores, total_count = get_popularity_sorted_ids(
+compute_fn=compute_dashboard_popularity,
+dao_class=dao_class,
+filters=request.filters,
+search=request.search,
+search_columns=DASHBOARD_SEARCH_COLUMNS,
+order_direction=request.order_direction,
+)
+
+# Apply pagination to sorted IDs
+page = max(request.page - 1, 0)
+page_size = request.page_size
+start = page * page_size
+end = start + page_size
+
+# Fetch full models for page IDs
+if page_ids := sorted_ids[start:end]:
+items = dao_class.find_by_ids(page_ids)
+id_to_item = {item.id: item for item in items}
+ordered_items = [id_to_item[pid] for pid in page_ids if pid in
id_to_item]
+else:
+ordered_items = []
Review Comment:
Similar to charts: dashboards may serialize relationships like `owners`,
`tags`, `roles`, and `slices` (`chart_count`/`charts`), which can lazy-load and
produce N+1 queries when using the popularity two-pass path. Consider applying
`query_options` (e.g., `subqueryload(Dashboard.owners)`,
`subqueryload(Dashboard.tags)`, `subqueryload(Dashboard.roles)`,
`subqueryload(Dashboard.slices)`) via the updated `find_by_ids` API.
##
superset/mcp_service/common/schema_discovery.py:
##
@@ -353,8 +361,9 @@ def get_columns_from_model(
"schema",
"changed_on",
"created_on",
+"popularity_score",
]
-DATASET_SEARCH_COLUMNS = ["table_name", "description"]
+DATASET_SEARCH_COLUMNS = ["schema", "sql", "table_name", "uuid"]
Review Comment:
This change removes `description` from dataset search columns (previously
`['table_name', 'description']`). If this removal isn’t intentional, consider
keeping `description` in the search column set for backwards compatibility and
better discoverability (e.g., `['table_name', 'description', 'schema', 'sql',
'uuid']`). If it is intentional, it would be helpful to reflect that in the PR
description/tool docs since it changes search behavior.
##
superset/mcp_service/chart/tool/list_charts.py:
##
@@ -37,7 +41,14 @@
ListChartsRequest,
serialize_chart_object,
)
+from superset.mcp_service.common.popularity import (
+attach_popularity_scores,
+compute_chart_popularity,
+get_popularity_sorted_ids,
+)
from superset.mcp_service.mcp_core import ModelListCore
+from superset.mcp_service.system.schemas import PaginationInfo
+from superset.mcp_service.utils.schema_utils import parse_request
Review Comment:
`parse_request` is imported but not referenced in the shown implementation.
If it’s no longer needed, please remove it (and similarly in the other list
tools where it was added) to keep module dependencies and lint noise down.
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,390 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r3074524234
##
superset/mcp_service/dashboard/tool/list_dashboards.py:
##
@@ -165,3 +212,87 @@ def _serialize_dashboard(
return result.model_dump(
mode="json", context={"select_columns": columns_to_filter}
)
+
+
+def _list_dashboards_by_popularity(
+request: ListDashboardsRequest,
+dao_class: Any,
+serializer: Callable[..., dict[str, Any] | None],
+all_columns: list[str],
+ctx: Context,
+) -> DashboardList:
+"""Two-pass listing: sort all matching dashboards by popularity score."""
+from superset.mcp_service.common.schema_discovery import (
+DASHBOARD_SORTABLE_COLUMNS,
+)
+
+sorted_ids, scores, total_count = get_popularity_sorted_ids(
+compute_fn=compute_dashboard_popularity,
+dao_class=dao_class,
+filters=request.filters,
+search=request.search,
+search_columns=DASHBOARD_SEARCH_COLUMNS,
+order_direction=request.order_direction,
+)
+
+# Apply pagination to sorted IDs
+page = max(request.page - 1, 0)
+page_size = request.page_size
+start = page * page_size
+end = start + page_size
+
+# Fetch full models for page IDs
+if page_ids := sorted_ids[start:end]:
+items = dao_class.find_by_ids(page_ids)
Review Comment:
**Suggestion:** The popularity-sort branch loads dashboards by ID without
eager-loading related collections, while serialization reads `slices`,
`owners`, `tags`, and `roles` per dashboard. This creates N+1 queries that grow
linearly with page size and can cause slow or timeout-prone responses. Add
query options when calling `find_by_ids` to prefetch these relationships.
[performance]
Severity Level: Major ⚠️
```mdx
- ⚠️ MCP `list_dashboards` popularity-sorted calls issue N+1 DB queries.
- ⚠️ Popularity-sorted dashboard listings may slow under larger page sizes.
```
```suggestion
from sqlalchemy.orm import subqueryload
from superset.models.dashboard import Dashboard
eager_options = [
subqueryload(Dashboard.slices),
subqueryload(Dashboard.owners),
subqueryload(Dashboard.tags),
subqueryload(Dashboard.roles),
]
items = dao_class.find_by_ids(page_ids, query_options=eager_options)
```
Steps of Reproduction ✅
```mdx
1. Call the MCP `list_dashboards` tool (defined in
`superset/mcp_service/dashboard/tool/list_dashboards.py:86-95`) with
`order_column="popularity_score"` and a non‑trivial `page_size` (for example
50). This
causes the main `list_dashboards` function to route the request into
`_list_dashboards_by_popularity` when `request.order_column ==
"popularity_score"` (lines
136-141).
2. Inside `_list_dashboards_by_popularity`
(`superset/mcp_service/dashboard/tool/list_dashboards.py:217-224`), the
helper computes
`sorted_ids` and then applies pagination (`page`, `start`, `end`) at lines
238-242. For
the current page, it builds `page_ids = sorted_ids[start:end]` and executes
`items =
dao_class.find_by_ids(page_ids)` at line 246, which calls
`BaseDAO.find_by_ids`
(`superset/daos/base.py:11-29`) without any `query_options`, returning
`Dashboard` ORM
instances with relationships still lazily loaded.
3. Still in `_list_dashboards_by_popularity`, the code iterates over the
loaded dashboards
and serializes each via `obj = serializer(item, select_columns)` at lines
247-248. The
`serializer` passed in from `list_dashboards` is `_serialize_dashboard`
defined at lines
125-129, which simply delegates to `serialize_dashboard_object(obj)` from
`superset/mcp_service/dashboard/schemas.py:16-33`.
4. In `serialize_dashboard_object`
(`superset/mcp_service/dashboard/schemas.py:16-79`),
the serializer accesses relationship attributes on each `Dashboard`
instance: it computes
`chart_count=len(getattr(dashboard, "slices", []))` (line 61), iterates
`owners=[... for
owner in getattr(dashboard, "owners", [])]` (lines 62-68), `tags=[... for
tag in
getattr(dashboard, "tags", [])]` (lines 69-74), and `roles=[... for role in
getattr(dashboard, "roles", [])]` (lines 75-79). These attributes correspond
to SQLAlchemy
relationships defined in `superset/models/dashboard.py:15-31` and line 46,
which use the
default lazy loading strategy (`relationship(...)` without eager options).
As a result,
each access to `.slices`, `.owners`, `.tags`, and `.roles` on every
dashboard returned by
`find_by_ids` triggers separate SELECT queries per dashboard, on top of the
initial
`find_by_ids` query, producing an N+1 (1 main + ~4×N) query pattern that
scales linearly
with `page_size`. This is consistent with existing code that explicitly uses
`subqueryl
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r3074522551
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,390 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table.
+
+Degrades gracefully when the logs table is empty (e.g. Preset production
+where events are routed to external systems) or inaccessible.
+"""
+try:
+view_counts = (
+db.session.query(id_column,
sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm
>= cutoff)
+.group_by(id_column)
+.all()
+)
+except sa.exc.SQLAlchemyError:
+logger.warning(
+"Failed to query logs table for view counts (action=%s). "
+"Scoring will proceed without view data.",
+action,
+exc_info=True,
+)
+return
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+days: Nu
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4117261946 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2980541558 ## superset/mcp_service/dataset/tool/get_dataset_info.py: ## @@ -117,6 +118,11 @@ async def get_dataset_info( result = tool.run_tool(request.identifier) if isinstance(result, DatasetInfo): +# Compute popularity_score for single dataset retrieval +if result.id is not None: +scores = compute_dataset_popularity([result.id]) +result.popularity_score = scores.get(result.id, 0.0) Review Comment: Fixed — popularity_score is now computed only when select_columns explicitly includes 'popularity_score' or when select_columns is empty (return everything). A new select_columns field on GetDatasetInfoRequest enables this opt-in behavior. ## superset/mcp_service/dataset/tool/get_dataset_info.py: ## @@ -117,6 +118,17 @@ async def get_dataset_info( result = tool.run_tool(request.identifier) if isinstance(result, DatasetInfo): +# Compute popularity_score for single dataset retrieval +# Isolated so a failure here doesn't prevent returning dataset info +if result.id is not None: +try: +scores = compute_dataset_popularity([result.id]) +result.popularity_score = scores.get(result.id, 0.0) +except (ValueError, TypeError, AttributeError) as e: +await ctx.warning( +"Failed to compute popularity score: %s" % (str(e),) +) Review Comment: Fixed — added SQLAlchemyError (from sqlalchemy.exc) to the except clause, so database errors during popularity computation are caught and logged as warnings rather than crashing the tool. ## superset/mcp_service/dataset/tool/list_datasets.py: ## @@ -124,32 +130,62 @@ def _serialize_dataset( """Serialize dataset (filtering via model_serializer).""" return serialize_dataset_object(obj) -# Create tool with standard serialization -tool = ModelListCore( -dao_class=DatasetDAO, -output_schema=DatasetInfo, -item_serializer=_serialize_dataset, -filter_type=DatasetFilter, -default_columns=DEFAULT_DATASET_COLUMNS, -search_columns=["schema", "sql", "table_name", "uuid"], -list_field_name="datasets", -output_list_schema=DatasetList, -all_columns=all_columns, -sortable_columns=DATASET_SORTABLE_COLUMNS, -logger=logger, +# Preserve original select_columns before any mutation for response filtering +original_select_columns = ( +list(request.select_columns) if request.select_columns else None ) -with event_logger.log_context(action="mcp.list_datasets.query"): -result = tool.run_tool( -filters=request.filters, -search=request.search, -select_columns=request.select_columns, -order_column=request.order_column, -order_direction=request.order_direction, -page=max(request.page - 1, 0), -page_size=request.page_size, +# Two-pass approach when sorting by popularity_score +if request.order_column == "popularity_score": +with event_logger.log_context(action="mcp.list_datasets.popularity_sort"): +result = _list_datasets_by_popularity( +request, DatasetDAO, _serialize_dataset, all_columns, ctx +) +else: +# Create tool with standard serialization +list_core = ModelListCore( +dao_class=DatasetDAO, +output_schema=DatasetInfo, +item_serializer=_serialize_dataset, +filter_type=DatasetFilter, +default_columns=DEFAULT_DATASET_COLUMNS, +search_columns=DATASET_SEARCH_COLUMNS, +list_field_name="datasets", +output_list_schema=DatasetList, +all_columns=all_columns, +sortable_columns=DATASET_SORTABLE_COLUMNS, +logger=logger, ) +# Strip computed fields before passing to DAO query +dao_columns = request.select_columns +if dao_columns: +dao_columns = [c for c in dao_columns if c != "popularity_score"] +# Ensure id is loaded when popularity_score was requested +# (scores are keyed by id) +if ( +"popularity_score" in request.select_columns +and "id" not in dao_columns +): +dao_columns = ["id"] + dao_columns + +with event_logger.log_context(action="mcp.list_datase
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4071370855 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2943419643
##
superset/mcp_service/dashboard/tool/list_dashboards.py:
##
@@ -155,3 +196,85 @@ def _serialize_dashboard(
return result.model_dump(
mode="json", context={"select_columns": columns_to_filter}
)
+
+
+def _list_dashboards_by_popularity(
+request: ListDashboardsRequest,
+dao_class: Any,
+serializer: Callable[..., dict[str, Any] | None],
+all_columns: list[str],
+ctx: Context,
+) -> DashboardList:
+"""Two-pass listing: sort all matching dashboards by popularity score."""
+from superset.mcp_service.common.schema_discovery import (
+DASHBOARD_SORTABLE_COLUMNS,
+)
+
+sorted_ids, scores, total_count = get_popularity_sorted_ids(
+compute_fn=compute_dashboard_popularity,
+dao_class=dao_class,
+filters=request.filters,
+search=request.search,
+search_columns=DASHBOARD_SEARCH_COLUMNS,
+order_direction=request.order_direction,
+)
+
+# Apply pagination to sorted IDs
+page = max(request.page - 1, 0)
+page_size = request.page_size
+start = page * page_size
+end = start + page_size
+
+# Fetch full models for page IDs
+if page_ids := sorted_ids[start:end]:
+items = dao_class.find_by_ids(page_ids)
+id_to_item = {item.id: item for item in items}
+ordered_items = [id_to_item[pid] for pid in page_ids if pid in
id_to_item]
+else:
+ordered_items = []
+
+# Serialize - preserve the original request columns for response filtering
+columns_requested = request.select_columns or DEFAULT_DASHBOARD_COLUMNS
+# Include popularity_score in response when sorting by it, even if not
+# explicitly in select_columns (so clients can see the sort key)
+if "popularity_score" not in columns_requested:
+columns_requested = list(columns_requested) + ["popularity_score"]
+# Expand select_columns for internal loading (need popularity_score for
+# attach step)
+select_columns = list(columns_requested)
+
+dash_objs = []
+for item in ordered_items:
+obj = serializer(item, select_columns)
+if obj is not None:
+dash_objs.append(obj)
+
+attach_popularity_scores(dash_objs, scores)
+
+total_pages = (total_count + page_size - 1) // page_size if page_size > 0
else 0
+pagination_info = PaginationInfo(
+page=page,
+page_size=page_size,
+total_count=total_count,
+total_pages=total_pages,
+has_next=page < total_pages - 1,
+has_previous=page > 0,
+)
+
+return DashboardList(
+dashboards=dash_objs,
+count=len(dash_objs),
+total_count=total_count,
+page=page,
+page_size=page_size,
+total_pages=total_pages,
+has_previous=page > 0,
+has_next=page < total_pages - 1,
+columns_requested=columns_requested,
+columns_loaded=select_columns,
+columns_available=all_columns,
+sortable_columns=DASHBOARD_SORTABLE_COLUMNS,
+filters_applied=request.filters if isinstance(request.filters, list)
else [],
+pagination=pagination_info,
+timestamp=datetime.now(timezone.utc),
+)
Review Comment:
**Suggestion:** The popularity-sorted path returns pagination with a 0-based
page index, while the normal listing path and request schema use 1-based pages.
This causes inconsistent API behavior and breaks clients that rely on `page=1`
for the first page. Convert the response page value in this branch to 1-based
before building `PaginationInfo` and `DashboardList`. [off-by-one]
Severity Level: Major ⚠️
```mdx
- ⚠️ `list_dashboards` popularity sort returns wrong page numbering.
- ❌ Clients mis-handle pagination boundaries on sorted dashboard results.
```
```suggestion
total_pages = (total_count + page_size - 1) // page_size if page_size >
0 else 0
page_1based = page + 1
pagination_info = PaginationInfo(
page=page_1based,
page_size=page_size,
total_count=total_count,
total_pages=total_pages,
has_next=page < total_pages - 1,
has_previous=page > 0,
)
return DashboardList(
dashboards=dash_objs,
count=len(dash_objs),
total_count=total_count,
page=page_1based,
page_size=page_size,
total_pages=total_pages,
has_previous=page > 0,
has_next=page < total_pages - 1,
columns_requested=columns_requested,
columns_loaded=select_columns,
columns_available=all_columns,
sortable_columns=DASHBOARD_SORTABLE_COLUMNS,
filters_applied=request.filters if isinstance(request.filters, list)
else [],
pagination=pagination_
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2933372228 ## superset/mcp_service/dataset/tool/list_datasets.py: ## @@ -151,10 +194,11 @@ def _serialize_dataset( ) ) -# Apply field filtering via serialization context -# Always use columns_requested (either explicit select_columns or defaults) -# This triggers DatasetInfo._filter_fields_by_context for each dataset -columns_to_filter = result.columns_requested +# Build response filtering from the original request columns when the +# user explicitly specified select_columns (to avoid leaking internally +# injected columns like 'id'). Fall back to result.columns_requested +# (which holds DAO defaults) when no explicit columns were requested. +columns_to_filter = original_select_columns or result.columns_requested Review Comment: Logic Bug: Popularity Score Response Filtering When order_column is 'popularity_score' and it's not in the original select_columns, the current logic uses original_select_columns for filtering, which excludes popularity_score from the response. However, the code comment in _list_datasets_by_popularity indicates it should be included so clients can see the sort key. Change to: columns_to_filter = result.columns_requested if request.order_column == 'popularity_score' else original_select_columns or result.columns_requested Code suggestion Check the AI-generated fix before applying ``` --- a/superset/mcp_service/dataset/tool/list_datasets.py +++ b/superset/mcp_service/dataset/tool/list_datasets.py @@ -197,7 +197,7 @@ # Build response filtering from the original request columns when the # user explicitly specified select_columns (to avoid leaking internally # injected columns like 'id'). Fall back to result.columns_requested # (which holds DAO defaults) when no explicit columns were requested. -columns_to_filter = original_select_columns or result.columns_requested +columns_to_filter = result.columns_requested if request.order_column == "popularity_score" else original_select_columns or result.columns_requested await ctx.debug( "Applying field filtering via serialization context: columns=%s" % (columns_to_filter,) ) ``` Code Review Run #a054b2 --- Should Bito avoid suggestions like this for future reviews? (https://alpha.bito.ai/home/ai-agents/review-rules>Manage Rules) - [ ] Yes, avoid them -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4057421313 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2932607468
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,368 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table."""
+view_counts = (
+db.session.query(id_column, sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm >=
cutoff)
+.group_by(id_column)
+.all()
+)
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+days: Number of days for view count window
+
+Returns:
+Dict mapping chart_id -> popularity score
+"""
+if not chart_ids:
+return {}
+
+scores = _init_scores(chart_ids)
+cutoff = datetime.now(timezone.utc) - timedelta(days=days)
+
+_add_view_scores(
+scores, "mount_explorer", Log.slice_id, chart_ids, cutoff, VIEW_WEIGHT
+)
+_add_fav_scores(scores, "slice", chart_ids, FAV_WEIGHT)
+
+# Dashboard count (how man
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4056370459 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2931948374 ## superset/mcp_service/dashboard/tool/list_dashboards.py: ## @@ -108,34 +124,53 @@ def _serialize_dashboard( """Serialize dashboard object (field filtering handled by model_serializer).""" return serialize_dashboard_object(obj) -tool = ModelListCore( -dao_class=DashboardDAO, -output_schema=DashboardInfo, -item_serializer=_serialize_dashboard, -filter_type=DashboardFilter, -default_columns=DEFAULT_DASHBOARD_COLUMNS, -search_columns=[ -"dashboard_title", -"slug", -"uuid", -], -list_field_name="dashboards", -output_list_schema=DashboardList, -all_columns=all_columns, -sortable_columns=DASHBOARD_SORTABLE_COLUMNS, -logger=logger, -) - -with event_logger.log_context(action="mcp.list_dashboards.query"): -result = tool.run_tool( -filters=request.filters, -search=request.search, -select_columns=request.select_columns, -order_column=request.order_column, -order_direction=request.order_direction, -page=max(request.page - 1, 0), -page_size=request.page_size, +# Two-pass approach when sorting by popularity_score +if request.order_column == "popularity_score": +with event_logger.log_context(action="mcp.list_dashboards.popularity_sort"): +result = _list_dashboards_by_popularity( +request, DashboardDAO, _serialize_dashboard, all_columns, ctx +) +else: +list_core = ModelListCore( +dao_class=DashboardDAO, +output_schema=DashboardInfo, +item_serializer=_serialize_dashboard, +filter_type=DashboardFilter, +default_columns=DEFAULT_DASHBOARD_COLUMNS, +search_columns=DASHBOARD_SEARCH_COLUMNS, +list_field_name="dashboards", +output_list_schema=DashboardList, +all_columns=all_columns, +sortable_columns=DASHBOARD_SORTABLE_COLUMNS, +logger=logger, ) + +# Strip computed fields before passing to DAO query +dao_columns = request.select_columns +if dao_columns: +dao_columns = [c for c in dao_columns if c != "popularity_score"] +# Ensure id is loaded when popularity_score was requested +# (scores are keyed by id) +if "popularity_score" in request.select_columns and "id" not in dao_columns: +dao_columns = ["id"] + dao_columns + +with event_logger.log_context(action="mcp.list_dashboards.query"): +result = list_core.run_tool( +filters=request.filters, +search=request.search, +select_columns=dao_columns, +order_column=request.order_column, +order_direction=request.order_direction, +page=max(request.page - 1, 0), +page_size=request.page_size, +) + +# Attach popularity scores if requested in select_columns Review Comment: Understood. The update ensures that when sorting by popularity_score, the field is automatically included in columns_requested for the response, even if not explicitly requested. This lets clients see the sort key. Applied to all three list tools in the latest commit. ## superset/mcp_service/chart/tool/list_charts.py: ## @@ -71,7 +92,7 @@ async def list_charts(request: ListChartsRequest, ctx: Context) -> ChartList: modified time. Sortable columns for order_column: id, slice_name, viz_type, -datasource_name, description, changed_on, created_on +datasource_name, description, changed_on, created_on, popularity_score Review Comment: Yes, removing `datasource_name` from the docstring aligns with the updated code, as it's no longer in the sortable columns list (now `CHART_SEARCH_COLUMNS`). The change prevents misleading documentation. **superset/mcp_service/chart/tool/list_charts.py** ``` -"viz_type", -"description", +"viz_type", +"datasource_name", +"description", ``` -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2931946291 ## superset/mcp_service/dataset/schemas.py: ## @@ -137,6 +137,12 @@ class DatasetInfo(BaseModel): is_favorite: bool | None = Field( None, description="Whether this dataset is favorited by the current user" ) +popularity_score: float | None = Field( +None, +description="Popularity score based on chart usage, certification, " +"and recency. Request via select_columns=['popularity_score'] or sort " +"via order_column='popularity_score'.", +) model_config = ConfigDict( Review Comment: Yes, the suggestion is valid — it adds the necessary import and logic to compute and assign the popularity score for single dataset retrievals, ensuring consistency with how popularity is handled in list operations. -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2931939149 ## superset/mcp_service/common/schema_discovery.py: ## @@ -449,6 +465,13 @@ def get_columns_from_model( "charts": ColumnMetadata( name="charts", description="Charts in dashboard", type="list", is_default=False ), +"popularity_score": ColumnMetadata( +name="popularity_score", +description="Popularity score based on views, favorites, chart count, " +"and recency", Review Comment: This question isn’t related to the pull request. I can only help with questions about the PR’s code or comments. -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2931934573 ## superset/mcp_service/common/schema_discovery.py: ## @@ -449,6 +465,13 @@ def get_columns_from_model( "charts": ColumnMetadata( name="charts", description="Charts in dashboard", type="list", is_default=False ), +"popularity_score": ColumnMetadata( +name="popularity_score", +description="Popularity score based on views, favorites, chart count, " +"and recency", Review Comment: Valid. Updated the dashboard `popularity_score` description to include certification and published status bonuses. Fixed in the latest commit. ## superset/mcp_service/dataset/schemas.py: ## @@ -137,6 +137,12 @@ class DatasetInfo(BaseModel): is_favorite: bool | None = Field( None, description="Whether this dataset is favorited by the current user" ) +popularity_score: float | None = Field( +None, +description="Popularity score based on chart usage, certification, " +"and recency. Request via select_columns=['popularity_score'] or sort " +"via order_column='popularity_score'.", +) model_config = ConfigDict( Review Comment: Valid. Added `compute_dataset_popularity()` call in `get_dataset_info` so single-dataset retrievals now populate `popularity_score`. Fixed in the latest commit. ## superset/mcp_service/dashboard/tool/list_dashboards.py: ## @@ -108,34 +124,53 @@ def _serialize_dashboard( """Serialize dashboard object (field filtering handled by model_serializer).""" return serialize_dashboard_object(obj) -tool = ModelListCore( -dao_class=DashboardDAO, -output_schema=DashboardInfo, -item_serializer=_serialize_dashboard, -filter_type=DashboardFilter, -default_columns=DEFAULT_DASHBOARD_COLUMNS, -search_columns=[ -"dashboard_title", -"slug", -"uuid", -], -list_field_name="dashboards", -output_list_schema=DashboardList, -all_columns=all_columns, -sortable_columns=DASHBOARD_SORTABLE_COLUMNS, -logger=logger, -) - -with event_logger.log_context(action="mcp.list_dashboards.query"): -result = tool.run_tool( -filters=request.filters, -search=request.search, -select_columns=request.select_columns, -order_column=request.order_column, -order_direction=request.order_direction, -page=max(request.page - 1, 0), -page_size=request.page_size, +# Two-pass approach when sorting by popularity_score +if request.order_column == "popularity_score": +with event_logger.log_context(action="mcp.list_dashboards.popularity_sort"): +result = _list_dashboards_by_popularity( +request, DashboardDAO, _serialize_dashboard, all_columns, ctx +) +else: +list_core = ModelListCore( +dao_class=DashboardDAO, +output_schema=DashboardInfo, +item_serializer=_serialize_dashboard, +filter_type=DashboardFilter, +default_columns=DEFAULT_DASHBOARD_COLUMNS, +search_columns=DASHBOARD_SEARCH_COLUMNS, +list_field_name="dashboards", +output_list_schema=DashboardList, +all_columns=all_columns, +sortable_columns=DASHBOARD_SORTABLE_COLUMNS, +logger=logger, ) + +# Strip computed fields before passing to DAO query +dao_columns = request.select_columns +if dao_columns: +dao_columns = [c for c in dao_columns if c != "popularity_score"] +# Ensure id is loaded when popularity_score was requested +# (scores are keyed by id) +if "popularity_score" in request.select_columns and "id" not in dao_columns: +dao_columns = ["id"] + dao_columns + +with event_logger.log_context(action="mcp.list_dashboards.query"): +result = list_core.run_tool( +filters=request.filters, +search=request.search, +select_columns=dao_columns, +order_column=request.order_column, +order_direction=request.order_direction, +page=max(request.page - 1, 0), +page_size=request.page_size, +) + +# Attach popularity scores if requested in select_columns Review Comment: Valid. When sorting by popularity_score, the response now includes `popularity_score` in `columns_requested` even if the user didn't explicitly request it, so clients can see the sort key. Applied to all three list tools. Fixed in the latest commit. ## superset/mcp_service/chart/tool/list_charts.py: ## @@ -71,7 +92,7 @@ async def list_charts(request: ListCharts
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2931761389
##
superset/mcp_service/dataset/tool/list_datasets.py:
##
@@ -152,9 +190,16 @@ def _serialize_dataset(
)
# Apply field filtering via serialization context
-# Always use columns_requested (either explicit select_columns or
defaults)
-# This triggers DatasetInfo._filter_fields_by_context for each dataset
columns_to_filter = result.columns_requested
+# Re-add popularity_score if it was originally requested
+# (it was stripped before the DAO query since it's computed)
+if (
+request.select_columns
+and "popularity_score" in request.select_columns
+and columns_to_filter
+and "popularity_score" not in columns_to_filter
+):
+columns_to_filter = list(columns_to_filter) + ["popularity_score"]
Review Comment:
Fixed in bad796b. Response filtering now uses the original
`request.select_columns` (saved before DAO mutation) instead of
`result.columns_requested` (which reflected DAO-expanded columns with injected
`id`). When no explicit columns requested, falls back to
`result.columns_requested` with DAO defaults.
##
superset/mcp_service/dataset/tool/list_datasets.py:
##
@@ -176,3 +221,78 @@ def _serialize_dataset(
)
)
raise
+
+
+def _list_datasets_by_popularity(
+request: ListDatasetsRequest,
+dao_class: Any,
+serializer: Callable[..., dict[str, Any] | None],
+all_columns: list[str],
+ctx: Context,
+) -> DatasetList:
+"""Two-pass listing: sort all matching datasets by popularity score."""
+from superset.mcp_service.common.schema_discovery import
DATASET_SORTABLE_COLUMNS
+
+sorted_ids, scores, total_count = get_popularity_sorted_ids(
+compute_fn=compute_dataset_popularity,
+dao_class=dao_class,
+filters=request.filters,
+search=request.search,
+search_columns=DATASET_SEARCH_COLUMNS,
+order_direction=request.order_direction,
+)
+
+# Apply pagination to sorted IDs
+page = max(request.page - 1, 0)
+page_size = request.page_size
+start = page * page_size
+end = start + page_size
+
+# Fetch full models for page IDs
+if page_ids := sorted_ids[start:end]:
+items = dao_class.find_by_ids(page_ids)
+id_to_item = {item.id: item for item in items}
+ordered_items = [id_to_item[pid] for pid in page_ids if pid in
id_to_item]
+else:
+ordered_items = []
+
+# Serialize
+select_columns = request.select_columns or DEFAULT_DATASET_COLUMNS
+if "popularity_score" not in select_columns:
+select_columns = list(select_columns) + ["popularity_score"]
+
+ds_objs = []
+for item in ordered_items:
+obj = serializer(item, select_columns)
+if obj is not None:
+ds_objs.append(obj)
+
+attach_popularity_scores(ds_objs, scores)
+
+total_pages = (total_count + page_size - 1) // page_size if page_size > 0
else 0
+pagination_info = PaginationInfo(
+page=page,
+page_size=page_size,
+total_count=total_count,
+total_pages=total_pages,
+has_next=page < total_pages - 1,
+has_previous=page > 0,
+)
+
+return DatasetList(
+datasets=ds_objs,
+count=len(ds_objs),
+total_count=total_count,
+page=page,
+page_size=page_size,
+total_pages=total_pages,
+has_previous=page > 0,
+has_next=page < total_pages - 1,
+columns_requested=select_columns,
+columns_loaded=select_columns,
+columns_available=all_columns,
+sortable_columns=DATASET_SORTABLE_COLUMNS,
+filters_applied=request.filters if isinstance(request.filters, list)
else [],
+pagination=pagination_info,
+timestamp=datetime.now(timezone.utc),
+)
Review Comment:
Fixed in bad796b. In all three `_list_*_by_popularity` functions,
`columns_requested` now reflects the original `request.select_columns` (or
defaults). The internal expansion for attach step is isolated to
`select_columns`/`columns_loaded` only.
--
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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2931760284 ## superset/mcp_service/chart/tool/list_charts.py: ## @@ -142,9 +188,16 @@ def _serialize_chart( ) # Apply field filtering via serialization context -# Always use columns_requested (either explicit select_columns or defaults) -# This triggers ChartInfo._filter_fields_by_context for each chart columns_to_filter = result.columns_requested +# Re-add popularity_score if it was originally requested +# (it was stripped before the DAO query since it's computed) +if ( +request.select_columns +and "popularity_score" in request.select_columns +and columns_to_filter +and "popularity_score" not in columns_to_filter +): +columns_to_filter = list(columns_to_filter) + ["popularity_score"] Review Comment: Fixed in bad796b. Response filtering now uses the original `request.select_columns` (saved before DAO mutation) instead of `result.columns_requested` (which reflected DAO-expanded columns with injected `id`). When no explicit columns requested, falls back to `result.columns_requested` with DAO defaults. ## superset/mcp_service/dashboard/tool/list_dashboards.py: ## @@ -144,9 +179,16 @@ def _serialize_dashboard( ) # Apply field filtering via serialization context -# Always use columns_requested (either explicit select_columns or defaults) -# This triggers DashboardInfo._filter_fields_by_context for each dashboard columns_to_filter = result.columns_requested +# Re-add popularity_score if it was originally requested +# (it was stripped before the DAO query since it's computed) +if ( +request.select_columns +and "popularity_score" in request.select_columns +and columns_to_filter +and "popularity_score" not in columns_to_filter +): +columns_to_filter = list(columns_to_filter) + ["popularity_score"] Review Comment: Fixed in bad796b. Response filtering now uses the original `request.select_columns` (saved before DAO mutation) instead of `result.columns_requested` (which reflected DAO-expanded columns with injected `id`). When no explicit columns requested, falls back to `result.columns_requested` with DAO defaults. -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4055696099 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2931292560
##
superset/mcp_service/dashboard/tool/list_dashboards.py:
##
@@ -155,3 +196,83 @@ def _serialize_dashboard(
return result.model_dump(
mode="json", context={"select_columns": columns_to_filter}
)
+
+
+def _list_dashboards_by_popularity(
+request: ListDashboardsRequest,
+dao_class: Any,
+serializer: Callable[..., dict[str, Any] | None],
+all_columns: list[str],
+ctx: Context,
+) -> DashboardList:
+"""Two-pass listing: sort all matching dashboards by popularity score."""
+from superset.mcp_service.common.schema_discovery import (
+DASHBOARD_SORTABLE_COLUMNS,
+)
+
+sorted_ids, scores, total_count = get_popularity_sorted_ids(
+compute_fn=compute_dashboard_popularity,
+dao_class=dao_class,
+filters=request.filters,
+search=request.search,
+search_columns=DASHBOARD_SEARCH_COLUMNS,
+order_direction=request.order_direction,
+)
+
+# Apply pagination to sorted IDs
+page = max(request.page - 1, 0)
+page_size = request.page_size
+start = page * page_size
+end = start + page_size
+
+# Fetch full models for page IDs
+if page_ids := sorted_ids[start:end]:
+items = dao_class.find_by_ids(page_ids)
+id_to_item = {item.id: item for item in items}
+ordered_items = [id_to_item[pid] for pid in page_ids if pid in
id_to_item]
+else:
+ordered_items = []
+
+# Serialize - preserve the original request columns for response filtering
+columns_requested = request.select_columns or DEFAULT_DASHBOARD_COLUMNS
Review Comment:
**Suggestion:** When sorting by popularity without explicit
`select_columns`, the response metadata and field filtering omit
`popularity_score`, so clients cannot see the score they explicitly asked to
sort by. Include `popularity_score` in the default requested columns for the
popularity-sort path. [logic error]
Severity Level: Major ⚠️
```mdx
- ⚠️ Popularity-sorted dashboard responses hide computed score field.
- ⚠️ MCP clients cannot display numeric ranking evidence.
```
```suggestion
columns_requested = request.select_columns or (
DEFAULT_DASHBOARD_COLUMNS + ["popularity_score"]
)
```
Steps of Reproduction ✅
```mdx
1. Invoke MCP tool `list_dashboards` with `order_column="popularity_score"`
and no
`select_columns` (real entrypoint is `list_dashboards()` at
`superset/mcp_service/dashboard/tool/list_dashboards.py:82`, registered via
`@tool` at
line 80; same invocation pattern is exercised in tests via
`Client.call_tool(...)` at
`tests/unit_tests/mcp_service/dashboard/tool/test_dashboard_tools.py:102-106`).
2. Request enters popularity branch (`if request.order_column ==
"popularity_score"`) at
`list_dashboards.py:133-137`, then `_list_dashboards_by_popularity()` sets
`columns_requested` to defaults only (`line 237`) and still
computes/attaches popularity
(`lines 241-242`, `250`).
3. Back in `list_dashboards()`, response filtering uses `columns_to_filter =
original_select_columns or result.columns_requested` (`line 190`), so with
no explicit
`select_columns`, filter list is default columns without `popularity_score`.
4. `DashboardInfo` serializer drops fields not in `select_columns` context
(`superset/mcp_service/dashboard/schemas.py:375-380`), so `popularity_score`
is removed
from each dashboard despite being computed and used for sorting.
```
Prompt for AI Agent 🤖
```mdx
This is a comment left during a code review.
**Path:** superset/mcp_service/dashboard/tool/list_dashboards.py
**Line:** 237:237
**Comment:**
*Logic Error: When sorting by popularity without explicit
`select_columns`, the response metadata and field filtering omit
`popularity_score`, so clients cannot see the score they explicitly asked to
sort by. Include `popularity_score` in the default requested columns for the
popularity-sort path.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
👍
| 👎
--
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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4055138901 ## **Sequence Diagram** This PR adds a computed popularity_score to chart, dashboard, and dataset list tools. The score is calculated only when requested, and popularity-based sorting uses a two-pass flow to rank first, then paginate and return full records. ```mermaid sequenceDiagram participant Client participant MCP List Tool participant DAO participant Popularity Module Client->>MCP List Tool: List assets with select columns or order column popularity_score alt Order by popularity_score MCP List Tool->>DAO: Fetch all matching asset ids MCP List Tool->>Popularity Module: Compute scores and sort ids MCP List Tool->>DAO: Fetch paged assets by sorted ids else popularity_score only requested in select columns MCP List Tool->>DAO: Fetch paged assets with normal ordering MCP List Tool->>Popularity Module: Compute scores for returned ids end MCP List Tool-->>Client: Return assets with popularity_score when requested ``` --- *Generated by [CodeAnt AI](https://codeant.ai)* -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4054898386 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2930869461 ## superset/mcp_service/chart/tool/list_charts.py: ## @@ -71,7 +92,7 @@ async def list_charts(request: ListChartsRequest, ctx: Context) -> ChartList: modified time. Sortable columns for order_column: id, slice_name, viz_type, -datasource_name, description, changed_on, created_on +datasource_name, description, changed_on, created_on, popularity_score Review Comment: Missing sortable column in schema The function docstring lists 'datasource_name' as a sortable column, but CHART_SORTABLE_COLUMNS imported from schema_discovery.py does not include it. This will cause sorting requests for datasource_name to fail with an error, as the ModelListCore validates against the sortable_columns list. Since datasource_name is a valid Slice model column and was previously sortable, it should be included. Code suggestion Check the AI-generated fix before applying ``` -"viz_type", -"description", +"viz_type", +"datasource_name", +"description", ``` Code Review Run #3a8f29 --- Should Bito avoid suggestions like this for future reviews? (https://alpha.bito.ai/home/ai-agents/review-rules>Manage Rules) - [ ] Yes, avoid them ## superset/mcp_service/dashboard/tool/list_dashboards.py: ## @@ -108,34 +124,53 @@ def _serialize_dashboard( """Serialize dashboard object (field filtering handled by model_serializer).""" return serialize_dashboard_object(obj) -tool = ModelListCore( -dao_class=DashboardDAO, -output_schema=DashboardInfo, -item_serializer=_serialize_dashboard, -filter_type=DashboardFilter, -default_columns=DEFAULT_DASHBOARD_COLUMNS, -search_columns=[ -"dashboard_title", -"slug", -"uuid", -], -list_field_name="dashboards", -output_list_schema=DashboardList, -all_columns=all_columns, -sortable_columns=DASHBOARD_SORTABLE_COLUMNS, -logger=logger, -) - -with event_logger.log_context(action="mcp.list_dashboards.query"): -result = tool.run_tool( -filters=request.filters, -search=request.search, -select_columns=request.select_columns, -order_column=request.order_column, -order_direction=request.order_direction, -page=max(request.page - 1, 0), -page_size=request.page_size, +# Two-pass approach when sorting by popularity_score +if request.order_column == "popularity_score": +with event_logger.log_context(action="mcp.list_dashboards.popularity_sort"): +result = _list_dashboards_by_popularity( +request, DashboardDAO, _serialize_dashboard, all_columns, ctx +) +else: +list_core = ModelListCore( +dao_class=DashboardDAO, +output_schema=DashboardInfo, +item_serializer=_serialize_dashboard, +filter_type=DashboardFilter, +default_columns=DEFAULT_DASHBOARD_COLUMNS, +search_columns=DASHBOARD_SEARCH_COLUMNS, +list_field_name="dashboards", +output_list_schema=DashboardList, +all_columns=all_columns, +sortable_columns=DASHBOARD_SORTABLE_COLUMNS, +logger=logger, ) + +# Strip computed fields before passing to DAO query +dao_columns = request.select_columns +if dao_columns: +dao_columns = [c for c in dao_columns if c != "popularity_score"] +# Ensure id is loaded when popularity_score was requested +# (scores are keyed by id) +if "popularity_score" in request.select_columns and "id" not in dao_columns: +dao_columns = ["id"] + dao_columns + +with event_logger.log_context(action="mcp.list_dashboards.query"): +result = list_core.run_tool( +filters=request.filters, +search=request.search, +select_columns=dao_columns, +order_column=request.order_column, +order_direction=request.order_direction, +page=max(request.page - 1, 0), +page_size=request.page_size, +) + +# Attach popularity scores if requested in select_columns Review Comment: Serialization Filtering Bug When sorting by non-popularity columns but requesting popularity_score, the field gets attached but filtered out during serialization since columns_requested excludes it. Update columns_requested to match the full request. Code suggestion Check the AI-generated fix before app
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2930742920
##
superset/mcp_service/chart/tool/list_charts.py:
##
@@ -142,9 +188,16 @@ def _serialize_chart(
)
# Apply field filtering via serialization context
-# Always use columns_requested (either explicit select_columns or
defaults)
-# This triggers ChartInfo._filter_fields_by_context for each chart
columns_to_filter = result.columns_requested
+# Re-add popularity_score if it was originally requested
+# (it was stripped before the DAO query since it's computed)
+if (
+request.select_columns
+and "popularity_score" in request.select_columns
+and columns_to_filter
+and "popularity_score" not in columns_to_filter
+):
+columns_to_filter = list(columns_to_filter) + ["popularity_score"]
Review Comment:
**Suggestion:** Internal column injection for score computation leaks into
the response shape. When a client requests only `popularity_score`, the code
adds `id` to `select_columns` for DAO loading and then reuses those mutated
columns for output filtering, so `id` is returned even though it was not
requested. Build response filtering from the original request columns instead
of DAO-expanded columns. [logic error]
Severity Level: Major ⚠️
```mdx
- ⚠️ list_charts response shape violates requested select_columns contract.
- ⚠️ Unrequested id field increases MCP response payload tokens.
- ⚠️ Client-side strict column parsers may mis-handle responses.
```
```suggestion
columns_to_filter = (
list(request.select_columns)
if request.select_columns
else result.columns_requested
)
# Re-add popularity_score if it was originally requested
# (it was stripped before the DAO query since it's computed)
if (
request.select_columns
and "popularity_score" in request.select_columns
and columns_to_filter
and "popularity_score" not in columns_to_filter
):
columns_to_filter = list(columns_to_filter) +
["popularity_score"]
```
Steps of Reproduction ✅
```mdx
1. MCP registers `list_charts` as a callable tool via imports in
`superset/mcp_service/app.py:393-399`; registration is verified in
`tests/unit_tests/mcp_service/test_mcp_tool_registration.py:31`.
2. Call MCP `tools/call` with `name=\"list_charts\"` and params
`{\"select_columns\":
[\"popularity_score\"]}`; middleware resolves tool by message name at
`superset/mcp_service/middleware.py:256`.
3. In `superset/mcp_service/chart/tool/list_charts.py:155-164`, code removes
`popularity_score` for DAO, then injects `"id"` into `dao_columns` to
compute scores.
4. `ModelListCore.run_tool()` sets `columns_requested = select_columns` at
`superset/mcp_service/mcp_core.py:154-160`, so response metadata now carries
`["id"]`
(internal column) rather than original request.
5. Back in `list_charts`, filtering uses `result.columns_requested` at
`superset/mcp_service/chart/tool/list_charts.py:191`, then re-adds
`popularity_score` at
`:194-200`, resulting in `["id","popularity_score"]`.
6. Serializer enforces context-based field output in
`superset/mcp_service/chart/schemas.py:151-155`, so response includes leaked
`id` even
though caller requested only `popularity_score`.
```
Prompt for AI Agent 🤖
```mdx
This is a comment left during a code review.
**Path:** superset/mcp_service/chart/tool/list_charts.py
**Line:** 191:200
**Comment:**
*Logic Error: Internal column injection for score computation leaks
into the response shape. When a client requests only `popularity_score`, the
code adds `id` to `select_columns` for DAO loading and then reuses those
mutated columns for output filtering, so `id` is returned even though it was
not requested. Build response filtering from the original request columns
instead of DAO-expanded columns.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
👍
| 👎
##
superset/mcp_service/dashboard/tool/list_dashboards.py:
##
@@ -144,9 +179,16 @@ def _serialize_dashboard(
)
# Apply field filtering via serialization context
-# Always use columns_requested (either explicit select_columns or defaults)
-# This triggers DashboardInfo._filter_fields_by_context for each dashboard
columns_to_filter = result.columns_requested
+# Re-add popularity_score if it was originally requested
+# (it was stripped before the DAO query since it's computed)
+if (
+reques
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4054552315 ## **Sequence Diagram** This PR adds a lazy computed popularity_score to chart, dashboard, and dataset list tools. The flow now branches between a two-pass ranking path for popularity sorting and a lightweight attach-only path when the score is merely selected. ```mermaid sequenceDiagram participant Client participant MCPListTool participant DAO participant PopularityModule Client->>MCPListTool: List assets with filters and options alt order column is popularity score MCPListTool->>DAO: Fetch matching asset ids MCPListTool->>PopularityModule: Compute scores and rank ids MCPListTool->>DAO: Fetch paged assets in ranked order MCPListTool->>MCPListTool: Attach popularity score to results else popularity score requested in selected columns MCPListTool->>DAO: Fetch paged assets normally MCPListTool->>PopularityModule: Compute scores for returned ids MCPListTool->>MCPListTool: Attach popularity score to results end MCPListTool-->>Client: Return paginated assets with optional popularity score ``` --- *Generated by [CodeAnt AI](https://codeant.ai)* -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2930716476 ## superset/mcp_service/chart/tool/list_charts.py: ## @@ -106,34 +129,50 @@ def _serialize_chart( """Serialize chart object (field filtering handled by model_serializer).""" return serialize_chart_object(cast(ChartLike | None, obj)) -tool = ModelListCore( -dao_class=ChartDAO, -output_schema=ChartInfo, -item_serializer=_serialize_chart, -filter_type=ChartFilter, -default_columns=DEFAULT_CHART_COLUMNS, -search_columns=[ -"slice_name", -"description", -], -list_field_name="charts", -output_list_schema=ChartList, -all_columns=all_columns, -sortable_columns=CHART_SORTABLE_COLUMNS, -logger=logger, -) - try: -with event_logger.log_context(action="mcp.list_charts.query"): -result = tool.run_tool( -filters=request.filters, -search=request.search, -select_columns=request.select_columns, -order_column=request.order_column, -order_direction=request.order_direction, -page=max(request.page - 1, 0), -page_size=request.page_size, +# Two-pass approach when sorting by popularity_score +if request.order_column == "popularity_score": +with event_logger.log_context(action="mcp.list_charts.popularity_sort"): +result = _list_charts_by_popularity( +request, ChartDAO, _serialize_chart, all_columns, ctx +) +else: +list_core = ModelListCore( +dao_class=ChartDAO, +output_schema=ChartInfo, +item_serializer=_serialize_chart, +filter_type=ChartFilter, +default_columns=DEFAULT_CHART_COLUMNS, +search_columns=CHART_SEARCH_COLUMNS, +list_field_name="charts", +output_list_schema=ChartList, +all_columns=all_columns, +sortable_columns=CHART_SORTABLE_COLUMNS, +logger=logger, ) + +# Strip computed fields before passing to DAO query +dao_columns = request.select_columns +if dao_columns: +dao_columns = [c for c in dao_columns if c != "popularity_score"] + +with event_logger.log_context(action="mcp.list_charts.query"): +result = list_core.run_tool( +filters=request.filters, +search=request.search, +select_columns=dao_columns, +order_column=request.order_column, +order_direction=request.order_direction, +page=max(request.page - 1, 0), +page_size=request.page_size, +) + +# Attach popularity scores if requested in select_columns +if request.select_columns and "popularity_score" in request.select_columns: +if chart_ids := [c.id for c in result.charts if c.id is not None]: +scores = compute_chart_popularity(chart_ids) +attach_popularity_scores(result.charts, scores) Review Comment: Good catch — fixed in ecc8782. When `popularity_score` is in `select_columns`, it's now re-added to `columns_to_filter` before serialization so it's not silently dropped. Also ensures `id` is always loaded so scores can be attached by ID. ## superset/mcp_service/dashboard/tool/list_dashboards.py: ## @@ -108,34 +124,49 @@ def _serialize_dashboard( """Serialize dashboard object (field filtering handled by model_serializer).""" return serialize_dashboard_object(obj) -tool = ModelListCore( -dao_class=DashboardDAO, -output_schema=DashboardInfo, -item_serializer=_serialize_dashboard, -filter_type=DashboardFilter, -default_columns=DEFAULT_DASHBOARD_COLUMNS, -search_columns=[ -"dashboard_title", -"slug", -"uuid", -], -list_field_name="dashboards", -output_list_schema=DashboardList, -all_columns=all_columns, -sortable_columns=DASHBOARD_SORTABLE_COLUMNS, -logger=logger, -) - -with event_logger.log_context(action="mcp.list_dashboards.query"): -result = tool.run_tool( -filters=request.filters, -search=request.search, -select_columns=request.select_columns, -order_column=request.order_column, -order_direction=request.order_direction, -page=max(request.page - 1, 0), -page_size=request.page_size, +# Two-pass approach when sorting by popularity_score +if request.order_column == "popularity_
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4054495904 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2930413744
##
superset/mcp_service/dashboard/tool/list_dashboards.py:
##
@@ -108,34 +124,49 @@ def _serialize_dashboard(
"""Serialize dashboard object (field filtering handled by
model_serializer)."""
return serialize_dashboard_object(obj)
-tool = ModelListCore(
-dao_class=DashboardDAO,
-output_schema=DashboardInfo,
-item_serializer=_serialize_dashboard,
-filter_type=DashboardFilter,
-default_columns=DEFAULT_DASHBOARD_COLUMNS,
-search_columns=[
-"dashboard_title",
-"slug",
-"uuid",
-],
-list_field_name="dashboards",
-output_list_schema=DashboardList,
-all_columns=all_columns,
-sortable_columns=DASHBOARD_SORTABLE_COLUMNS,
-logger=logger,
-)
-
-with event_logger.log_context(action="mcp.list_dashboards.query"):
-result = tool.run_tool(
-filters=request.filters,
-search=request.search,
-select_columns=request.select_columns,
-order_column=request.order_column,
-order_direction=request.order_direction,
-page=max(request.page - 1, 0),
-page_size=request.page_size,
+# Two-pass approach when sorting by popularity_score
+if request.order_column == "popularity_score":
+with
event_logger.log_context(action="mcp.list_dashboards.popularity_sort"):
+result = _list_dashboards_by_popularity(
+request, DashboardDAO, _serialize_dashboard, all_columns, ctx
+)
+else:
+list_core = ModelListCore(
+dao_class=DashboardDAO,
+output_schema=DashboardInfo,
+item_serializer=_serialize_dashboard,
+filter_type=DashboardFilter,
+default_columns=DEFAULT_DASHBOARD_COLUMNS,
+search_columns=DASHBOARD_SEARCH_COLUMNS,
+list_field_name="dashboards",
+output_list_schema=DashboardList,
+all_columns=all_columns,
+sortable_columns=DASHBOARD_SORTABLE_COLUMNS,
+logger=logger,
)
+
+# Strip computed fields before passing to DAO query
+dao_columns = request.select_columns
+if dao_columns:
+dao_columns = [c for c in dao_columns if c != "popularity_score"]
Review Comment:
**Suggestion:** When `popularity_score` is requested via `select_columns`,
the DAO query removes it but does not ensure `id` is still loaded. Since score
attachment depends on object IDs, rows loaded without `id` cannot be scored and
the response will miss/empty `popularity_score`. Keep `id` in `dao_columns`
whenever `popularity_score` is requested. [logic error]
Severity Level: Major ⚠️
```mdx
- ❌ list_dashboards cannot reliably return requested popularity_score.
- ⚠️ MCP ranking clients get incomplete dashboard relevance data.
```
```suggestion
dao_columns = request.select_columns
if dao_columns:
dao_columns = [c for c in dao_columns if c != "popularity_score"]
if "popularity_score" in request.select_columns and "id" not in
dao_columns:
dao_columns = list(dao_columns) + ["id"]
```
Steps of Reproduction ✅
```mdx
1. Call MCP tool `list_dashboards` with
`select_columns=["dashboard_title","popularity_score"]` (tool is registered
in
`superset/mcp_service/app.py:406` and invoked in tests via
`client.call_tool("list_dashboards", ...)` at
`tests/unit_tests/mcp_service/dashboard/tool/test_dashboard_tools.py:102-105`).
2. In `superset/mcp_service/dashboard/tool/list_dashboards.py:149-157`,
`popularity_score`
is stripped and `ModelListCore.run_tool` receives
`select_columns=["dashboard_title"]`.
3. `ModelListCore.run_tool` forwards `columns=columns_to_load` to DAO
(`superset/mcp_service/mcp_core.py:165-174`); DAO then queries only
requested columns
(`superset/daos/base.py:643-645`), so rows may not contain `id`. Serializer
sets
`id=getattr(..., "id", None)`
(`superset/mcp_service/dashboard/schemas.py:528`), yielding
`id=None`.
4. Score attachment path
(`superset/mcp_service/dashboard/tool/list_dashboards.py:165-168`) skips
rows with `id is
None`, so scores are not mapped. Final response filtering uses
`result.columns_requested`
(`list_dashboards.py:178-185`, `dashboard/schemas.py:378`), so requested
popularity
metadata is not reliably returned.
```
Prompt for AI Agent 🤖
```mdx
This is a comment left during a code review.
**Path:** superset/mcp_service/dashboard/tool/list_dashboards.py
**Line:** 149:151
**Comment:**
*Logic Error: When `popularity_score` is requested via
`select_columns`, the DAO query removes
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4054186571 ## **Sequence Diagram** This PR adds a computed popularity_score to chart, dashboard, and dataset list tools. The score is calculated only when requested, with a dedicated two pass flow when sorting by popularity to rank results before pagination. ```mermaid sequenceDiagram participant Client participant MCPListTool participant DAO participant PopularityModule Client->>MCPListTool: List assets with filters and sort options alt order_column is popularity_score MCPListTool->>DAO: Fetch all matching asset IDs MCPListTool->>PopularityModule: Compute scores and rank IDs MCPListTool->>DAO: Fetch paged assets in ranked ID order else popularity_score in select_columns only MCPListTool->>DAO: Fetch paged assets with normal sorting MCPListTool->>PopularityModule: Compute scores for returned asset IDs end MCPListTool-->>Client: Return asset list with optional popularity_score ``` --- *Generated by [CodeAnt AI](https://codeant.ai)* -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2930390812
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,368 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table."""
+view_counts = (
+db.session.query(id_column, sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm >=
cutoff)
+.group_by(id_column)
+.all()
+)
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+days: Number of days for view count window
+
+Returns:
+Dict mapping chart_id -> popularity score
+"""
+if not chart_ids:
+return {}
+
+scores = _init_scores(chart_ids)
+cutoff = datetime.now(timezone.utc) - timedelta(days=days)
+
+_add_view_scores(
+scores, "mount_explorer", Log.slice_id, chart_ids, cutoff, VIEW_WEIGHT
+)
+_add_fav_scores(scores, "slice", chart_ids, FAV_WEIGHT)
+
+# Dashboard count (how man
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2930382136
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,368 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table."""
+view_counts = (
+db.session.query(id_column, sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm >=
cutoff)
+.group_by(id_column)
+.all()
+)
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+days: Number of days for view count window
+
+Returns:
+Dict mapping chart_id -> popularity score
+"""
+if not chart_ids:
+return {}
+
+scores = _init_scores(chart_ids)
+cutoff = datetime.now(timezone.utc) - timedelta(days=days)
+
+_add_view_scores(
+scores, "mount_explorer", Log.slice_id, chart_ids, cutoff, VIEW_WEIGHT
+)
+_add_fav_scores(scores, "slice", chart_ids, FAV_WEIGHT)
+
+# Dashboard count (how many dashb
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2927567676
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,368 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Default time window for view counts
+DEFAULT_VIEW_WINDOW_DAYS = 30
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table."""
+view_counts = (
+db.session.query(id_column, sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm >=
cutoff)
+.group_by(id_column)
+.all()
+)
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(
+chart_ids: list[int], days: int = DEFAULT_VIEW_WINDOW_DAYS
+) -> dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+days: Number of days for view count window
+
+Returns:
+Dict mapping chart_id -> popularity score
+"""
+if not chart_ids:
+return {}
+
+scores = _init_scores(chart_ids)
+cutoff = datetime.now(timezone.utc) - timedelta(days=days)
+
+_add_view_scores(
+scores, "mount_explorer", Log.slice_id, chart_ids, cutoff, VIEW_WEIGHT
+)
+_add_fav_scores(scores, "slice", chart_ids, FAV_WEIGHT)
+
+# Dashboard count (how man
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4050251477 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2911821699
##
tests/unit_tests/mcp_service/common/test_popularity.py:
##
@@ -0,0 +1,386 @@
+# 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.
+
+"""
+Unit tests for the popularity scoring module.
+"""
+
+from __future__ import annotations
+
+from collections import namedtuple
+from datetime import datetime, timedelta, timezone
+from unittest.mock import MagicMock, patch
+
+from superset.mcp_service.common.popularity import (
+_recency_bonus,
+compute_chart_popularity,
+compute_dashboard_popularity,
+compute_dataset_popularity,
+get_popularity_sorted_ids,
+)
+
+# Named tuples to simulate SQLAlchemy query results
+ViewRow = namedtuple("ViewRow", ["slice_id", "view_count"])
+DashViewRow = namedtuple("DashViewRow", ["dashboard_id", "view_count"])
+FavRow = namedtuple("FavRow", ["obj_id", "fav_count"])
+DashCountRow = namedtuple("DashCountRow", ["slice_id", "dash_count"])
+ChartCountRow = namedtuple("ChartCountRow", ["dashboard_id", "chart_count"])
+DatasetChartCountRow = namedtuple(
+"DatasetChartCountRow", ["datasource_id", "chart_count"]
+)
+ChartMeta = namedtuple("ChartMeta", ["id", "certified_by", "changed_on"])
+DashMeta = namedtuple("DashMeta", ["id", "published", "certified_by",
"changed_on"])
+DatasetMeta = namedtuple("DatasetMeta", ["id", "extra", "changed_on"])
+
+
+class TestRecencyBonus:
+def test_within_7_days(self):
+recent = datetime.now(timezone.utc) - timedelta(days=3)
+assert _recency_bonus(recent) == 5.0
+
+def test_within_30_days(self):
+moderate = datetime.now(timezone.utc) - timedelta(days=15)
+assert _recency_bonus(moderate) == 2.0
+
+def test_older_than_30_days(self):
+old = datetime.now(timezone.utc) - timedelta(days=60)
+assert _recency_bonus(old) == 0.0
+
+def test_none_changed_on(self):
+assert _recency_bonus(None) == 0.0
+
+def test_naive_datetime_treated_as_utc(self):
+# Naive datetime (no tzinfo) should be treated as UTC
+recent = datetime.now(timezone.utc).replace(tzinfo=None) -
timedelta(days=3)
+assert _recency_bonus(recent) == 5.0
+
+
+class TestComputeChartPopularity:
+def test_empty_ids_returns_empty(self):
+assert compute_chart_popularity([]) == {}
+
+@patch("superset.mcp_service.common.popularity.db")
+def test_chart_scoring_formula(self, mock_db):
+"""Test: view_count*3 + fav_count*5 + dash_count*2 + certified*10 +
recency"""
+recent = datetime.now(timezone.utc) - timedelta(days=2) # +5 recency
+
+# Setup mock query chain
+mock_session = MagicMock()
+mock_db.session = mock_session
+
+# Each db.session.query() call returns a different chain
+query_chains = [
+# 1. View counts query
+self._mock_query_chain([ViewRow(slice_id=1, view_count=10)]),
+# 2. Fav counts query
+self._mock_query_chain([FavRow(obj_id=1, fav_count=3)]),
+# 3. Dashboard counts query
+self._mock_query_chain([DashCountRow(slice_id=1, dash_count=4)]),
+# 4. Chart metadata query
+self._mock_query_chain(
+[ChartMeta(id=1, certified_by="admin", changed_on=recent)]
+),
+]
+mock_session.query.side_effect = query_chains
+
+scores = compute_chart_popularity([1])
+
+# views: 10*3=30, favs: 3*5=15, dashes: 4*2=8, certified: 10, recency:
5
+expected = 30 + 15 + 8 + 10 + 5
+assert scores[1] == expected
+
+@patch("superset.mcp_service.common.popularity.db")
+def test_chart_no_activity_only_recency(self, mock_db):
+"""Chart with no views/favs/dashboards gets only recency +
certification."""
+old = datetime.now(timezone.utc) - timedelta(days=60) # 0 recency
+
+mock_session = MagicMock()
+mock_db.session = mock_session
+
+query_chains = [
+self._mock_query_chain([]), # No views
+self._mock_query_chain([]), # No favs
+self._mock_query_chain([]), # No dashboards
+self._mock_query_chai
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4031444840 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2911805917
##
tests/unit_tests/mcp_service/common/test_popularity.py:
##
@@ -0,0 +1,386 @@
+# 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.
+
+"""
+Unit tests for the popularity scoring module.
+"""
+
+from __future__ import annotations
+
+from collections import namedtuple
+from datetime import datetime, timedelta, timezone
+from unittest.mock import MagicMock, patch
+
+from superset.mcp_service.common.popularity import (
+_recency_bonus,
+compute_chart_popularity,
+compute_dashboard_popularity,
+compute_dataset_popularity,
+get_popularity_sorted_ids,
+)
+
+# Named tuples to simulate SQLAlchemy query results
+ViewRow = namedtuple("ViewRow", ["slice_id", "view_count"])
+DashViewRow = namedtuple("DashViewRow", ["dashboard_id", "view_count"])
+FavRow = namedtuple("FavRow", ["obj_id", "fav_count"])
+DashCountRow = namedtuple("DashCountRow", ["slice_id", "dash_count"])
+ChartCountRow = namedtuple("ChartCountRow", ["dashboard_id", "chart_count"])
+DatasetChartCountRow = namedtuple(
+"DatasetChartCountRow", ["datasource_id", "chart_count"]
+)
+ChartMeta = namedtuple("ChartMeta", ["id", "certified_by", "changed_on"])
+DashMeta = namedtuple("DashMeta", ["id", "published", "certified_by",
"changed_on"])
+DatasetMeta = namedtuple("DatasetMeta", ["id", "extra", "changed_on"])
+
+
+class TestRecencyBonus:
+def test_within_7_days(self):
+recent = datetime.now(timezone.utc) - timedelta(days=3)
+assert _recency_bonus(recent) == 5.0
+
+def test_within_30_days(self):
+moderate = datetime.now(timezone.utc) - timedelta(days=15)
+assert _recency_bonus(moderate) == 2.0
+
+def test_older_than_30_days(self):
+old = datetime.now(timezone.utc) - timedelta(days=60)
+assert _recency_bonus(old) == 0.0
+
+def test_none_changed_on(self):
+assert _recency_bonus(None) == 0.0
+
+def test_naive_datetime_treated_as_utc(self):
+# Naive datetime (no tzinfo) should be treated as UTC
+recent = datetime.now(timezone.utc).replace(tzinfo=None) -
timedelta(days=3)
+assert _recency_bonus(recent) == 5.0
+
+
+class TestComputeChartPopularity:
+def test_empty_ids_returns_empty(self):
+assert compute_chart_popularity([]) == {}
+
+@patch("superset.mcp_service.common.popularity.db")
+def test_chart_scoring_formula(self, mock_db):
+"""Test: view_count*3 + fav_count*5 + dash_count*2 + certified*10 +
recency"""
+recent = datetime.now(timezone.utc) - timedelta(days=2) # +5 recency
+
+# Setup mock query chain
+mock_session = MagicMock()
+mock_db.session = mock_session
+
+# Each db.session.query() call returns a different chain
+query_chains = [
+# 1. View counts query
+self._mock_query_chain([ViewRow(slice_id=1, view_count=10)]),
+# 2. Fav counts query
+self._mock_query_chain([FavRow(obj_id=1, fav_count=3)]),
+# 3. Dashboard counts query
+self._mock_query_chain([DashCountRow(slice_id=1, dash_count=4)]),
+# 4. Chart metadata query
+self._mock_query_chain(
+[ChartMeta(id=1, certified_by="admin", changed_on=recent)]
+),
+]
+mock_session.query.side_effect = query_chains
+
+scores = compute_chart_popularity([1])
+
+# views: 10*3=30, favs: 3*5=15, dashes: 4*2=8, certified: 10, recency:
5
+expected = 30 + 15 + 8 + 10 + 5
+assert scores[1] == expected
+
+@patch("superset.mcp_service.common.popularity.db")
+def test_chart_no_activity_only_recency(self, mock_db):
+"""Chart with no views/favs/dashboards gets only recency +
certification."""
+old = datetime.now(timezone.utc) - timedelta(days=60) # 0 recency
+
+mock_session = MagicMock()
+mock_db.session = mock_session
+
+query_chains = [
+self._mock_query_chain([]), # No views
+self._mock_query_chain([]), # No favs
+self._mock_query_chain([]), # No dashboards
+self._mock_query_chain(
+
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
aminghadersohi commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2911389261
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,359 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table."""
+view_counts = (
+db.session.query(id_column, sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm >=
cutoff)
+.group_by(id_column)
+.all()
+)
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(chart_ids: list[int], days: int = 30) ->
dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+days: Number of days for view count window
+
+Returns:
+Dict mapping chart_id -> popularity score
+"""
+if not chart_ids:
+return {}
+
+scores = _init_scores(chart_ids)
+cutoff = datetime.now(timezone.utc) - timedelta(days=days)
+
+_add_view_scores(
+scores, "mount_explorer", Log.slice_id, chart_ids, cutoff, VIEW_WEIGHT
+)
+_add_fav_scores(scores, "slice", chart_ids, FAV_WEIGHT)
+
+# Dashboard count (how many dashboards contain each chart)
+dash_counts = (
+db.session.query(
+dashboard_slic
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2911130553
##
tests/unit_tests/mcp_service/common/test_popularity.py:
##
@@ -0,0 +1,386 @@
+# 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.
+
+"""
+Unit tests for the popularity scoring module.
+"""
+
+from __future__ import annotations
+
+from collections import namedtuple
+from datetime import datetime, timedelta, timezone
+from unittest.mock import MagicMock, patch
+
+from superset.mcp_service.common.popularity import (
+_recency_bonus,
+compute_chart_popularity,
+compute_dashboard_popularity,
+compute_dataset_popularity,
+get_popularity_sorted_ids,
+)
+
+# Named tuples to simulate SQLAlchemy query results
+ViewRow = namedtuple("ViewRow", ["slice_id", "view_count"])
+DashViewRow = namedtuple("DashViewRow", ["dashboard_id", "view_count"])
+FavRow = namedtuple("FavRow", ["obj_id", "fav_count"])
+DashCountRow = namedtuple("DashCountRow", ["slice_id", "dash_count"])
+ChartCountRow = namedtuple("ChartCountRow", ["dashboard_id", "chart_count"])
+DatasetChartCountRow = namedtuple(
+"DatasetChartCountRow", ["datasource_id", "chart_count"]
+)
+ChartMeta = namedtuple("ChartMeta", ["id", "certified_by", "changed_on"])
+DashMeta = namedtuple("DashMeta", ["id", "published", "certified_by",
"changed_on"])
+DatasetMeta = namedtuple("DatasetMeta", ["id", "extra", "changed_on"])
+
+
+class TestRecencyBonus:
+def test_within_7_days(self):
+recent = datetime.now(timezone.utc) - timedelta(days=3)
+assert _recency_bonus(recent) == 5.0
+
+def test_within_30_days(self):
+moderate = datetime.now(timezone.utc) - timedelta(days=15)
+assert _recency_bonus(moderate) == 2.0
+
+def test_older_than_30_days(self):
+old = datetime.now(timezone.utc) - timedelta(days=60)
+assert _recency_bonus(old) == 0.0
+
+def test_none_changed_on(self):
+assert _recency_bonus(None) == 0.0
+
+def test_naive_datetime_treated_as_utc(self):
+# Naive datetime (no tzinfo) should be treated as UTC
+recent = datetime.now(timezone.utc).replace(tzinfo=None) -
timedelta(days=3)
+assert _recency_bonus(recent) == 5.0
+
+
+class TestComputeChartPopularity:
+def test_empty_ids_returns_empty(self):
+assert compute_chart_popularity([]) == {}
+
+@patch("superset.mcp_service.common.popularity.db")
+def test_chart_scoring_formula(self, mock_db):
+"""Test: view_count*3 + fav_count*5 + dash_count*2 + certified*10 +
recency"""
+recent = datetime.now(timezone.utc) - timedelta(days=2) # +5 recency
+
+# Setup mock query chain
+mock_session = MagicMock()
+mock_db.session = mock_session
+
+# Each db.session.query() call returns a different chain
+query_chains = [
+# 1. View counts query
+self._mock_query_chain([ViewRow(slice_id=1, view_count=10)]),
+# 2. Fav counts query
+self._mock_query_chain([FavRow(obj_id=1, fav_count=3)]),
+# 3. Dashboard counts query
+self._mock_query_chain([DashCountRow(slice_id=1, dash_count=4)]),
+# 4. Chart metadata query
+self._mock_query_chain(
+[ChartMeta(id=1, certified_by="admin", changed_on=recent)]
+),
+]
+mock_session.query.side_effect = query_chains
+
+scores = compute_chart_popularity([1])
+
+# views: 10*3=30, favs: 3*5=15, dashes: 4*2=8, certified: 10, recency:
5
+expected = 30 + 15 + 8 + 10 + 5
+assert scores[1] == expected
+
+@patch("superset.mcp_service.common.popularity.db")
+def test_chart_no_activity_only_recency(self, mock_db):
+"""Chart with no views/favs/dashboards gets only recency +
certification."""
+old = datetime.now(timezone.utc) - timedelta(days=60) # 0 recency
+
+mock_session = MagicMock()
+mock_db.session = mock_session
+
+query_chains = [
+self._mock_query_chain([]), # No views
+self._mock_query_chain([]), # No favs
+self._mock_query_chain([]), # No dashboards
+self._mock_query_chai
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4030651900 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on code in PR #38416: URL: https://github.com/apache/superset/pull/38416#discussion_r2910653665 ## superset/mcp_service/dataset/tool/list_datasets.py: ## @@ -115,32 +121,45 @@ def _serialize_dataset( """Serialize dataset (filtering via model_serializer).""" return serialize_dataset_object(obj) -# Create tool with standard serialization -tool = ModelListCore( -dao_class=DatasetDAO, -output_schema=DatasetInfo, -item_serializer=_serialize_dataset, -filter_type=DatasetFilter, -default_columns=DEFAULT_DATASET_COLUMNS, -search_columns=["schema", "sql", "table_name", "uuid"], -list_field_name="datasets", -output_list_schema=DatasetList, -all_columns=all_columns, -sortable_columns=DATASET_SORTABLE_COLUMNS, -logger=logger, -) - -with event_logger.log_context(action="mcp.list_datasets.query"): -result = tool.run_tool( -filters=request.filters, -search=request.search, -select_columns=request.select_columns, -order_column=request.order_column, -order_direction=request.order_direction, -page=max(request.page - 1, 0), -page_size=request.page_size, +# Two-pass approach when sorting by popularity_score +if request.order_column == "popularity_score": +with event_logger.log_context(action="mcp.list_datasets.popularity_sort"): +result = _list_datasets_by_popularity( +request, DatasetDAO, _serialize_dataset, all_columns, ctx +) +else: +# Create tool with standard serialization +list_core = ModelListCore( +dao_class=DatasetDAO, +output_schema=DatasetInfo, +item_serializer=_serialize_dataset, +filter_type=DatasetFilter, +default_columns=DEFAULT_DATASET_COLUMNS, +search_columns=DATASET_SEARCH_COLUMNS, +list_field_name="datasets", +output_list_schema=DatasetList, +all_columns=all_columns, +sortable_columns=DATASET_SORTABLE_COLUMNS, +logger=logger, ) +with event_logger.log_context(action="mcp.list_datasets.query"): +result = list_core.run_tool( +filters=request.filters, +search=request.search, +select_columns=request.select_columns, +order_column=request.order_column, +order_direction=request.order_direction, +page=max(request.page - 1, 0), +page_size=request.page_size, +) + +# Attach popularity scores if requested in select_columns +if request.select_columns and "popularity_score" in request.select_columns: +if ds_ids := [d.id for d in result.datasets if d.id is not None]: +scores = compute_dataset_popularity(ds_ids) +attach_popularity_scores(result.datasets, scores) Review Comment: **Suggestion:** The dataset list tool passes `select_columns` directly (including `popularity_score`) into `ModelListCore.run_tool` and thus to `DatasetDAO.list(columns=...)`, but `popularity_score` is computed rather than stored, so selecting it will cause the DAO query to fail instead of letting the later scoring code augment the results. [logic error] Severity Level: Critical 🚨 ```mdx - ❌ list_datasets fails when selecting popularity_score column. - ⚠️ MCP clients cannot retrieve dataset popularity metadata. - ⚠️ New popularity_score feature unusable without workaround for datasets. ``` ```suggestion else: # Create tool with standard serialization list_core = ModelListCore( dao_class=DatasetDAO, output_schema=DatasetInfo, item_serializer=_serialize_dataset, filter_type=DatasetFilter, default_columns=DEFAULT_DATASET_COLUMNS, search_columns=DATASET_SEARCH_COLUMNS, list_field_name="datasets", output_list_schema=DatasetList, all_columns=all_columns, sortable_columns=DATASET_SORTABLE_COLUMNS, logger=logger, ) # popularity_score is computed, so do not request it as a physical column original_select_columns = list(request.select_columns or []) dao_select_columns = [ col for c
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4030146411 ## **Sequence Diagram** This PR adds a computed popularity_score to chart, dashboard, and dataset MCP list tools and introduces a two-pass flow that ranks assets by aggregated usage signals when clients sort by popularity_score. ```mermaid sequenceDiagram participant Client participant MCPListTool as MCP list tool participant DAO as Asset DAO participant Popularity as Popularity service Client->>MCPListTool: List assets ordered by popularity_score MCPListTool->>DAO: List matching asset ids with filters and search DAO-->>MCPListTool: Asset ids MCPListTool->>Popularity: Compute popularity scores for asset ids MCPListTool->>DAO: Fetch full asset models for requested page DAO-->>MCPListTool: Asset models MCPListTool->>Popularity: Attach popularity_score to each model MCPListTool-->>Client: Paginated asset list including popularity_score ``` --- *Generated by [CodeAnt AI](https://codeant.ai)* -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4027816822 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4027598670 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4027211713 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codeant-ai-for-open-source[bot] commented on code in PR #38416:
URL: https://github.com/apache/superset/pull/38416#discussion_r2907958031
##
superset/mcp_service/common/popularity.py:
##
@@ -0,0 +1,359 @@
+# 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.
+
+"""
+Popularity scoring for charts, dashboards, and datasets.
+
+Computes a composite popularity score based on views, favorites,
+relationships, certification, and recency.
+"""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from datetime import datetime, timedelta, timezone
+from typing import Any
+
+import sqlalchemy as sa
+
+from superset.connectors.sqla.models import SqlaTable
+from superset.extensions import db
+from superset.models.core import FavStar, Log
+from superset.models.dashboard import Dashboard, dashboard_slices
+from superset.models.slice import Slice
+from superset.utils import json as json_utils
+
+logger = logging.getLogger(__name__)
+
+# Scoring weights
+VIEW_WEIGHT = 3
+FAV_WEIGHT = 5
+DASHBOARD_COUNT_WEIGHT = 2
+CHART_COUNT_WEIGHT_DASHBOARD = 1
+CHART_COUNT_WEIGHT_DATASET = 3
+CERTIFICATION_BONUS = 10
+PUBLISHED_BONUS = 3
+
+# Recency thresholds and bonuses
+RECENCY_RECENT_DAYS = 7
+RECENCY_MODERATE_DAYS = 30
+RECENCY_RECENT_BONUS = 5.0
+RECENCY_MODERATE_BONUS = 2.0
+
+# Two-pass query limit
+MAX_POPULARITY_SORT_PAGE_SIZE = 100_000
+
+
+def _recency_bonus(changed_on: datetime | None) -> float:
+"""Compute recency bonus based on changed_on timestamp."""
+if not changed_on:
+return 0.0
+now = datetime.now(timezone.utc)
+# Ensure changed_on is timezone-aware for comparison
+if changed_on.tzinfo is None:
+changed_on = changed_on.replace(tzinfo=timezone.utc)
+delta = now - changed_on
+if delta <= timedelta(days=RECENCY_RECENT_DAYS):
+return RECENCY_RECENT_BONUS
+if delta <= timedelta(days=RECENCY_MODERATE_DAYS):
+return RECENCY_MODERATE_BONUS
+return 0.0
+
+
+def _init_scores(ids: list[int]) -> dict[int, float]:
+"""Initialize a score dict with 0.0 for each ID."""
+return {i: 0.0 for i in ids} # noqa: C420
+
+
+def _add_view_scores(
+scores: dict[int, float],
+action: str,
+id_column: sa.Column,
+entity_ids: list[int],
+cutoff: datetime,
+weight: int,
+) -> None:
+"""Add view count scores from the logs table."""
+view_counts = (
+db.session.query(id_column, sa.func.count(Log.id).label("view_count"))
+.filter(Log.action == action, id_column.in_(entity_ids), Log.dttm >=
cutoff)
+.group_by(id_column)
+.all()
+)
+for row in view_counts:
+entity_id = row[0]
+if entity_id in scores:
+scores[entity_id] += row.view_count * weight
+
+
+def _add_fav_scores(
+scores: dict[int, float],
+class_name: str,
+entity_ids: list[int],
+weight: int,
+) -> None:
+"""Add favorite count scores from the favstar table."""
+fav_counts = (
+db.session.query(FavStar.obj_id,
sa.func.count(FavStar.id).label("fav_count"))
+.filter(FavStar.class_name == class_name,
FavStar.obj_id.in_(entity_ids))
+.group_by(FavStar.obj_id)
+.all()
+)
+for row in fav_counts:
+if row.obj_id in scores:
+scores[row.obj_id] += row.fav_count * weight
+
+
+def compute_chart_popularity(chart_ids: list[int], days: int = 30) ->
dict[int, float]:
+"""Compute popularity scores for charts.
+
+Formula: view_count_30d * 3 + fav_count * 5 + dashboard_count * 2
+ + is_certified * 10 + recency_bonus
+
+Args:
+chart_ids: List of chart IDs to score
+days: Number of days for view count window
+
+Returns:
+Dict mapping chart_id -> popularity score
+"""
+if not chart_ids:
+return {}
+
+scores = _init_scores(chart_ids)
+cutoff = datetime.now(timezone.utc) - timedelta(days=days)
+
+_add_view_scores(
+scores, "mount_explorer", Log.slice_id, chart_ids, cutoff, VIEW_WEIGHT
+)
+_add_fav_scores(scores, "slice", chart_ids, FAV_WEIGHT)
+
+# Dashboard count (how many dashboards contain each chart)
+dash_counts = (
+db.session.query(
+
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4010787592 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4007737815 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4003693781 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4001214180 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
netlify[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4000984079 ### ✅ Deploy Preview for *superset-docs-preview* ready! | Name | Link | |:-:|| |🔨 Latest commit | c95bd8cbcbbaf61963e5a1d3fb6fca39adb76569 | |🔍 Latest deploy log | https://app.netlify.com/projects/superset-docs-preview/deploys/69a8c18190c2460007e9654a | |😎 Deploy Preview | [https://deploy-preview-38416--superset-docs-preview.netlify.app](https://deploy-preview-38416--superset-docs-preview.netlify.app) | |📱 Preview on mobile | Toggle QR Code... _Use your smartphone camera to open QR code link._ | --- _To edit notification comments on pull requests, go to your [Netlify project configuration](https://app.netlify.com/projects/superset-docs-preview/configuration/notifications#deploy-notifications)._ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4000722925 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). Bito Usage Guide **Commands** Type the following command in the pull request comment and save the comment. - `/review` - Manually triggers a full AI review. - `/pause` - Pauses automatic reviews on this pull request. - `/resume` - Resumes automatic reviews. - `/resolve` - Marks all Bito-posted review comments as resolved. - `/abort` - Cancels all in-progress reviews. Refer to the https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/available-commands"; target="_blank" rel="noopener noreferrer">documentation for additional commands. **Configuration** This repository uses `Superset` You can customize the agent settings https://alpha.bito.ai/home/ai-agents/code-review-agent"; target="_blank" rel="noopener noreferrer">here or contact your Bito workspace admin at [email protected]. **Documentation & Help** - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/getting-started/install-run-using-bito-cloud/create-or-customize-an-agent-instance"; target="_blank" rel="noopener noreferrer">Customize agent settings - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/implementing-custom-code-review-rules"; target="_blank" rel="noopener noreferrer">Review rules - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent"; target="_blank" rel="noopener noreferrer">General documentation - https://docs.bito.ai/bito-dev-agents/ai-code-review-agent/faqs"; target="_blank" rel="noopener noreferrer">FAQ -- 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]
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
codecov[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4000416808 ## [Codecov](https://app.codecov.io/gh/apache/superset/pull/38416?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report :x: Patch coverage is `0%` with `112 lines` in your changes missing coverage. Please review. :white_check_mark: Project coverage is 64.30%. Comparing base ([`f2f5559`](https://app.codecov.io/gh/apache/superset/commit/f2f55591eccef795f816043b93876e6ca4beacd3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)) to head ([`3f0ea32`](https://app.codecov.io/gh/apache/superset/commit/3f0ea324a0519a7d0b8546709e32cfe19e964083?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)). :warning: Report is 30 commits behind head on master. | [Files with missing lines](https://app.codecov.io/gh/apache/superset/pull/38416?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Patch % | Lines | |---|---|---| | [superset/mcp\_service/common/popularity.py](https://app.codecov.io/gh/apache/superset/pull/38416?src=pr&el=tree&filepath=superset%2Fmcp_service%2Fcommon%2Fpopularity.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3VwZXJzZXQvbWNwX3NlcnZpY2UvY29tbW9uL3BvcHVsYXJpdHkucHk=) | 0.00% | [112 Missing :warning: ](https://app.codecov.io/gh/apache/superset/pull/38416?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | :x: Your project check has failed because the head coverage (99.24%) is below the target coverage (100.00%). You can increase the head coverage or adjust the [target](https://docs.codecov.com/docs/commit-status#target) coverage. Additional details and impacted files ```diff @@Coverage Diff @@ ## master #38416 +/- ## == + Coverage 64.29% 64.30% +0.01% == Files1811 2489 +678 Lines 71491 123924 +52433 Branches2277528827+6052 == + Hits4596779694 +33727 - Misses 2552442807 +17283 - Partials0 1423+1423 ``` | [Flag](https://app.codecov.io/gh/apache/superset/pull/38416/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | | |---|---|---| | [hive](https://app.codecov.io/gh/apache/superset/pull/38416/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `40.99% <0.00%> (?)` | | | [presto](https://app.codecov.io/gh/apache/superset/pull/38416/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `41.00% <0.00%> (?)` | | | [python](https://app.codecov.io/gh/apache/superset/pull/38416/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `63.73% <0.00%> (?)` | | | [sqlite](https://app.codecov.io/gh/apache/superset/pull/38416/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `63.59% <0.00%> (?)` | | Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#carryforward-flags-in-the-pull-request-comment) to find out more. [:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/apache/superset/pull/38416?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache). :loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache). :rocket: New features to boost your workflow: - :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems. - :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges. -- This is an automated message from the Apache Git Service. To
Re: [PR] feat(mcp): add popularity_score computed field for charts, dashboards, datasets [superset]
bito-code-review[bot] commented on PR #38416: URL: https://github.com/apache/superset/pull/38416#issuecomment-4000364837 AI Code Review is in progress (usually takes 3 to 15 minutes unless it's a very large PR). -- 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]
