mirror of
https://github.com/apache/superset.git
synced 2024-09-12 08:39:45 -04:00
Re-enable pylint for some model files (#8770)
* Allow id as a valid name for pylint * Re-enable pylint for superset/models/core.py * re-enable pylint for superset/models/sql_lab.py * re-enable pylint for superset/models/schedules.py * re-enable pylint for superset/models/helpers.py * re-enable pylint for superset/models/annotations.py * re-enable pylint on superset/models/tags.py * a couple more fixes after black formatting... * Add another inline pylint disable * Fix black * Move to inline disables for 'id' attribute on models * Fix lint disables after black reformatted them
This commit is contained in:
parent
6c130b32ad
commit
adf2cc2039
@ -14,7 +14,6 @@
|
|||||||
# KIND, either express or implied. See the License for the
|
# KIND, either express or implied. See the License for the
|
||||||
# specific language governing permissions and limitations
|
# specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
# pylint: disable=C,R,W
|
|
||||||
"""a collection of Annotation-related models"""
|
"""a collection of Annotation-related models"""
|
||||||
from flask_appbuilder import Model
|
from flask_appbuilder import Model
|
||||||
from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, String, Text
|
from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, String, Text
|
||||||
@ -28,7 +27,7 @@ class AnnotationLayer(Model, AuditMixinNullable):
|
|||||||
"""A logical namespace for a set of annotations"""
|
"""A logical namespace for a set of annotations"""
|
||||||
|
|
||||||
__tablename__ = "annotation_layer"
|
__tablename__ = "annotation_layer"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
name = Column(String(250))
|
name = Column(String(250))
|
||||||
descr = Column(Text)
|
descr = Column(Text)
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ class Annotation(Model, AuditMixinNullable):
|
|||||||
"""Time-related annotation"""
|
"""Time-related annotation"""
|
||||||
|
|
||||||
__tablename__ = "annotation"
|
__tablename__ = "annotation"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
start_dttm = Column(DateTime)
|
start_dttm = Column(DateTime)
|
||||||
end_dttm = Column(DateTime)
|
end_dttm = Column(DateTime)
|
||||||
layer_id = Column(Integer, ForeignKey("annotation_layer.id"), nullable=False)
|
layer_id = Column(Integer, ForeignKey("annotation_layer.id"), nullable=False)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
# KIND, either express or implied. See the License for the
|
# KIND, either express or implied. See the License for the
|
||||||
# specific language governing permissions and limitations
|
# specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
# pylint: disable=C,R,W
|
# pylint: disable=line-too-long,unused-argument,ungrouped-imports
|
||||||
"""A collection of ORM sqlalchemy models for Superset"""
|
"""A collection of ORM sqlalchemy models for Superset"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@ -66,8 +66,9 @@ from superset.utils import cache as cache_util, core as utils
|
|||||||
from superset.viz import BaseViz, viz_types
|
from superset.viz import BaseViz, viz_types
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from superset.connectors.base.models import BaseDatasource
|
from superset.connectors.base.models import ( # pylint: disable=unused-import
|
||||||
|
BaseDatasource,
|
||||||
|
)
|
||||||
|
|
||||||
config = app.config
|
config = app.config
|
||||||
custom_password_store = config["SQLALCHEMY_CUSTOM_PASSWORD_STORE"]
|
custom_password_store = config["SQLALCHEMY_CUSTOM_PASSWORD_STORE"]
|
||||||
@ -76,6 +77,7 @@ log_query = config["QUERY_LOGGER"]
|
|||||||
metadata = Model.metadata # pylint: disable=no-member
|
metadata = Model.metadata # pylint: disable=no-member
|
||||||
|
|
||||||
PASSWORD_MASK = "X" * 10
|
PASSWORD_MASK = "X" * 10
|
||||||
|
DB_CONNECTION_MUTATOR = config["DB_CONNECTION_MUTATOR"]
|
||||||
|
|
||||||
|
|
||||||
def set_related_perm(mapper, connection, target):
|
def set_related_perm(mapper, connection, target):
|
||||||
@ -93,8 +95,8 @@ def copy_dashboard(mapper, connection, target):
|
|||||||
if dashboard_id is None:
|
if dashboard_id is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
Session = sessionmaker(autoflush=False)
|
session_class = sessionmaker(autoflush=False)
|
||||||
session = Session(bind=connection)
|
session = session_class(bind=connection)
|
||||||
new_user = session.query(User).filter_by(id=target.id).first()
|
new_user = session.query(User).filter_by(id=target.id).first()
|
||||||
|
|
||||||
# copy template dashboard to user
|
# copy template dashboard to user
|
||||||
@ -126,16 +128,16 @@ class Url(Model, AuditMixinNullable):
|
|||||||
"""Used for the short url feature"""
|
"""Used for the short url feature"""
|
||||||
|
|
||||||
__tablename__ = "url"
|
__tablename__ = "url"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
url = Column(Text)
|
url = Column(Text)
|
||||||
|
|
||||||
|
|
||||||
class KeyValue(Model):
|
class KeyValue(Model): # pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
"""Used for any type of key-value store"""
|
"""Used for any type of key-value store"""
|
||||||
|
|
||||||
__tablename__ = "keyvalue"
|
__tablename__ = "keyvalue"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
value = Column(Text, nullable=False)
|
value = Column(Text, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
@ -144,7 +146,7 @@ class CssTemplate(Model, AuditMixinNullable):
|
|||||||
"""CSS templates for dashboards"""
|
"""CSS templates for dashboards"""
|
||||||
|
|
||||||
__tablename__ = "css_templates"
|
__tablename__ = "css_templates"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
template_name = Column(String(250))
|
template_name = Column(String(250))
|
||||||
css = Column(Text, default="")
|
css = Column(Text, default="")
|
||||||
|
|
||||||
@ -158,12 +160,14 @@ slice_user = Table(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Slice(Model, AuditMixinNullable, ImportMixin):
|
class Slice(
|
||||||
|
Model, AuditMixinNullable, ImportMixin
|
||||||
|
): # pylint: disable=too-many-public-methods
|
||||||
|
|
||||||
"""A slice is essentially a report or a view on data"""
|
"""A slice is essentially a report or a view on data"""
|
||||||
|
|
||||||
__tablename__ = "slices"
|
__tablename__ = "slices"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
slice_name = Column(String(250))
|
slice_name = Column(String(250))
|
||||||
datasource_id = Column(Integer)
|
datasource_id = Column(Integer)
|
||||||
datasource_type = Column(String(200))
|
datasource_type = Column(String(200))
|
||||||
@ -175,6 +179,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
|||||||
perm = Column(String(1000))
|
perm = Column(String(1000))
|
||||||
schema_perm = Column(String(1000))
|
schema_perm = Column(String(1000))
|
||||||
owners = relationship(security_manager.user_model, secondary=slice_user)
|
owners = relationship(security_manager.user_model, secondary=slice_user)
|
||||||
|
token = ""
|
||||||
|
|
||||||
export_fields = [
|
export_fields = [
|
||||||
"slice_name",
|
"slice_name",
|
||||||
@ -208,6 +213,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
|||||||
cache_timeout=self.cache_timeout,
|
cache_timeout=self.cache_timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# pylint: disable=using-constant-test
|
||||||
@datasource.getter # type: ignore
|
@datasource.getter # type: ignore
|
||||||
@utils.memoized
|
@utils.memoized
|
||||||
def get_datasource(self) -> Optional["BaseDatasource"]:
|
def get_datasource(self) -> Optional["BaseDatasource"]:
|
||||||
@ -230,6 +236,8 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
|||||||
datasource = self.datasource
|
datasource = self.datasource
|
||||||
return datasource.url if datasource else None
|
return datasource.url if datasource else None
|
||||||
|
|
||||||
|
# pylint: enable=using-constant-test
|
||||||
|
|
||||||
@property # type: ignore
|
@property # type: ignore
|
||||||
@utils.memoized
|
@utils.memoized
|
||||||
def viz(self) -> BaseViz:
|
def viz(self) -> BaseViz:
|
||||||
@ -249,7 +257,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
|||||||
try:
|
try:
|
||||||
d = self.viz.data
|
d = self.viz.data
|
||||||
self.token = d.get("token") # type: ignore
|
self.token = d.get("token") # type: ignore
|
||||||
except Exception as e:
|
except Exception as e: # pylint: disable=broad-except
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
d["error"] = str(e)
|
d["error"] = str(e)
|
||||||
return {
|
return {
|
||||||
@ -275,7 +283,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
|||||||
form_data: Dict[str, Any] = {}
|
form_data: Dict[str, Any] = {}
|
||||||
try:
|
try:
|
||||||
form_data = json.loads(self.params)
|
form_data = json.loads(self.params)
|
||||||
except Exception as e:
|
except Exception as e: # pylint: disable=broad-except
|
||||||
logging.error("Malformed json in slice's params")
|
logging.error("Malformed json in slice's params")
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
form_data.update(
|
form_data.update(
|
||||||
@ -322,9 +330,8 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def slice_link(self) -> Markup:
|
def slice_link(self) -> Markup:
|
||||||
url = self.slice_url
|
|
||||||
name = escape(self.chart)
|
name = escape(self.chart)
|
||||||
return Markup(f'<a href="{url}">{name}</a>')
|
return Markup(f'<a href="{self.url}">{name}</a>')
|
||||||
|
|
||||||
def get_viz(self, force: bool = False) -> BaseViz:
|
def get_viz(self, force: bool = False) -> BaseViz:
|
||||||
"""Creates :py:class:viz.BaseViz object from the url_params_multidict.
|
"""Creates :py:class:viz.BaseViz object from the url_params_multidict.
|
||||||
@ -392,7 +399,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
|
|||||||
session.flush()
|
session.flush()
|
||||||
return slc_to_override.id
|
return slc_to_override.id
|
||||||
session.add(slc_to_import)
|
session.add(slc_to_import)
|
||||||
logging.info("Final slice: {}".format(slc_to_import.to_json()))
|
logging.info("Final slice: %s", str(slc_to_import.to_json()))
|
||||||
session.flush()
|
session.flush()
|
||||||
return slc_to_import.id
|
return slc_to_import.id
|
||||||
|
|
||||||
@ -423,12 +430,14 @@ dashboard_user = Table(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
class Dashboard( # pylint: disable=too-many-instance-attributes
|
||||||
|
Model, AuditMixinNullable, ImportMixin
|
||||||
|
):
|
||||||
|
|
||||||
"""The dashboard object!"""
|
"""The dashboard object!"""
|
||||||
|
|
||||||
__tablename__ = "dashboards"
|
__tablename__ = "dashboards"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
dashboard_title = Column(String(500))
|
dashboard_title = Column(String(500))
|
||||||
position_json = Column(utils.MediumText())
|
position_json = Column(utils.MediumText())
|
||||||
description = Column(Text)
|
description = Column(Text)
|
||||||
@ -470,7 +479,7 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
|||||||
return "/superset/dashboard/{}/?preselect_filters={}".format(
|
return "/superset/dashboard/{}/?preselect_filters={}".format(
|
||||||
self.slug or self.id, filters
|
self.slug or self.id, filters
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
pass
|
pass
|
||||||
return f"/superset/dashboard/{self.slug or self.id}/"
|
return f"/superset/dashboard/{self.slug or self.id}/"
|
||||||
|
|
||||||
@ -485,8 +494,8 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
|||||||
@property
|
@property
|
||||||
def sqla_metadata(self) -> None:
|
def sqla_metadata(self) -> None:
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
metadata = MetaData(bind=self.get_sqla_engine())
|
meta = MetaData(bind=self.get_sqla_engine())
|
||||||
metadata.reflect()
|
meta.reflect()
|
||||||
|
|
||||||
def dashboard_link(self) -> Markup:
|
def dashboard_link(self) -> Markup:
|
||||||
title = escape(self.dashboard_title or "<empty>")
|
title = escape(self.dashboard_title or "<empty>")
|
||||||
@ -523,7 +532,7 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_obj(
|
def import_obj( # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
||||||
cls, dashboard_to_import: "Dashboard", import_time: Optional[int] = None
|
cls, dashboard_to_import: "Dashboard", import_time: Optional[int] = None
|
||||||
) -> int:
|
) -> int:
|
||||||
"""Imports the dashboard from the object to the database.
|
"""Imports the dashboard from the object to the database.
|
||||||
@ -579,10 +588,10 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
|||||||
dashboard.position_json = json.dumps(position_data)
|
dashboard.position_json = json.dumps(position_data)
|
||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
"Started import of the dashboard: {}".format(dashboard_to_import.to_json())
|
"Started import of the dashboard: %s", dashboard_to_import.to_json()
|
||||||
)
|
)
|
||||||
session = db.session
|
session = db.session
|
||||||
logging.info("Dashboard has {} slices".format(len(dashboard_to_import.slices)))
|
logging.info("Dashboard has %d slices", len(dashboard_to_import.slices))
|
||||||
# copy slices object as Slice.import_slice will mutate the slice
|
# copy slices object as Slice.import_slice will mutate the slice
|
||||||
# and will remove the existing dashboard - slice association
|
# and will remove the existing dashboard - slice association
|
||||||
slices = copy(dashboard_to_import.slices)
|
slices = copy(dashboard_to_import.slices)
|
||||||
@ -599,9 +608,9 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
|||||||
}
|
}
|
||||||
for slc in slices:
|
for slc in slices:
|
||||||
logging.info(
|
logging.info(
|
||||||
"Importing slice {} from the dashboard: {}".format(
|
"Importing slice %s from the dashboard: %s",
|
||||||
slc.to_json(), dashboard_to_import.dashboard_title
|
slc.to_json(),
|
||||||
)
|
dashboard_to_import.dashboard_title,
|
||||||
)
|
)
|
||||||
remote_slc = remote_id_slice_map.get(slc.id)
|
remote_slc = remote_id_slice_map.get(slc.id)
|
||||||
new_slc_id = Slice.import_obj(slc, remote_slc, import_time=import_time)
|
new_slc_id = Slice.import_obj(slc, remote_slc, import_time=import_time)
|
||||||
@ -677,14 +686,16 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
|||||||
existing_dashboard.slices = new_slices
|
existing_dashboard.slices = new_slices
|
||||||
session.flush()
|
session.flush()
|
||||||
return existing_dashboard.id
|
return existing_dashboard.id
|
||||||
else:
|
|
||||||
dashboard_to_import.slices = new_slices
|
dashboard_to_import.slices = new_slices
|
||||||
session.add(dashboard_to_import)
|
session.add(dashboard_to_import)
|
||||||
session.flush()
|
session.flush()
|
||||||
return dashboard_to_import.id # type: ignore
|
return dashboard_to_import.id # type: ignore
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_dashboards(cls, dashboard_ids: List) -> str:
|
def export_dashboards( # pylint: disable=too-many-locals
|
||||||
|
cls, dashboard_ids: List
|
||||||
|
) -> str:
|
||||||
copied_dashboards = []
|
copied_dashboards = []
|
||||||
datasource_ids = set()
|
datasource_ids = set()
|
||||||
for dashboard_id in dashboard_ids:
|
for dashboard_id in dashboard_ids:
|
||||||
@ -741,7 +752,9 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Database(Model, AuditMixinNullable, ImportMixin):
|
class Database(
|
||||||
|
Model, AuditMixinNullable, ImportMixin
|
||||||
|
): # pylint: disable=too-many-public-methods
|
||||||
|
|
||||||
"""An ORM object that stores Database related information"""
|
"""An ORM object that stores Database related information"""
|
||||||
|
|
||||||
@ -749,7 +762,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
type = "table"
|
type = "table"
|
||||||
__table_args__ = (UniqueConstraint("database_name"),)
|
__table_args__ = (UniqueConstraint("database_name"),)
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
verbose_name = Column(String(250), unique=True)
|
verbose_name = Column(String(250), unique=True)
|
||||||
# short unique name, used in permissions
|
# short unique name, used in permissions
|
||||||
database_name = Column(String(250), unique=True, nullable=False)
|
database_name = Column(String(250), unique=True, nullable=False)
|
||||||
@ -763,7 +776,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
allow_ctas = Column(Boolean, default=False)
|
allow_ctas = Column(Boolean, default=False)
|
||||||
allow_dml = Column(Boolean, default=False)
|
allow_dml = Column(Boolean, default=False)
|
||||||
force_ctas_schema = Column(String(250))
|
force_ctas_schema = Column(String(250))
|
||||||
allow_multi_schema_metadata_fetch = Column(Boolean, default=False)
|
allow_multi_schema_metadata_fetch = Column( # pylint: disable=invalid-name
|
||||||
|
Boolean, default=False
|
||||||
|
)
|
||||||
extra = Column(
|
extra = Column(
|
||||||
Text,
|
Text,
|
||||||
default=textwrap.dedent(
|
default=textwrap.dedent(
|
||||||
@ -836,8 +851,8 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def backend(self) -> str:
|
def backend(self) -> str:
|
||||||
url = make_url(self.sqlalchemy_uri_decrypted)
|
sqlalchemy_url = make_url(self.sqlalchemy_uri_decrypted)
|
||||||
return url.get_backend_name()
|
return sqlalchemy_url.get_backend_name()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def metadata_cache_timeout(self) -> Dict[str, Any]:
|
def metadata_cache_timeout(self) -> Dict[str, Any]:
|
||||||
@ -864,12 +879,14 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
return self.get_extra().get("default_schemas", [])
|
return self.get_extra().get("default_schemas", [])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_password_masked_url_from_uri(cls, uri: str):
|
def get_password_masked_url_from_uri(cls, uri: str): # pylint: disable=invalid-name
|
||||||
url = make_url(uri)
|
sqlalchemy_url = make_url(uri)
|
||||||
return cls.get_password_masked_url(url)
|
return cls.get_password_masked_url(sqlalchemy_url)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_password_masked_url(cls, url: URL) -> URL:
|
def get_password_masked_url(
|
||||||
|
cls, url: URL # pylint: disable=redefined-outer-name
|
||||||
|
) -> URL:
|
||||||
url_copy = deepcopy(url)
|
url_copy = deepcopy(url)
|
||||||
if url_copy.password is not None:
|
if url_copy.password is not None:
|
||||||
url_copy.password = PASSWORD_MASK
|
url_copy.password = PASSWORD_MASK
|
||||||
@ -884,7 +901,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
self.sqlalchemy_uri = str(conn) # hides the password
|
self.sqlalchemy_uri = str(conn) # hides the password
|
||||||
|
|
||||||
def get_effective_user(
|
def get_effective_user(
|
||||||
self, url: URL, user_name: Optional[str] = None
|
self,
|
||||||
|
url: URL, # pylint: disable=redefined-outer-name
|
||||||
|
user_name: Optional[str] = None,
|
||||||
) -> Optional[str]:
|
) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Get the effective user, especially during impersonation.
|
Get the effective user, especially during impersonation.
|
||||||
@ -914,18 +933,18 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
source: Optional[int] = None,
|
source: Optional[int] = None,
|
||||||
) -> Engine:
|
) -> Engine:
|
||||||
extra = self.get_extra()
|
extra = self.get_extra()
|
||||||
url = make_url(self.sqlalchemy_uri_decrypted)
|
sqlalchemy_url = make_url(self.sqlalchemy_uri_decrypted)
|
||||||
url = self.db_engine_spec.adjust_database_uri(url, schema)
|
sqlalchemy_url = self.db_engine_spec.adjust_database_uri(sqlalchemy_url, schema)
|
||||||
effective_username = self.get_effective_user(url, user_name)
|
effective_username = self.get_effective_user(sqlalchemy_url, user_name)
|
||||||
# If using MySQL or Presto for example, will set url.username
|
# If using MySQL or Presto for example, will set url.username
|
||||||
# If using Hive, will not do anything yet since that relies on a
|
# If using Hive, will not do anything yet since that relies on a
|
||||||
# configuration parameter instead.
|
# configuration parameter instead.
|
||||||
self.db_engine_spec.modify_url_for_impersonation(
|
self.db_engine_spec.modify_url_for_impersonation(
|
||||||
url, self.impersonate_user, effective_username
|
sqlalchemy_url, self.impersonate_user, effective_username
|
||||||
)
|
)
|
||||||
|
|
||||||
masked_url = self.get_password_masked_url(url)
|
masked_url = self.get_password_masked_url(sqlalchemy_url)
|
||||||
logging.info("Database.get_sqla_engine(). Masked URL: {0}".format(masked_url))
|
logging.info("Database.get_sqla_engine(). Masked URL: %s", str(masked_url))
|
||||||
|
|
||||||
params = extra.get("engine_params", {})
|
params = extra.get("engine_params", {})
|
||||||
if nullpool:
|
if nullpool:
|
||||||
@ -935,7 +954,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
configuration: Dict[str, Any] = {}
|
configuration: Dict[str, Any] = {}
|
||||||
configuration.update(
|
configuration.update(
|
||||||
self.db_engine_spec.get_configuration_for_impersonation(
|
self.db_engine_spec.get_configuration_for_impersonation(
|
||||||
str(url), self.impersonate_user, effective_username
|
str(sqlalchemy_url), self.impersonate_user, effective_username
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if configuration:
|
if configuration:
|
||||||
@ -945,12 +964,11 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
|
|
||||||
params.update(self.get_encrypted_extra())
|
params.update(self.get_encrypted_extra())
|
||||||
|
|
||||||
DB_CONNECTION_MUTATOR = config["DB_CONNECTION_MUTATOR"]
|
|
||||||
if DB_CONNECTION_MUTATOR:
|
if DB_CONNECTION_MUTATOR:
|
||||||
url, params = DB_CONNECTION_MUTATOR(
|
sqlalchemy_url, params = DB_CONNECTION_MUTATOR(
|
||||||
url, params, effective_username, security_manager, source
|
sqlalchemy_url, params, effective_username, security_manager, source
|
||||||
)
|
)
|
||||||
return create_engine(url, **params)
|
return create_engine(sqlalchemy_url, **params)
|
||||||
|
|
||||||
def get_reserved_words(self) -> Set[str]:
|
def get_reserved_words(self) -> Set[str]:
|
||||||
return self.get_dialect().preparer.reserved_words
|
return self.get_dialect().preparer.reserved_words
|
||||||
@ -958,7 +976,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
def get_quoter(self):
|
def get_quoter(self):
|
||||||
return self.get_dialect().identifier_preparer.quote
|
return self.get_dialect().identifier_preparer.quote
|
||||||
|
|
||||||
def get_df(
|
def get_df( # pylint: disable=too-many-locals
|
||||||
self, sql: str, schema: str, mutator: Optional[Callable] = None
|
self, sql: str, schema: str, mutator: Optional[Callable] = None
|
||||||
) -> pd.DataFrame:
|
) -> pd.DataFrame:
|
||||||
sqls = [str(s).strip(" ;") for s in sqlparse.parse(sql)]
|
sqls = [str(s).strip(" ;") for s in sqlparse.parse(sql)]
|
||||||
@ -982,9 +1000,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
|
|
||||||
with closing(engine.raw_connection()) as conn:
|
with closing(engine.raw_connection()) as conn:
|
||||||
with closing(conn.cursor()) as cursor:
|
with closing(conn.cursor()) as cursor:
|
||||||
for sql in sqls[:-1]:
|
for sql_ in sqls[:-1]:
|
||||||
_log_query(sql)
|
_log_query(sql_)
|
||||||
self.db_engine_spec.execute(cursor, sql)
|
self.db_engine_spec.execute(cursor, sql_)
|
||||||
cursor.fetchall()
|
cursor.fetchall()
|
||||||
|
|
||||||
_log_query(sqls[-1])
|
_log_query(sqls[-1])
|
||||||
@ -1012,12 +1030,14 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
|
|
||||||
sql = str(qry.compile(engine, compile_kwargs={"literal_binds": True}))
|
sql = str(qry.compile(engine, compile_kwargs={"literal_binds": True}))
|
||||||
|
|
||||||
if engine.dialect.identifier_preparer._double_percents:
|
if (
|
||||||
|
engine.dialect.identifier_preparer._double_percents # pylint: disable=protected-access
|
||||||
|
):
|
||||||
sql = sql.replace("%%", "%")
|
sql = sql.replace("%%", "%")
|
||||||
|
|
||||||
return sql
|
return sql
|
||||||
|
|
||||||
def select_star(
|
def select_star( # pylint: disable=too-many-arguments
|
||||||
self,
|
self,
|
||||||
table_name: str,
|
table_name: str,
|
||||||
sql: Optional[str] = None,
|
sql: Optional[str] = None,
|
||||||
@ -1109,7 +1129,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
return [
|
return [
|
||||||
utils.DatasourceName(table=table, schema=schema) for table in tables
|
utils.DatasourceName(table=table, schema=schema) for table in tables
|
||||||
]
|
]
|
||||||
except Exception as e:
|
except Exception as e: # pylint: disable=broad-except
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
|
|
||||||
@cache_util.memoized_func(
|
@cache_util.memoized_func(
|
||||||
@ -1139,7 +1159,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
database=self, inspector=self.inspector, schema=schema
|
database=self, inspector=self.inspector, schema=schema
|
||||||
)
|
)
|
||||||
return [utils.DatasourceName(table=view, schema=schema) for view in views]
|
return [utils.DatasourceName(table=view, schema=schema) for view in views]
|
||||||
except Exception as e:
|
except Exception as e: # pylint: disable=broad-except
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
|
|
||||||
@cache_util.memoized_func(
|
@cache_util.memoized_func(
|
||||||
@ -1232,7 +1252,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
return self.inspector.get_foreign_keys(table_name, schema)
|
return self.inspector.get_foreign_keys(table_name, schema)
|
||||||
|
|
||||||
def get_schema_access_for_csv_upload(self) -> List[str]:
|
def get_schema_access_for_csv_upload( # pylint: disable=invalid-name
|
||||||
|
self
|
||||||
|
) -> List[str]:
|
||||||
return self.get_extra().get("schemas_allowed_for_csv_upload", [])
|
return self.get_extra().get("schemas_allowed_for_csv_upload", [])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -1269,13 +1291,13 @@ sqla.event.listen(Database, "after_insert", security_manager.set_perm)
|
|||||||
sqla.event.listen(Database, "after_update", security_manager.set_perm)
|
sqla.event.listen(Database, "after_update", security_manager.set_perm)
|
||||||
|
|
||||||
|
|
||||||
class Log(Model):
|
class Log(Model): # pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
"""ORM object used to log Superset actions to the database"""
|
"""ORM object used to log Superset actions to the database"""
|
||||||
|
|
||||||
__tablename__ = "logs"
|
__tablename__ = "logs"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
action = Column(String(512))
|
action = Column(String(512))
|
||||||
user_id = Column(Integer, ForeignKey("ab_user.id"))
|
user_id = Column(Integer, ForeignKey("ab_user.id"))
|
||||||
dashboard_id = Column(Integer)
|
dashboard_id = Column(Integer)
|
||||||
@ -1289,10 +1311,10 @@ class Log(Model):
|
|||||||
referrer = Column(String(1024))
|
referrer = Column(String(1024))
|
||||||
|
|
||||||
|
|
||||||
class FavStar(Model):
|
class FavStar(Model): # pylint: disable=too-few-public-methods
|
||||||
__tablename__ = "favstar"
|
__tablename__ = "favstar"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
user_id = Column(Integer, ForeignKey("ab_user.id"))
|
user_id = Column(Integer, ForeignKey("ab_user.id"))
|
||||||
class_name = Column(String(50))
|
class_name = Column(String(50))
|
||||||
obj_id = Column(Integer)
|
obj_id = Column(Integer)
|
||||||
@ -1303,7 +1325,7 @@ class DatasourceAccessRequest(Model, AuditMixinNullable):
|
|||||||
"""ORM model for the access requests for datasources and dbs."""
|
"""ORM model for the access requests for datasources and dbs."""
|
||||||
|
|
||||||
__tablename__ = "access_request"
|
__tablename__ = "access_request"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
|
|
||||||
datasource_id = Column(Integer)
|
datasource_id = Column(Integer)
|
||||||
datasource_type = Column(String(200))
|
datasource_type = Column(String(200))
|
||||||
@ -1337,33 +1359,33 @@ class DatasourceAccessRequest(Model, AuditMixinNullable):
|
|||||||
action_list = ""
|
action_list = ""
|
||||||
perm = self.datasource.perm # pylint: disable=no-member
|
perm = self.datasource.perm # pylint: disable=no-member
|
||||||
pv = security_manager.find_permission_view_menu("datasource_access", perm)
|
pv = security_manager.find_permission_view_menu("datasource_access", perm)
|
||||||
for r in pv.role:
|
for role in pv.role:
|
||||||
if r.name in self.ROLES_BLACKLIST:
|
if role.name in self.ROLES_BLACKLIST:
|
||||||
continue
|
continue
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
url = (
|
href = (
|
||||||
f"/superset/approve?datasource_type={self.datasource_type}&"
|
f"/superset/approve?datasource_type={self.datasource_type}&"
|
||||||
f"datasource_id={self.datasource_id}&"
|
f"datasource_id={self.datasource_id}&"
|
||||||
f"created_by={self.created_by.username}&role_to_grant={r.name}"
|
f"created_by={self.created_by.username}&role_to_grant={role.name}"
|
||||||
)
|
)
|
||||||
href = '<a href="{}">Grant {} Role</a>'.format(url, r.name)
|
link = '<a href="{}">Grant {} Role</a>'.format(href, role.name)
|
||||||
action_list = action_list + "<li>" + href + "</li>"
|
action_list = action_list + "<li>" + link + "</li>"
|
||||||
return "<ul>" + action_list + "</ul>"
|
return "<ul>" + action_list + "</ul>"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_roles(self) -> str:
|
def user_roles(self) -> str:
|
||||||
action_list = ""
|
action_list = ""
|
||||||
for r in self.created_by.roles: # pylint: disable=no-member
|
for role in self.created_by.roles: # pylint: disable=no-member
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
url = (
|
href = (
|
||||||
f"/superset/approve?datasource_type={self.datasource_type}&"
|
f"/superset/approve?datasource_type={self.datasource_type}&"
|
||||||
f"datasource_id={self.datasource_id}&"
|
f"datasource_id={self.datasource_id}&"
|
||||||
f"created_by={self.created_by.username}&role_to_extend={r.name}"
|
f"created_by={self.created_by.username}&role_to_extend={role.name}"
|
||||||
)
|
)
|
||||||
href = '<a href="{}">Extend {} Role</a>'.format(url, r.name)
|
link = '<a href="{}">Extend {} Role</a>'.format(href, role.name)
|
||||||
if r.name in self.ROLES_BLACKLIST:
|
if role.name in self.ROLES_BLACKLIST:
|
||||||
href = "{} Role".format(r.name)
|
link = "{} Role".format(role.name)
|
||||||
action_list = action_list + "<li>" + href + "</li>"
|
action_list = action_list + "<li>" + link + "</li>"
|
||||||
return "<ul>" + action_list + "</ul>"
|
return "<ul>" + action_list + "</ul>"
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
# KIND, either express or implied. See the License for the
|
# KIND, either express or implied. See the License for the
|
||||||
# specific language governing permissions and limitations
|
# specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
# pylint: disable=C,R,W
|
|
||||||
"""a collection of model-related helper classes and functions"""
|
"""a collection of model-related helper classes and functions"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@ -22,6 +21,8 @@ import re
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
# isort and pylint disagree, isort should win
|
||||||
|
# pylint: disable=ungrouped-imports
|
||||||
import humanize
|
import humanize
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
import yaml
|
import yaml
|
||||||
@ -38,10 +39,12 @@ from superset.utils.core import QueryStatus
|
|||||||
def json_to_dict(json_str):
|
def json_to_dict(json_str):
|
||||||
if json_str:
|
if json_str:
|
||||||
val = re.sub(",[ \t\r\n]+}", "}", json_str)
|
val = re.sub(",[ \t\r\n]+}", "}", json_str)
|
||||||
val = re.sub(",[ \t\r\n]+\]", "]", val)
|
val = re.sub(
|
||||||
|
",[ \t\r\n]+\]", "]", val # pylint: disable=anomalous-backslash-in-string
|
||||||
|
)
|
||||||
return json.loads(val)
|
return json.loads(val)
|
||||||
else:
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class ImportMixin(object):
|
class ImportMixin(object):
|
||||||
@ -83,24 +86,24 @@ class ImportMixin(object):
|
|||||||
if not include_parent_ref:
|
if not include_parent_ref:
|
||||||
parent_ref = cls.__mapper__.relationships.get(cls.export_parent)
|
parent_ref = cls.__mapper__.relationships.get(cls.export_parent)
|
||||||
if parent_ref:
|
if parent_ref:
|
||||||
parent_excludes = {c.name for c in parent_ref.local_columns}
|
parent_excludes = {column.name for column in parent_ref.local_columns}
|
||||||
|
|
||||||
def formatter(c):
|
def formatter(column):
|
||||||
return (
|
return (
|
||||||
"{0} Default ({1})".format(str(c.type), c.default.arg)
|
"{0} Default ({1})".format(str(column.type), column.default.arg)
|
||||||
if c.default
|
if column.default
|
||||||
else str(c.type)
|
else str(column.type)
|
||||||
)
|
)
|
||||||
|
|
||||||
schema = {
|
schema = {
|
||||||
c.name: formatter(c)
|
column.name: formatter(column)
|
||||||
for c in cls.__table__.columns
|
for column in cls.__table__.columns
|
||||||
if (c.name in cls.export_fields and c.name not in parent_excludes)
|
if (column.name in cls.export_fields and column.name not in parent_excludes)
|
||||||
}
|
}
|
||||||
if recursive:
|
if recursive:
|
||||||
for c in cls.export_children:
|
for column in cls.export_children:
|
||||||
child_class = cls.__mapper__.relationships[c].argument.class_
|
child_class = cls.__mapper__.relationships[column].argument.class_
|
||||||
schema[c] = [
|
schema[column] = [
|
||||||
child_class.export_schema(
|
child_class.export_schema(
|
||||||
recursive=recursive, include_parent_ref=include_parent_ref
|
recursive=recursive, include_parent_ref=include_parent_ref
|
||||||
)
|
)
|
||||||
@ -108,8 +111,12 @@ class ImportMixin(object):
|
|||||||
return schema
|
return schema
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_from_dict(cls, session, dict_rep, parent=None, recursive=True, sync=[]):
|
def import_from_dict(
|
||||||
|
cls, session, dict_rep, parent=None, recursive=True, sync=None
|
||||||
|
): # pylint: disable=too-many-arguments,too-many-locals,too-many-branches
|
||||||
"""Import obj from a dictionary"""
|
"""Import obj from a dictionary"""
|
||||||
|
if sync is None:
|
||||||
|
sync = []
|
||||||
parent_refs = cls._parent_foreign_key_mappings()
|
parent_refs = cls._parent_foreign_key_mappings()
|
||||||
export_fields = set(cls.export_fields) | set(parent_refs.keys())
|
export_fields = set(cls.export_fields) | set(parent_refs.keys())
|
||||||
new_children = {
|
new_children = {
|
||||||
@ -126,10 +133,10 @@ class ImportMixin(object):
|
|||||||
|
|
||||||
if not parent:
|
if not parent:
|
||||||
if cls.export_parent:
|
if cls.export_parent:
|
||||||
for p in parent_refs.keys():
|
for prnt in parent_refs.keys():
|
||||||
if p not in dict_rep:
|
if prnt not in dict_rep:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"{0}: Missing field {1}".format(cls.__name__, p)
|
"{0}: Missing field {1}".format(cls.__name__, prnt)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Set foreign keys to parent obj
|
# Set foreign keys to parent obj
|
||||||
@ -182,10 +189,10 @@ class ImportMixin(object):
|
|||||||
|
|
||||||
# Recursively create children
|
# Recursively create children
|
||||||
if recursive:
|
if recursive:
|
||||||
for c in cls.export_children:
|
for child in cls.export_children:
|
||||||
child_class = cls.__mapper__.relationships[c].argument.class_
|
child_class = cls.__mapper__.relationships[child].argument.class_
|
||||||
added = []
|
added = []
|
||||||
for c_obj in new_children.get(c, []):
|
for c_obj in new_children.get(child, []):
|
||||||
added.append(
|
added.append(
|
||||||
child_class.import_from_dict(
|
child_class.import_from_dict(
|
||||||
session=session, dict_rep=c_obj, parent=obj, sync=sync
|
session=session, dict_rep=c_obj, parent=obj, sync=sync
|
||||||
@ -193,8 +200,10 @@ class ImportMixin(object):
|
|||||||
)
|
)
|
||||||
# If children should get synced, delete the ones that did not
|
# If children should get synced, delete the ones that did not
|
||||||
# get updated.
|
# get updated.
|
||||||
if c in sync and not is_new_obj:
|
if child in sync and not is_new_obj:
|
||||||
back_refs = child_class._parent_foreign_key_mappings()
|
back_refs = (
|
||||||
|
child_class._parent_foreign_key_mappings() # pylint: disable=protected-access
|
||||||
|
)
|
||||||
delete_filters = [
|
delete_filters = [
|
||||||
getattr(child_class, k) == getattr(obj, back_refs.get(k))
|
getattr(child_class, k) == getattr(obj, back_refs.get(k))
|
||||||
for k in back_refs.keys()
|
for k in back_refs.keys()
|
||||||
@ -203,7 +212,7 @@ class ImportMixin(object):
|
|||||||
session.query(child_class).filter(and_(*delete_filters))
|
session.query(child_class).filter(and_(*delete_filters))
|
||||||
).difference(set(added))
|
).difference(set(added))
|
||||||
for o in to_delete:
|
for o in to_delete:
|
||||||
logging.info("Deleting %s %s", c, str(obj))
|
logging.info("Deleting %s %s", child, str(obj))
|
||||||
session.delete(o)
|
session.delete(o)
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
@ -234,16 +243,16 @@ class ImportMixin(object):
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if recursive:
|
if recursive:
|
||||||
for c in self.export_children:
|
for cld in self.export_children:
|
||||||
# sorting to make lists of children stable
|
# sorting to make lists of children stable
|
||||||
dict_rep[c] = sorted(
|
dict_rep[cld] = sorted(
|
||||||
[
|
[
|
||||||
child.export_to_dict(
|
child.export_to_dict(
|
||||||
recursive=recursive,
|
recursive=recursive,
|
||||||
include_parent_ref=include_parent_ref,
|
include_parent_ref=include_parent_ref,
|
||||||
include_defaults=include_defaults,
|
include_defaults=include_defaults,
|
||||||
)
|
)
|
||||||
for child in getattr(self, c)
|
for child in getattr(self, cld)
|
||||||
],
|
],
|
||||||
key=lambda k: sorted(str(k.items())),
|
key=lambda k: sorted(str(k.items())),
|
||||||
)
|
)
|
||||||
@ -276,7 +285,7 @@ class ImportMixin(object):
|
|||||||
try:
|
try:
|
||||||
if g.user:
|
if g.user:
|
||||||
self.owners = [g.user]
|
self.owners = [g.user]
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
self.owners = []
|
self.owners = []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -288,6 +297,13 @@ class ImportMixin(object):
|
|||||||
return json_to_dict(self.template_params)
|
return json_to_dict(self.template_params)
|
||||||
|
|
||||||
|
|
||||||
|
def _user_link(user): # pylint: disable=no-self-use
|
||||||
|
if not user:
|
||||||
|
return ""
|
||||||
|
url = "/superset/profile/{}/".format(user.username)
|
||||||
|
return Markup('<a href="{}">{}</a>'.format(url, escape(user) or ""))
|
||||||
|
|
||||||
|
|
||||||
class AuditMixinNullable(AuditMixin):
|
class AuditMixinNullable(AuditMixin):
|
||||||
|
|
||||||
"""Altering the AuditMixin to use nullable fields
|
"""Altering the AuditMixin to use nullable fields
|
||||||
@ -319,12 +335,6 @@ class AuditMixinNullable(AuditMixin):
|
|||||||
nullable=True,
|
nullable=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _user_link(self, user):
|
|
||||||
if not user:
|
|
||||||
return ""
|
|
||||||
url = "/superset/profile/{}/".format(user.username)
|
|
||||||
return Markup('<a href="{}">{}</a>'.format(url, escape(user) or ""))
|
|
||||||
|
|
||||||
def changed_by_name(self):
|
def changed_by_name(self):
|
||||||
if self.created_by:
|
if self.created_by:
|
||||||
return escape("{}".format(self.created_by))
|
return escape("{}".format(self.created_by))
|
||||||
@ -332,11 +342,11 @@ class AuditMixinNullable(AuditMixin):
|
|||||||
|
|
||||||
@renders("created_by")
|
@renders("created_by")
|
||||||
def creator(self):
|
def creator(self):
|
||||||
return self._user_link(self.created_by)
|
return _user_link(self.created_by)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def changed_by_(self):
|
def changed_by_(self):
|
||||||
return self._user_link(self.changed_by)
|
return _user_link(self.changed_by)
|
||||||
|
|
||||||
@renders("changed_on")
|
@renders("changed_on")
|
||||||
def changed_on_(self):
|
def changed_on_(self):
|
||||||
@ -351,14 +361,14 @@ class AuditMixinNullable(AuditMixin):
|
|||||||
return Markup(f'<span class="no-wrap">{self.changed_on_humanized}</span>')
|
return Markup(f'<span class="no-wrap">{self.changed_on_humanized}</span>')
|
||||||
|
|
||||||
|
|
||||||
class QueryResult(object):
|
class QueryResult(object): # pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
"""Object returned by the query interface"""
|
"""Object returned by the query interface"""
|
||||||
|
|
||||||
def __init__(
|
def __init__( # pylint: disable=too-many-arguments
|
||||||
self, df, query, duration, status=QueryStatus.SUCCESS, error_message=None
|
self, df, query, duration, status=QueryStatus.SUCCESS, error_message=None
|
||||||
):
|
):
|
||||||
self.df = df
|
self.df = df # pylint: disable=invalid-name
|
||||||
self.query = query
|
self.query = query
|
||||||
self.duration = duration
|
self.duration = duration
|
||||||
self.status = status
|
self.status = status
|
||||||
@ -374,7 +384,7 @@ class ExtraJSONMixin:
|
|||||||
def extra(self):
|
def extra(self):
|
||||||
try:
|
try:
|
||||||
return json.loads(self.extra_json)
|
return json.loads(self.extra_json)
|
||||||
except Exception:
|
except Exception: # pylint: disable=broad-except
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def set_extra_json(self, d):
|
def set_extra_json(self, d):
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
# KIND, either express or implied. See the License for the
|
# KIND, either express or implied. See the License for the
|
||||||
# specific language governing permissions and limitations
|
# specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
# pylint: disable=C,R,W
|
|
||||||
"""Models for scheduled execution of jobs"""
|
"""Models for scheduled execution of jobs"""
|
||||||
|
|
||||||
import enum
|
import enum
|
||||||
@ -51,7 +50,7 @@ class EmailSchedule:
|
|||||||
|
|
||||||
__tablename__ = "email_schedules"
|
__tablename__ = "email_schedules"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
active = Column(Boolean, default=True, index=True)
|
active = Column(Boolean, default=True, index=True)
|
||||||
crontab = Column(String(50))
|
crontab = Column(String(50))
|
||||||
|
|
||||||
@ -92,3 +91,4 @@ def get_scheduler_model(report_type):
|
|||||||
return DashboardEmailSchedule
|
return DashboardEmailSchedule
|
||||||
elif report_type == ScheduleType.slice.value:
|
elif report_type == ScheduleType.slice.value:
|
||||||
return SliceEmailSchedule
|
return SliceEmailSchedule
|
||||||
|
return None
|
||||||
|
@ -14,11 +14,11 @@
|
|||||||
# KIND, either express or implied. See the License for the
|
# KIND, either express or implied. See the License for the
|
||||||
# specific language governing permissions and limitations
|
# specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
# pylint: disable=C,R,W
|
|
||||||
"""A collection of ORM sqlalchemy models for SQL Lab"""
|
"""A collection of ORM sqlalchemy models for SQL Lab"""
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
# pylint: disable=ungrouped-imports
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
import sqlalchemy as sqla
|
import sqlalchemy as sqla
|
||||||
from flask import Markup
|
from flask import Markup
|
||||||
@ -48,7 +48,7 @@ class Query(Model, ExtraJSONMixin):
|
|||||||
table may represent multiple SQL statements executed sequentially"""
|
table may represent multiple SQL statements executed sequentially"""
|
||||||
|
|
||||||
__tablename__ = "query"
|
__tablename__ = "query"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
client_id = Column(String(11), unique=True, nullable=False)
|
client_id = Column(String(11), unique=True, nullable=False)
|
||||||
|
|
||||||
database_id = Column(Integer, ForeignKey("dbs.id"), nullable=False)
|
database_id = Column(Integer, ForeignKey("dbs.id"), nullable=False)
|
||||||
@ -149,7 +149,7 @@ class SavedQuery(Model, AuditMixinNullable, ExtraJSONMixin):
|
|||||||
"""ORM model for SQL query"""
|
"""ORM model for SQL query"""
|
||||||
|
|
||||||
__tablename__ = "saved_query"
|
__tablename__ = "saved_query"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||||
user_id = Column(Integer, ForeignKey("ab_user.id"), nullable=True)
|
user_id = Column(Integer, ForeignKey("ab_user.id"), nullable=True)
|
||||||
db_id = Column(Integer, ForeignKey("dbs.id"), nullable=True)
|
db_id = Column(Integer, ForeignKey("dbs.id"), nullable=True)
|
||||||
schema = Column(String(128))
|
schema = Column(String(128))
|
||||||
@ -194,7 +194,9 @@ class TabState(Model, AuditMixinNullable, ExtraJSONMixin):
|
|||||||
__tablename__ = "tab_state"
|
__tablename__ = "tab_state"
|
||||||
|
|
||||||
# basic info
|
# basic info
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column( # pylint: disable=invalid-name
|
||||||
|
Integer, primary_key=True, autoincrement=True
|
||||||
|
)
|
||||||
user_id = Column(Integer, ForeignKey("ab_user.id"))
|
user_id = Column(Integer, ForeignKey("ab_user.id"))
|
||||||
label = Column(String(256))
|
label = Column(String(256))
|
||||||
active = Column(Boolean, default=False)
|
active = Column(Boolean, default=False)
|
||||||
@ -245,7 +247,9 @@ class TableSchema(Model, AuditMixinNullable, ExtraJSONMixin):
|
|||||||
|
|
||||||
__tablename__ = "table_schema"
|
__tablename__ = "table_schema"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column( # pylint: disable=invalid-name
|
||||||
|
Integer, primary_key=True, autoincrement=True
|
||||||
|
)
|
||||||
tab_state_id = Column(Integer, ForeignKey("tab_state.id", ondelete="CASCADE"))
|
tab_state_id = Column(Integer, ForeignKey("tab_state.id", ondelete="CASCADE"))
|
||||||
|
|
||||||
database_id = Column(Integer, ForeignKey("dbs.id"), nullable=False)
|
database_id = Column(Integer, ForeignKey("dbs.id"), nullable=False)
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
# KIND, either express or implied. See the License for the
|
# KIND, either express or implied. See the License for the
|
||||||
# specific language governing permissions and limitations
|
# specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
# pylint: disable=C,R,W,no-init
|
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
|
|
||||||
import enum
|
import enum
|
||||||
|
Loading…
Reference in New Issue
Block a user