2024-11-30 11:27:32 +00:00
|
|
|
import datetime
|
|
|
|
import operator
|
2024-12-07 23:18:25 +00:00
|
|
|
from typing import Tuple, List, Callable
|
2024-11-30 11:27:32 +00:00
|
|
|
|
|
|
|
from token_bot.persistant_database import Alert, AlertType
|
2024-12-03 12:12:48 +00:00
|
|
|
from token_bot.token_database.flavor import Flavor
|
2024-11-30 11:27:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
class UpdateTrigger:
|
|
|
|
def __init__(self, alert: Alert):
|
2024-12-09 01:07:26 +00:00
|
|
|
self._alert: Alert = alert
|
|
|
|
self._last_trigger: Tuple[datetime.datetime, int] | None = None
|
2024-12-03 12:27:35 +00:00
|
|
|
self._last_alerting: Tuple[datetime.datetime, int] | None = None
|
2024-12-09 01:07:26 +00:00
|
|
|
self._squelched: bool = False
|
2024-11-30 11:27:32 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def alert(self) -> Alert:
|
|
|
|
return self._alert
|
|
|
|
|
2024-12-03 12:12:48 +00:00
|
|
|
@property
|
|
|
|
def last_trigger(self) -> Tuple[datetime.datetime, int] | None:
|
|
|
|
return self._last_trigger
|
|
|
|
|
2024-12-05 04:31:54 +00:00
|
|
|
@property
|
2024-12-03 12:27:35 +00:00
|
|
|
def last_alerting(self) -> Tuple[datetime.datetime, int] | None:
|
|
|
|
return self._last_alerting
|
|
|
|
|
2024-12-05 04:31:54 +00:00
|
|
|
@property
|
|
|
|
def squelched(self):
|
|
|
|
return self._squelched
|
|
|
|
|
2024-12-09 01:07:26 +00:00
|
|
|
def _find_next_trigger(
|
|
|
|
self,
|
|
|
|
comparison_operator: Callable,
|
|
|
|
starting_point: datetime.datetime,
|
|
|
|
history: List[Tuple[datetime.datetime, int]],
|
|
|
|
):
|
|
|
|
candidate_datum: Tuple[datetime.datetime, int] | None = None
|
2024-11-30 11:27:32 +00:00
|
|
|
for datum in history:
|
2024-12-07 23:18:25 +00:00
|
|
|
if datum[0] > starting_point and datum != history[-1]:
|
2024-12-09 01:07:26 +00:00
|
|
|
if candidate_datum is None or comparison_operator(
|
|
|
|
datum[1], candidate_datum[1]
|
|
|
|
):
|
2024-12-07 23:18:25 +00:00
|
|
|
candidate_datum = datum
|
2024-11-30 11:27:32 +00:00
|
|
|
self._last_trigger = candidate_datum
|
|
|
|
|
2024-12-09 01:07:26 +00:00
|
|
|
def check_and_update(
|
|
|
|
self,
|
|
|
|
new_datum: Tuple[datetime.datetime, int],
|
|
|
|
history: List[Tuple[datetime.datetime, int]],
|
|
|
|
) -> bool:
|
2024-12-03 12:12:48 +00:00
|
|
|
match self.alert.flavor:
|
|
|
|
case Flavor.RETAIL:
|
2024-12-09 01:07:26 +00:00
|
|
|
start_time = datetime.datetime.fromisoformat(
|
|
|
|
"2020-11-15 00:00:01.000000000+00:00"
|
|
|
|
)
|
2024-12-03 12:12:48 +00:00
|
|
|
case Flavor.CLASSIC:
|
2024-12-09 01:07:26 +00:00
|
|
|
start_time = datetime.datetime.fromisoformat(
|
|
|
|
"2023-05-23 00:00:01.000000000+00:00"
|
|
|
|
)
|
2024-12-03 12:12:48 +00:00
|
|
|
case _:
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2024-11-30 11:27:32 +00:00
|
|
|
now = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
|
|
match self._alert.alert_type:
|
|
|
|
case AlertType.DAILY_LOW:
|
|
|
|
time_range = datetime.timedelta(days=1)
|
|
|
|
comparison_operator = operator.lt
|
|
|
|
case AlertType.DAILY_HIGH:
|
|
|
|
time_range = datetime.timedelta(days=1)
|
|
|
|
comparison_operator = operator.gt
|
|
|
|
case AlertType.WEEKLY_LOW:
|
|
|
|
time_range = datetime.timedelta(weeks=1)
|
|
|
|
comparison_operator = operator.lt
|
|
|
|
case AlertType.WEEKLY_HIGH:
|
|
|
|
time_range = datetime.timedelta(weeks=1)
|
|
|
|
comparison_operator = operator.gt
|
|
|
|
case AlertType.MONTHLY_LOW:
|
|
|
|
time_range = datetime.timedelta(days=31)
|
|
|
|
comparison_operator = operator.lt
|
|
|
|
case AlertType.MONTHLY_HIGH:
|
|
|
|
time_range = datetime.timedelta(days=31)
|
|
|
|
comparison_operator = operator.gt
|
|
|
|
case AlertType.YEARLY_LOW:
|
|
|
|
time_range = datetime.timedelta(days=365)
|
|
|
|
comparison_operator = operator.lt
|
|
|
|
case AlertType.YEARLY_HIGH:
|
|
|
|
time_range = datetime.timedelta(days=365)
|
|
|
|
comparison_operator = operator.gt
|
|
|
|
case AlertType.ALL_TIME_LOW:
|
2024-12-03 12:12:48 +00:00
|
|
|
time_range = now - start_time
|
2024-11-30 11:27:32 +00:00
|
|
|
comparison_operator = operator.lt
|
|
|
|
case AlertType.ALL_TIME_HIGH:
|
2024-12-03 12:12:48 +00:00
|
|
|
time_range = now - start_time
|
2024-11-30 11:27:32 +00:00
|
|
|
comparison_operator = operator.gt
|
|
|
|
case _:
|
|
|
|
# TODO: The logic here is certainly wrong for Custom
|
|
|
|
time_range = datetime.timedelta(days=int(365.25 * 6))
|
|
|
|
comparison_operator = operator.eq
|
|
|
|
|
|
|
|
if new_datum[0] > now - time_range:
|
|
|
|
if self._last_trigger is None:
|
|
|
|
self._last_trigger = new_datum
|
2024-12-03 12:27:35 +00:00
|
|
|
self._last_alerting = new_datum
|
2024-11-30 11:27:32 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
# If the self._last_trigger falls out of scope of the alert, find the next thing that would have triggered
|
|
|
|
# the alert so the next time a high or low comes up it's correctly comparing against the range of the alert
|
|
|
|
# rather than since the alert triggered
|
|
|
|
if self._last_trigger[0] < now - time_range:
|
|
|
|
self._find_next_trigger(comparison_operator, now - time_range, history)
|
|
|
|
|
|
|
|
if comparison_operator(new_datum[1], self._last_trigger[1]):
|
|
|
|
self._last_trigger = new_datum
|
2024-12-03 12:27:35 +00:00
|
|
|
self._last_alerting = new_datum
|
2024-11-30 12:45:00 +00:00
|
|
|
was_squelched = self._squelched
|
|
|
|
self._squelched = True
|
2024-12-03 12:12:48 +00:00
|
|
|
return not was_squelched
|
2024-11-30 12:45:00 +00:00
|
|
|
elif self._squelched:
|
|
|
|
self._squelched = False
|
2024-12-05 04:31:54 +00:00
|
|
|
return False
|