This is an automated email from the ASF dual-hosted git repository. lyndsi pushed a commit to branch lyndsi/sql-lab-new-explore-button-functionality-and-move-save-dataset-to-split-save-button in repository https://gitbox.apache.org/repos/asf/superset.git
commit a81d536514273873b48a66b577e6b4b3b9eb84d0 Author: AAfghahi <[email protected]> AuthorDate: Wed Jun 29 17:28:12 2022 -0400 pylint --- superset/common/query_context_factory.py | 2 - superset/common/query_context_processor.py | 1 + superset/models/helpers.py | 89 ++++++++++-------------- superset/models/sql_lab.py | 4 +- superset/views/core.py | 8 ++- tests/integration_tests/charts/data/api_tests.py | 1 - 6 files changed, 47 insertions(+), 58 deletions(-) diff --git a/superset/common/query_context_factory.py b/superset/common/query_context_factory.py index eefc5e6830..dc43d28de9 100644 --- a/superset/common/query_context_factory.py +++ b/superset/common/query_context_factory.py @@ -82,8 +82,6 @@ class QueryContextFactory: # pylint: disable=too-few-public-methods # pylint: disable=no-self-use def _convert_to_model(self, datasource: DatasourceDict) -> BaseDatasource: - from superset.datasource.dao import DatasourceDAO - from superset.utils.core import DatasourceType return DatasourceDAO.get_datasource( session=db.session, diff --git a/superset/common/query_context_processor.py b/superset/common/query_context_processor.py index 31f38cb839..3b174dc7a2 100644 --- a/superset/common/query_context_processor.py +++ b/superset/common/query_context_processor.py @@ -185,6 +185,7 @@ class QueryContextProcessor: # support multiple queries from different data sources. # The datasource here can be different backend but the interface is common + # pylint: disable=import-outside-toplevel from superset.models.sql_lab import Query query = "" diff --git a/superset/models/helpers.py b/superset/models/helpers.py index 62eec0ec57..2e5aafe64b 100644 --- a/superset/models/helpers.py +++ b/superset/models/helpers.py @@ -24,10 +24,8 @@ from datetime import datetime, timedelta from json.decoder import JSONDecodeError from typing import ( Any, - Callable, cast, Dict, - Hashable, List, Mapping, NamedTuple, @@ -55,16 +53,16 @@ from flask_appbuilder.models.mixins import AuditMixin from flask_appbuilder.security.sqla.models import User from flask_babel import lazy_gettext as _ from jinja2.exceptions import TemplateError -from sqlalchemy import and_, or_, UniqueConstraint +from sqlalchemy import and_, Column, or_, UniqueConstraint from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import Mapper, Session from sqlalchemy.orm.exc import MultipleResultsFound -from sqlalchemy.sql.elements import ColumnClause, TextClause +from sqlalchemy.sql.elements import ColumnElement, literal_column, TextClause from sqlalchemy.sql.expression import Label, Select, TextAsFrom from sqlalchemy.sql.selectable import Alias, TableClause from sqlalchemy_utils import UUIDType -from superset import app, db, is_feature_enabled, security_manager +from superset import app, is_feature_enabled, security_manager from superset.advanced_data_type.types import AdvancedDataTypeResponse from superset.common.db_query_status import QueryStatus from superset.constants import EMPTY_STRING, NULL_STRING @@ -72,23 +70,20 @@ from superset.errors import ErrorLevel, SupersetError, SupersetErrorType from superset.exceptions import ( AdvancedDataTypeResponseError, QueryClauseValidationException, + QueryObjectValidationError, SupersetSecurityException, ) from superset.extensions import feature_flag_manager -from superset.jinja_context import ( - BaseTemplateProcessor, - ExtraCache, - get_template_processor, -) -from superset.sql_parse import ( - extract_table_references, - has_table_query, - insert_rls, - ParsedQuery, - sanitize_clause, - Table as TableName, +from superset.jinja_context import BaseTemplateProcessor +from superset.sql_parse import has_table_query, insert_rls, ParsedQuery, sanitize_clause +from superset.superset_typing import ( + AdhocMetric, + FilterValue, + FilterValues, + Metric, + OrderBy, + QueryObjectDict, ) -from superset.superset_typing import AdhocColumn from superset.utils import core as utils if TYPE_CHECKING: @@ -96,6 +91,7 @@ if TYPE_CHECKING: from superset.db_engine_specs import BaseEngineSpec from superset.models.core import Database + config = app.config logger = logging.getLogger(__name__) @@ -637,24 +633,6 @@ def clone_model( return target.__class__(**data) -from typing import Any, Dict, List, NamedTuple - -import sqlparse -from sqlalchemy import Column -from sqlalchemy.sql.elements import ColumnElement, Label, literal_column - -from superset.exceptions import QueryObjectValidationError -from superset.superset_typing import ( - AdhocMetric, - FilterValue, - FilterValues, - Metric, - OrderBy, - QueryObjectDict, -) -from superset.utils import core as utils - - # todo(hugh): centralize where this code lives class QueryStringExtended(NamedTuple): applied_template_filters: Optional[List[str]] @@ -672,7 +650,7 @@ class SqlaQuery(NamedTuple): sqla_query: Select -class ExploreMixin: +class ExploreMixin: # pylint: disable=too-many-public-methods """ Allows any flask_appbuilder.Model (Query, Table, etc.) to be used to power a chart inside /explore @@ -759,7 +737,8 @@ class ExploreMixin: def get_extra_cache_keys(query_obj: Dict[str, Any]) -> List[str]: raise NotImplementedError() - def _process_sql_expression( # type: ignore + def _process_sql_expression( # type: ignore # pylint: disable=no-self-use + self, expression: Optional[str], database_id: int, schema: str, @@ -823,8 +802,8 @@ class ExploreMixin: sql = f"{cte}\n{sql}" return sql + @staticmethod def validate_adhoc_subquery( - self, sql: str, database_id: int, default_schema: str, @@ -839,8 +818,6 @@ class ExploreMixin: :raise SupersetSecurityException if sql contains sub-queries or nested sub-queries with table """ - # pylint: disable=import-outside-toplevel - from superset import is_feature_enabled statements = [] for statement in sqlparse.parse(sql): @@ -969,7 +946,9 @@ class ExploreMixin: return df try: - df = self.database.get_df(sql, self.schema, mutator=assign_column_label) # type: ignore + df = self.database.get_df( + sql, self.schema, mutator=assign_column_label # type: ignore + ) except Exception as ex: # pylint: disable=broad-except df = pd.DataFrame() status = QueryStatus.FAILED @@ -1051,7 +1030,7 @@ class ExploreMixin: def adhoc_metric_to_sqla( self, metric: AdhocMetric, - columns_by_name: Dict[str, Dict[str, Any]], + columns_by_name: Dict[str, "TableColumn"], template_processor: Optional[BaseTemplateProcessor] = None, ) -> ColumnElement: """ @@ -1069,11 +1048,13 @@ class ExploreMixin: if expression_type == utils.AdhocMetricExpressionType.SIMPLE: metric_column = metric.get("column") or {} column_name = cast(str, metric_column.get("column_name")) - table_column: Optional[Dict[str, Any]] = columns_by_name.get(column_name) + table_column: Optional["TableColumn"] = columns_by_name.get(column_name) sqla_column = sa.column(column_name) + if table_column: + sqla_column = table_column.get_sqla_col() sqla_metric = self.sqla_aggregations[metric["aggregate"]](sqla_column) elif expression_type == utils.AdhocMetricExpressionType.SQL: - expression = _process_sql_expression( # type: ignore + expression = self._process_sql_expression( # type: ignore expression=metric["sqlExpression"], database_id=self.database_id, schema=self.schema, @@ -1319,12 +1300,14 @@ class ExploreMixin: if granularity not in self.dttm_cols and granularity is not None: granularity = self.main_dttm_col - columns_by_name: Dict[str, TableColumn] = { + columns_by_name: Dict[str, "TableColumn"] = { col.get("column_name"): col for col in self.columns # col.column_name: col for col in self.columns } - metrics_by_name: Dict[str, SqlMetric] = {m.metric_name: m for m in self.metrics} + metrics_by_name: Dict[str, "SqlMetric"] = { + m.metric_name: m for m in self.metrics + } if not granularity and is_timeseries: raise QueryObjectValidationError( @@ -1373,7 +1356,7 @@ class ExploreMixin: if isinstance(col, dict): col = cast(AdhocMetric, col) if col.get("sqlExpression"): - col["sqlExpression"] = _process_sql_expression( # type: ignore + col["sqlExpression"] = self._process_sql_expression( # type: ignore expression=col["sqlExpression"], database_id=self.database_id, schema=self.schema, @@ -1529,7 +1512,7 @@ class ExploreMixin: flt_col = flt["col"] val = flt.get("val") op = flt["op"].upper() - col_obj: Optional[TableColumn] = None + col_obj: Optional["TableColumn"] = None sqla_col: Optional[Column] = None if flt_col == utils.DTTM_ALIAS and is_timeseries and dttm_col: col_obj = dttm_col @@ -1675,7 +1658,9 @@ class ExploreMixin: where = extras.get("where") if where: try: - where = template_processor.process_template(f"({where})") # type: ignore + where = template_processor.process_template( # type: ignore + f"({where})" + ) except TemplateError as ex: raise QueryObjectValidationError( _( @@ -1687,7 +1672,9 @@ class ExploreMixin: having = extras.get("having") if having: try: - having = template_processor.process_template(f"({having})") # type: ignore + having = template_processor.process_template( # type: ignore + f"({having})" + ) except TemplateError as ex: raise QueryObjectValidationError( _( diff --git a/superset/models/sql_lab.py b/superset/models/sql_lab.py index d610410ded..dc5f3dd51d 100644 --- a/superset/models/sql_lab.py +++ b/superset/models/sql_lab.py @@ -56,7 +56,7 @@ if TYPE_CHECKING: from superset.db_engine_specs import BaseEngineSpec -class Query(Model, ExtraJSONMixin, ExploreMixin): +class Query(Model, ExtraJSONMixin, ExploreMixin): # pylint: disable=abstract-method """ORM model for SQL query Now that SQL Lab support multi-statement execution, an entry in this @@ -175,6 +175,7 @@ class Query(Model, ExtraJSONMixin, ExploreMixin): @property def columns(self) -> List[ResultSetColumnType]: # todo(hughhh): move this logic into a base class + # pylint: disable=import-outside-toplevel from superset.utils.core import GenericDataType bool_types = ("BOOL",) @@ -193,6 +194,7 @@ class Query(Model, ExtraJSONMixin, ExploreMixin): date_types = ("DATE", "TIME") str_types = ("VARCHAR", "STRING", "CHAR") columns = [] + col_type = "" for col in self.extra.get("columns", []): computed_column = {**col} col_type = col.get("type") diff --git a/superset/views/core.py b/superset/views/core.py index 8067b4e4a5..4699ffb1ae 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -647,7 +647,9 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods errors=[ SupersetError( message=__( - "This chart type is not supported when using an unsaved query as a chart source. Create a dataset to visualize your data." + "This chart type is not supported when using an unsaved" + " query as a chart source. Create a dataset to visualize" + " your data." ), error_type=SupersetErrorType.VIZ_TYPE_REQUIRES_DATASET_ERROR, level=ErrorLevel.ERROR, @@ -788,9 +790,8 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods value = GetFormDataCommand(parameters).run() initial_form_data = json.loads(value) if value else {} - from superset.datasource.dao import DatasourceDAO + # pylint: disable=import-outside-toplevel from superset.models.helpers import ExploreMixin - from superset.utils.core import DatasourceType # Handle SIP-68 Models or explore view # API will always use /explore/<datasource_type>/<int:datasource_id>/ to query @@ -893,6 +894,7 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods "form_data": form_data, "datasource_id": datasource_id, "datasource_type": datasource_type, + "datasource_name": datasource_name, "slice": slc.data if slc else None, "standalone": standalone_mode, "force": force, diff --git a/tests/integration_tests/charts/data/api_tests.py b/tests/integration_tests/charts/data/api_tests.py index 67b9826d26..683e3f9b0d 100644 --- a/tests/integration_tests/charts/data/api_tests.py +++ b/tests/integration_tests/charts/data/api_tests.py @@ -332,7 +332,6 @@ class TestPostChartDataApi(BaseTestChartDataApi): {"column": "gender"}, {"column": "num"}, {"column": "name"}, - {"column": "__time_range"}, ], ) expected_row_count = self.get_expected_row_count("client_id_2")
