mirror of
https://github.com/apache/superset.git
synced 2026-04-15 22:25:15 +00:00
* chore: migrate all harmless incubator refs like comments and documentation files * chore: migrate all superset.incubator.apache.org to superset.apache.org to * chore: migrate dist.apache.org refs * chore: removing incubating and Disclaimer * fix: linting issues
97 lines
3.0 KiB
Python
97 lines
3.0 KiB
Python
# 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.
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
import pandas as pd
|
|
from sqlalchemy.orm import Session
|
|
|
|
from superset import jinja_context
|
|
from superset.models.alerts import Alert, SQLObservation
|
|
|
|
logger = logging.getLogger("tasks.email_reports")
|
|
|
|
|
|
# Session needs to be passed along in the celery workers and db.session cannot be used.
|
|
# For more info see: https://github.com/apache/superset/issues/10530
|
|
def observe(alert_id: int, session: Session) -> Optional[str]:
|
|
"""Collect observations for the alert.
|
|
Returns an error message if the observer value was not valid
|
|
"""
|
|
|
|
alert = session.query(Alert).filter_by(id=alert_id).one()
|
|
|
|
value = None
|
|
|
|
tp = jinja_context.get_template_processor(database=alert.database)
|
|
rendered_sql = tp.process_template(alert.sql)
|
|
df = alert.database.get_df(rendered_sql)
|
|
|
|
error_msg = validate_observer_result(df, alert.id, alert.label)
|
|
|
|
if not error_msg and not df.empty and df.to_records()[0][1] is not None:
|
|
value = float(df.to_records()[0][1])
|
|
|
|
observation = SQLObservation(
|
|
alert_id=alert_id, dttm=datetime.utcnow(), value=value, error_msg=error_msg,
|
|
)
|
|
|
|
session.add(observation)
|
|
session.commit()
|
|
|
|
return error_msg
|
|
|
|
|
|
def validate_observer_result(
|
|
sql_result: pd.DataFrame, alert_id: int, alert_label: str
|
|
) -> Optional[str]:
|
|
"""
|
|
Verifies if a DataFrame SQL query result to see if
|
|
it contains a valid value for a SQLObservation.
|
|
Returns an error message if the result is invalid.
|
|
"""
|
|
try:
|
|
if sql_result.empty:
|
|
# empty results are used for the not null validator
|
|
return None
|
|
|
|
rows = sql_result.to_records()
|
|
|
|
assert (
|
|
len(rows) == 1
|
|
), f"Observer for alert <{alert_id}:{alert_label}> returned more than 1 row"
|
|
|
|
assert (
|
|
len(rows[0]) == 2
|
|
), f"Observer for alert <{alert_id}:{alert_label}> returned more than 1 column"
|
|
|
|
if rows[0][1] is None:
|
|
return None
|
|
|
|
float(rows[0][1])
|
|
|
|
except AssertionError as error:
|
|
return str(error)
|
|
except (TypeError, ValueError):
|
|
return (
|
|
f"Observer for alert <{alert_id}:{alert_label}> returned a non-number value"
|
|
)
|
|
|
|
return None
|