mirror of
https://github.com/getredash/redash.git
synced 2025-12-19 17:37:19 -05:00
Fix alert evaluation logic and issue with calculating min and max of columns without numbers (#7103)
This commit is contained in:
@@ -67,7 +67,9 @@ export default function Criteria({ columnNames, resultValues, alertOptions, onCh
|
||||
<small className="alert-criteria-hint">
|
||||
Max column value is{" "}
|
||||
<code className="p-0">
|
||||
{toString(Math.max(...resultValues.map((o) => o[alertOptions.column]))) || "unknown"}
|
||||
{toString(
|
||||
Math.max(...resultValues.map((o) => Number(o[alertOptions.column])).filter((value) => !isNaN(value)))
|
||||
) || "unknown"}
|
||||
</code>
|
||||
</small>
|
||||
);
|
||||
@@ -76,7 +78,9 @@ export default function Criteria({ columnNames, resultValues, alertOptions, onCh
|
||||
<small className="alert-criteria-hint">
|
||||
Min column value is{" "}
|
||||
<code className="p-0">
|
||||
{toString(Math.min(...resultValues.map((o) => o[alertOptions.column]))) || "unknown"}
|
||||
{toString(
|
||||
Math.min(...resultValues.map((o) => Number(o[alertOptions.column])).filter((value) => !isNaN(value)))
|
||||
) || "unknown"}
|
||||
</code>
|
||||
</small>
|
||||
);
|
||||
|
||||
@@ -916,6 +916,8 @@ def next_state(op, value, threshold):
|
||||
|
||||
if op(value, threshold):
|
||||
new_state = Alert.TRIGGERED_STATE
|
||||
elif not value_is_number and op not in [OPERATORS.get("!="), OPERATORS.get("=="), OPERATORS.get("equals")]:
|
||||
new_state = Alert.UNKNOWN_STATE
|
||||
else:
|
||||
new_state = Alert.OK_STATE
|
||||
|
||||
@@ -967,18 +969,22 @@ class Alert(TimestampMixin, BelongsToOrgMixin, db.Model):
|
||||
else:
|
||||
selector = self.options["selector"]
|
||||
|
||||
if selector == "max":
|
||||
max_val = float("-inf")
|
||||
for i in range(0, len(data["rows"])):
|
||||
max_val = max(max_val, data["rows"][i][self.options["column"]])
|
||||
value = max_val
|
||||
elif selector == "min":
|
||||
min_val = float("inf")
|
||||
for i in range(0, len(data["rows"])):
|
||||
min_val = min(min_val, data["rows"][i][self.options["column"]])
|
||||
value = min_val
|
||||
else:
|
||||
value = data["rows"][0][self.options["column"]]
|
||||
try:
|
||||
if selector == "max":
|
||||
max_val = float("-inf")
|
||||
for i in range(len(data["rows"])):
|
||||
max_val = max(max_val, float(data["rows"][i][self.options["column"]]))
|
||||
value = max_val
|
||||
elif selector == "min":
|
||||
min_val = float("inf")
|
||||
for i in range(len(data["rows"])):
|
||||
min_val = min(min_val, float(data["rows"][i][self.options["column"]]))
|
||||
value = min_val
|
||||
else:
|
||||
value = data["rows"][0][self.options["column"]]
|
||||
|
||||
except ValueError:
|
||||
return self.UNKNOWN_STATE
|
||||
|
||||
threshold = self.options["value"]
|
||||
|
||||
@@ -1007,7 +1013,6 @@ class Alert(TimestampMixin, BelongsToOrgMixin, db.Model):
|
||||
result_table = [] # A two-dimensional array which can rendered as a table in Mustache
|
||||
for row in data["rows"]:
|
||||
result_table.append([row[col["name"]] for col in data["columns"]])
|
||||
print("OPTIONS", self.options)
|
||||
context = {
|
||||
"ALERT_NAME": self.name,
|
||||
"ALERT_URL": "{host}/alerts/{alert_id}".format(host=host, alert_id=self.id),
|
||||
|
||||
@@ -71,23 +71,45 @@ class TestAlertEvaluate(BaseTestCase):
|
||||
alert = self.create_alert(results)
|
||||
self.assertEqual(alert.evaluate(), Alert.UNKNOWN_STATE)
|
||||
|
||||
def test_evaluates_correctly_with_max_selector(self):
|
||||
results = {"rows": [{"foo": 1}, {"foo": 2}], "columns": [{"name": "foo", "type": "STRING"}]}
|
||||
alert = self.create_alert(results)
|
||||
alert.options["selector"] = "max"
|
||||
self.assertEqual(alert.evaluate(), Alert.OK_STATE)
|
||||
|
||||
def test_evaluates_correctly_with_min_selector(self):
|
||||
results = {"rows": [{"foo": 2}, {"foo": 1}], "columns": [{"name": "foo", "type": "STRING"}]}
|
||||
alert = self.create_alert(results)
|
||||
alert.options["selector"] = "min"
|
||||
self.assertEqual(alert.evaluate(), Alert.TRIGGERED_STATE)
|
||||
|
||||
def test_evaluates_correctly_with_first_selector(self):
|
||||
results = {"rows": [{"foo": 1}, {"foo": 2}], "columns": [{"name": "foo", "type": "STRING"}]}
|
||||
results = {"rows": [{"foo": 1}, {"foo": 2}], "columns": [{"name": "foo", "type": "INTEGER"}]}
|
||||
alert = self.create_alert(results)
|
||||
alert.options["selector"] = "first"
|
||||
self.assertEqual(alert.evaluate(), Alert.TRIGGERED_STATE)
|
||||
results = {
|
||||
"rows": [{"foo": "test"}, {"foo": "test"}, {"foo": "test"}],
|
||||
"columns": [{"name": "foo", "type": "STRING"}],
|
||||
}
|
||||
alert = self.create_alert(results)
|
||||
alert.options["selector"] = "first"
|
||||
alert.options["op"] = "<"
|
||||
self.assertEqual(alert.evaluate(), Alert.UNKNOWN_STATE)
|
||||
|
||||
def test_evaluates_correctly_with_min_selector(self):
|
||||
results = {"rows": [{"foo": 2}, {"foo": 1}], "columns": [{"name": "foo", "type": "INTEGER"}]}
|
||||
alert = self.create_alert(results)
|
||||
alert.options["selector"] = "min"
|
||||
self.assertEqual(alert.evaluate(), Alert.TRIGGERED_STATE)
|
||||
results = {
|
||||
"rows": [{"foo": "test"}, {"foo": "test"}, {"foo": "test"}],
|
||||
"columns": [{"name": "foo", "type": "STRING"}],
|
||||
}
|
||||
alert = self.create_alert(results)
|
||||
alert.options["selector"] = "min"
|
||||
self.assertEqual(alert.evaluate(), Alert.UNKNOWN_STATE)
|
||||
|
||||
def test_evaluates_correctly_with_max_selector(self):
|
||||
results = {"rows": [{"foo": 1}, {"foo": 2}], "columns": [{"name": "foo", "type": "INTEGER"}]}
|
||||
alert = self.create_alert(results)
|
||||
alert.options["selector"] = "max"
|
||||
self.assertEqual(alert.evaluate(), Alert.OK_STATE)
|
||||
results = {
|
||||
"rows": [{"foo": "test"}, {"foo": "test"}, {"foo": "test"}],
|
||||
"columns": [{"name": "foo", "type": "STRING"}],
|
||||
}
|
||||
alert = self.create_alert(results)
|
||||
alert.options["selector"] = "max"
|
||||
self.assertEqual(alert.evaluate(), Alert.UNKNOWN_STATE)
|
||||
|
||||
|
||||
class TestNextState(TestCase):
|
||||
|
||||
Reference in New Issue
Block a user