willbarrett commented on a change in pull request #11890:
URL: 
https://github.com/apache/incubator-superset/pull/11890#discussion_r538829281



##########
File path: superset/reports/commands/execute.py
##########
@@ -192,45 +200,173 @@ def _send(self, report_schedule: ReportSchedule) -> None:
         if notification_errors:
             raise 
ReportScheduleNotificationError(";".join(notification_errors))
 
+    def is_in_grace_period(self) -> bool:
+        """
+        Checks if an alert is on it's grace period
+        """
+        last_success = ReportScheduleDAO.find_last_success_log(
+            self._report_schedule, session=self._session
+        )
+        return (
+            last_success is not None
+            and self._report_schedule.grace_period
+            and datetime.utcnow()
+            - timedelta(seconds=self._report_schedule.grace_period)
+            < last_success.end_dttm
+        )
+
+    def is_working_timeout(self) -> bool:
+        """
+        Checks if an alert is on a working timeout
+        """
+        return (
+            self._report_schedule.working_timeout is not None
+            and self._report_schedule.last_eval_dttm is not None
+            and datetime.utcnow()
+            - timedelta(seconds=self._report_schedule.working_timeout)
+            > self._report_schedule.last_eval_dttm
+        )
+
+    def next(self) -> None:
+        raise NotImplementedError()
+
+
+class ReportNotTriggeredErrorState(BaseReportState):
+    """
+    Handle Not triggered and Error state
+    next final states:
+    - Not Triggered
+    - Success
+    - Error
+    """
+
+    current_states = [ReportState.NOOP, ReportState.ERROR]
+    initial = True
+
+    def next(self) -> None:
+        self.set_state_and_log(ReportState.WORKING)
+        try:
+            # If it's an alert check if the alert is triggered
+            if self._report_schedule.type == ReportScheduleType.ALERT:
+                if not AlertCommand(self._report_schedule).run():
+                    self.set_state_and_log(ReportState.NOOP)
+                    return
+            self._send()
+            self.set_state_and_log(ReportState.SUCCESS)
+        except CommandException as ex:
+            self.set_state_and_log(ReportState.ERROR, error_message=str(ex))
+            raise ex
+
+
+class ReportWorkingState(BaseReportState):
+    """
+    Handle Working state
+    next states:
+    - Error
+    - Working
+    """
+
+    current_states = [ReportState.WORKING]
+
+    def next(self) -> None:
+        if self.is_working_timeout():
+            exception_timeout = ReportScheduleWorkingTimeoutError()
+            self.set_state_and_log(
+                ReportState.ERROR, error_message=str(exception_timeout),
+            )
+            raise exception_timeout
+        exception_working = ReportSchedulePreviousWorkingError()
+        self.set_state_and_log(
+            ReportState.WORKING, error_message=str(exception_working),
+        )
+        raise exception_working
+
+
+class ReportSuccessState(BaseReportState):
+    """
+    Handle Success, Grace state
+    next states:
+    - Grace
+    - Not triggered
+    - Success
+    """
+
+    current_states = [ReportState.SUCCESS, ReportState.GRACE]
+
+    def next(self) -> None:
+        self.set_state_and_log(ReportState.WORKING)
+        if self._report_schedule.type == ReportScheduleType.ALERT:
+            if self.is_in_grace_period():
+                self.set_state_and_log(
+                    ReportState.GRACE,
+                    error_message=str(ReportScheduleAlertGracePeriodError()),
+                )
+                return
+            self.set_state_and_log(
+                ReportState.NOOP,
+                error_message=str(ReportScheduleAlertEndGracePeriodError()),
+            )
+            return
+        try:
+            self._send()
+            self.set_state_and_log(ReportState.SUCCESS)
+        except CommandException as ex:
+            self.set_state_and_log(ReportState.ERROR, error_message=str(ex))
+
+
+class ReportScheduleStateMachine:  # pylint: disable=too-few-public-methods
+    """
+    Simple state machine for Alerts/Reports states
+    """
+
+    states_cls = [ReportWorkingState, ReportNotTriggeredErrorState, 
ReportSuccessState]
+
+    def __init__(
+        self,
+        session: Session,
+        report_schedule: ReportSchedule,
+        scheduled_dttm: datetime,
+    ):
+        self._session = session
+        self._report_schedule = report_schedule
+        self._scheduled_dttm = scheduled_dttm
+
+    def run(self) -> None:

Review comment:
       It looks like this will silently swallow not-found states?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to