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

dpgaspar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new bd79bd2  feat: new report schedule models (#11550)
bd79bd2 is described below

commit bd79bd2a54e5b8dc1895e0e56c130ba57f0bb4c8
Author: Daniel Vaz Gaspar <[email protected]>
AuthorDate: Fri Nov 6 09:33:32 2020 +0000

    feat: new report schedule models (#11550)
    
    * feat: new report schedule models
    
    * lint and unique constraint
    
    * support sqlite
    
    * fix sqlite
    
    * add audit mixin and minor fixes
    
    * fix FK's
    
    * address comments
    
    * lint
---
 .../versions/49b5a32daba5_add_report_schedules.py  | 133 ++++++++++++++++
 superset/models/reports.py                         | 177 +++++++++++++++++++++
 2 files changed, 310 insertions(+)

diff --git a/superset/migrations/versions/49b5a32daba5_add_report_schedules.py 
b/superset/migrations/versions/49b5a32daba5_add_report_schedules.py
new file mode 100644
index 0000000..f207454
--- /dev/null
+++ b/superset/migrations/versions/49b5a32daba5_add_report_schedules.py
@@ -0,0 +1,133 @@
+# 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.
+"""add report schedules
+
+Revision ID: 49b5a32daba5
+Revises: 96e99fb176a0
+Create Date: 2020-11-04 11:06:59.249758
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = "49b5a32daba5"
+down_revision = "96e99fb176a0"
+
+import sqlalchemy as sa
+from alembic import op
+from sqlalchemy.exc import OperationalError
+
+
+def upgrade():
+    op.create_table(
+        "report_schedule",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("type", sa.String(length=50), nullable=False),
+        sa.Column("name", sa.String(length=150), nullable=False, unique=True),
+        sa.Column("description", sa.Text(), nullable=True),
+        sa.Column("context_markdown", sa.Text(), nullable=True),
+        sa.Column("active", sa.Boolean(), default=True, nullable=True),
+        sa.Column("crontab", sa.String(length=50), nullable=False),
+        sa.Column("sql", sa.Text(), nullable=True),
+        sa.Column("chart_id", sa.Integer(), nullable=True),
+        sa.Column("dashboard_id", sa.Integer(), nullable=True),
+        sa.Column("database_id", sa.Integer(), nullable=True),
+        sa.Column("last_eval_dttm", sa.DateTime(), nullable=True),
+        sa.Column("last_state", sa.String(length=50), nullable=True),
+        sa.Column("last_value", sa.Float(), nullable=True),
+        sa.Column("last_value_row_json", sa.Text(), nullable=True),
+        sa.Column("validator_type", sa.String(length=100), nullable=True),
+        sa.Column("validator_config_json", sa.Text(), default="{}", 
nullable=True),
+        sa.Column("log_retention", sa.Integer(), nullable=True, default=90),
+        sa.Column("grace_period", sa.Integer(), nullable=True, default=60 * 60 
* 4),
+        # Audit Mixin
+        sa.Column("created_on", sa.DateTime(), nullable=True),
+        sa.Column("changed_on", sa.DateTime(), nullable=True),
+        sa.Column("created_by_fk", sa.Integer(), nullable=True),
+        sa.Column("changed_by_fk", sa.Integer(), nullable=True),
+        sa.ForeignKeyConstraint(["chart_id"], ["slices.id"]),
+        sa.ForeignKeyConstraint(["dashboard_id"], ["dashboards.id"]),
+        sa.ForeignKeyConstraint(["database_id"], ["dbs.id"]),
+        sa.ForeignKeyConstraint(["changed_by_fk"], ["ab_user.id"]),
+        sa.ForeignKeyConstraint(["created_by_fk"], ["ab_user.id"]),
+        sa.PrimaryKeyConstraint("id"),
+    )
+    try:
+        op.create_unique_constraint(
+            "uq_report_schedule_name", "report_schedule", ["name"]
+        )
+    except Exception:
+        # Expected to fail on SQLite
+        pass
+    op.create_index(
+        op.f("ix_report_schedule_active"), "report_schedule", ["active"], 
unique=False
+    )
+
+    op.create_table(
+        "report_execution_log",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("scheduled_dttm", sa.DateTime(), nullable=False),
+        sa.Column("start_dttm", sa.DateTime(), nullable=True),
+        sa.Column("end_dttm", sa.DateTime(), nullable=True),
+        sa.Column("value", sa.Float(), nullable=True),
+        sa.Column("value_row_json", sa.Text(), nullable=True),
+        sa.Column("state", sa.String(length=50), nullable=False),
+        sa.Column("error_message", sa.Text(), nullable=True),
+        sa.Column("report_schedule_id", sa.Integer(), nullable=False),
+        sa.ForeignKeyConstraint(["report_schedule_id"], 
["report_schedule.id"]),
+        sa.PrimaryKeyConstraint("id"),
+    )
+
+    op.create_table(
+        "report_recipient",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("type", sa.String(length=50), nullable=False),
+        sa.Column("recipient_config_json", sa.Text(), default="{}", 
nullable=True),
+        sa.Column("report_schedule_id", sa.Integer(), nullable=False),
+        # Audit Mixin
+        sa.Column("created_on", sa.DateTime(), nullable=True),
+        sa.Column("changed_on", sa.DateTime(), nullable=True),
+        sa.Column("created_by_fk", sa.Integer(), nullable=True),
+        sa.Column("changed_by_fk", sa.Integer(), nullable=True),
+        sa.ForeignKeyConstraint(["report_schedule_id"], 
["report_schedule.id"]),
+        sa.ForeignKeyConstraint(["changed_by_fk"], ["ab_user.id"]),
+        sa.ForeignKeyConstraint(["created_by_fk"], ["ab_user.id"]),
+        sa.PrimaryKeyConstraint("id"),
+    )
+
+    op.create_table(
+        "report_schedule_user",
+        sa.Column("id", sa.Integer(), nullable=False),
+        sa.Column("user_id", sa.Integer(), nullable=False),
+        sa.Column("report_schedule_id", sa.Integer(), nullable=False),
+        sa.ForeignKeyConstraint(["report_schedule_id"], 
["report_schedule.id"],),
+        sa.ForeignKeyConstraint(["user_id"], ["ab_user.id"],),
+        sa.PrimaryKeyConstraint("id"),
+    )
+
+
+def downgrade():
+    op.drop_index(op.f("ix_report_schedule_active"), 
table_name="report_schedule")
+    try:
+        op.drop_constraint("uq_report_schedule_name", "report_schedule", 
type_="unique")
+    except Exception:
+        # Expected to fail on SQLite
+        pass
+
+    op.drop_table("report_execution_log")
+    op.drop_table("report_recipient")
+    op.drop_table("report_schedule_user")
+    op.drop_table("report_schedule")
diff --git a/superset/models/reports.py b/superset/models/reports.py
new file mode 100644
index 0000000..c510cc6
--- /dev/null
+++ b/superset/models/reports.py
@@ -0,0 +1,177 @@
+# 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.
+# pylint: disable=line-too-long,unused-argument,ungrouped-imports
+"""A collection of ORM sqlalchemy models for Superset"""
+import enum
+
+from flask_appbuilder import Model
+from sqlalchemy import (
+    Boolean,
+    Column,
+    DateTime,
+    Float,
+    ForeignKey,
+    Integer,
+    String,
+    Table,
+    Text,
+)
+from sqlalchemy.orm import relationship
+from sqlalchemy.schema import UniqueConstraint
+
+from superset.extensions import security_manager
+from superset.models.core import Database
+from superset.models.dashboard import Dashboard
+from superset.models.helpers import AuditMixinNullable
+from superset.models.slice import Slice
+
+metadata = Model.metadata  # pylint: disable=no-member
+
+
+class ReportScheduleType(str, enum.Enum):
+    ALERT = "Alert"
+    REPORT = "Report"
+
+
+class ReportScheduleValidatorType(str, enum.Enum):
+    """ Validator types for alerts """
+
+    not_null = "not null"
+    operator = "operator"
+
+
+class ReportRecipientType(str, enum.Enum):
+    EMAIL = "Email"
+    SLACK = "Slack"
+
+
+class ReportLogState(str, enum.Enum):
+    SUCCESS = "Success"
+    ERROR = "Error"
+
+
+class ReportEmailFormat(str, enum.Enum):
+    VISUALIZATION = "Visualization"
+    DATA = "Raw data"
+
+
+report_schedule_user = Table(
+    "report_schedule_user",
+    metadata,
+    Column("id", Integer, primary_key=True),
+    Column("user_id", Integer, ForeignKey("ab_user.id"), nullable=False),
+    Column(
+        "report_schedule_id", Integer, ForeignKey("report_schedule.id"), 
nullable=False
+    ),
+    UniqueConstraint("user_id", "report_schedule_id"),
+)
+
+
+class ReportSchedule(Model, AuditMixinNullable):
+
+    """
+    Report Schedules, supports alerts and reports
+    """
+
+    __tablename__ = "report_schedule"
+    id = Column(Integer, primary_key=True)
+    type = Column(String(50), nullable=False)
+    name = Column(String(150), nullable=False, unique=True)
+    description = Column(Text)
+    context_markdown = Column(Text)
+    active = Column(Boolean, default=True, index=True)
+    crontab = Column(String(50), nullable=False)
+    sql = Column(Text())
+    # (Alerts/Reports) M-O to chart
+    chart_id = Column(Integer, ForeignKey("slices.id"), nullable=True)
+    chart = relationship(Slice, backref="report_schedules", 
foreign_keys=[chart_id])
+    # (Alerts/Reports) M-O to dashboard
+    dashboard_id = Column(Integer, ForeignKey("dashboards.id"), nullable=True)
+    dashboard = relationship(
+        Dashboard, backref="report_schedules", foreign_keys=[dashboard_id]
+    )
+    # (Alerts) M-O to database
+    database_id = Column(Integer, ForeignKey("dbs.id"), nullable=True)
+    database = relationship(Database, foreign_keys=[database_id])
+    owners = relationship(security_manager.user_model, 
secondary=report_schedule_user)
+
+    # (Alerts) Stamped last observations
+    last_eval_dttm = Column(DateTime)
+    last_state = Column(String(50))
+    last_value = Column(Float)
+    last_value_row_json = Column(Text)
+
+    # (Alerts) Observed value validation related columns
+    validator_type = Column(String(100))
+    validator_config_json = Column(Text, default="{}")
+
+    # Log retention
+    log_retention = Column(Integer, default=90)
+    grace_period = Column(Integer, default=60 * 60 * 4)
+
+    def __repr__(self) -> str:
+        return str(self.name)
+
+
+class ReportRecipients(
+    Model, AuditMixinNullable
+):  # pylint: disable=too-few-public-methods
+
+    """
+    Report Recipients, meant to support multiple notification types, eg: 
Slack, email
+    """
+
+    __tablename__ = "report_recipient"
+    id = Column(Integer, primary_key=True)
+    type = Column(String(50), nullable=False)
+    recipient_config_json = Column(Text, default="{}")
+    report_schedule_id = Column(
+        Integer, ForeignKey("report_schedule.id"), nullable=False
+    )
+    report_schedule = relationship(
+        ReportSchedule, backref="recipients", foreign_keys=[report_schedule_id]
+    )
+
+
+class ReportExecutionLog(Model):  # pylint: disable=too-few-public-methods
+
+    """
+    Report Execution Log, hold the result of the report execution with 
timestamps,
+    last observation and possible error messages
+    """
+
+    __tablename__ = "report_execution_log"
+    id = Column(Integer, primary_key=True)
+
+    # Timestamps
+    scheduled_dttm = Column(DateTime, nullable=False)
+    start_dttm = Column(DateTime)
+    end_dttm = Column(DateTime)
+
+    # (Alerts) Observed values
+    value = Column(Float)
+    value_row_json = Column(Text)
+
+    state = Column(String(50), nullable=False)
+    error_message = Column(Text)
+
+    report_schedule_id = Column(
+        Integer, ForeignKey("report_schedule.id"), nullable=False
+    )
+    report_schedule = relationship(
+        ReportSchedule, backref="logs", foreign_keys=[report_schedule_id]
+    )

Reply via email to