(ns metabase.pulse.update-alerts ;; TODO this should be moved to notification (:require [metabase.events :as events] [metabase.models.notification :as models.notification] [toucan2.core :as t2])) | |
(defn- card-archived? [old-card new-card] (and (not (:archived old-card)) (:archived new-card))) | |
(defn- line-area-bar? [display] (contains? #{:line :area :bar} display)) | |
(defn- progress? [display] (= :progress display)) | |
(defn- allows-rows-alert? [display] (not (contains? #{:line :bar :area :progress} display))) | |
Alerts no longer make sense when the kind of question being alerted on significantly changes. Setting up an alert when a time series query reaches 10 is no longer valid if the question switches from a line graph to a table. This function goes through various scenarios that render an alert no longer valid | (defn- display-change-broke-alert? [{old-display :display} {new-display :display}] (when-not (= old-display new-display) (or ;; Did the alert switch from a table type to a line/bar/area/progress graph type? (and (allows-rows-alert? old-display) (or (line-area-bar? new-display) (progress? new-display))) ;; Switching from a line/bar/area to another type that is not those three invalidates the alert (and (line-area-bar? old-display) (not (line-area-bar? new-display))) ;; Switching from a progress graph to anything else invalidates the alert (and (progress? old-display) (not (progress? new-display)))))) |
If we had a goal before, and now it's gone, the alert is no longer valid | (defn- goal-missing? [old-card new-card] (and (get-in old-card [:visualization_settings :graph.goal_value]) (not (get-in new-card [:visualization_settings :graph.goal_value])))) |
If there are multiple breakouts and a goal, we don't know which breakout to compare to the goal, so it invalidates the alert | (defn- multiple-breakouts? [{:keys [display] :as new-card}] (and (get-in new-card [:visualization_settings :graph.goal_value]) (or (line-area-bar? display) (progress? display)) (< 1 (count (get-in new-card [:dataset_query :query :breakout]))))) |
Removes all of the alerts and notifies all of the email recipients of the alerts change. | (defn delete-alert-and-notify! [topic actor card] (when-let [card-notifications (seq (models.notification/notifications-for-card (:id card)))] (t2/delete! :model/Notification :id [:in (map :id card-notifications)]) (events/publish-event! topic {:card card :actor actor :notifications card-notifications}))) |
Delete alerts if the card has been changed in a way that invalidates the alert TODO -- consider whether this should be triggered indirectly by an event e.g. | (defn delete-alerts-if-needed! [& {:keys [old-card new-card actor]}] (cond (card-archived? old-card new-card) (delete-alert-and-notify! :event/card-update.notification-deleted.card-archived actor new-card) (or (display-change-broke-alert? old-card new-card) (goal-missing? old-card new-card) (multiple-breakouts? new-card)) (delete-alert-and-notify! :event/card-update.notification-deleted.card-changed actor new-card) ;; The change doesn't invalidate the alert, do nothing :else nil)) |