feat: mealplan-webhooks (#1403)

* fix type errors on event bus

* webhooks fields required for new implementation

* db migration

* wip: webhook query + tests and stub function

* ignore type checker error

* type and method cleanup

* datetime and time utc validator

* update testing code for utc scheduled time

* fix file cmp function call

* update version_number

* add support for translating "time" objects when restoring backup

* bump recipe-scrapers

* use specific import syntax

* generate frontend types

* utilize names exports

* use utc times

* add task to scheduler

* implement new scheduler functionality

* stub for type annotation

* implement meal-plan data getter

* add experimental banner
This commit is contained in:
Hayden
2022-06-17 13:25:47 -08:00
committed by GitHub
parent b1256f4ad2
commit 5a053cdcd6
22 changed files with 428 additions and 93 deletions

View File

@@ -18,10 +18,12 @@ class AlchemyExporter(BaseService):
look_for_datetime = {"created_at", "update_at", "date_updated", "timestamp", "expires_at"}
look_for_date = {"date_added", "date"}
look_for_time = {"scheduled_time"}
class DateTimeParser(BaseModel):
date: datetime.date = None
time: datetime.datetime = None
dt: datetime.datetime = None
time: datetime.time = None
def __init__(self, connection_str: str) -> None:
super().__init__()
@@ -44,10 +46,11 @@ class AlchemyExporter(BaseService):
data[key] = [AlchemyExporter.convert_to_datetime(item) for item in value]
elif isinstance(value, str):
if key in AlchemyExporter.look_for_datetime:
data[key] = AlchemyExporter.DateTimeParser(time=value).time
data[key] = AlchemyExporter.DateTimeParser(dt=value).dt
if key in AlchemyExporter.look_for_date:
data[key] = AlchemyExporter.DateTimeParser(date=value).date
if key in AlchemyExporter.look_for_time:
data[key] = AlchemyExporter.DateTimeParser(time=value).time
return data
@staticmethod

View File

@@ -37,7 +37,7 @@ class EventBusService:
self.bg = bg
self._publisher = ApprisePublisher
self.session = session
self.group_id = None
self.group_id: UUID4 | None = None
@property
def publisher(self) -> PublisherLike:
@@ -55,7 +55,7 @@ class EventBusService:
def dispatch(
self, group_id: UUID4, event_type: EventTypes, msg: str = "", event_source: EventSource = None
) -> None:
self.group_id = group_id # type: ignore
self.group_id = group_id
def _dispatch(event_source: EventSource = None):
if urls := self.get_urls(event_type):

View File

@@ -1,6 +1,14 @@
from .purge_group_exports import *
from .purge_password_reset import *
from .purge_registration import *
from .post_webhooks import post_group_webhooks
from .purge_group_exports import purge_group_data_exports
from .purge_password_reset import purge_password_reset_tokens
from .purge_registration import purge_group_registration
__all__ = [
"post_group_webhooks",
"purge_password_reset_tokens",
"purge_group_data_exports",
"purge_group_registration",
]
"""
Tasks Package

View File

@@ -0,0 +1,54 @@
from datetime import datetime, timezone
import requests
from fastapi.encoders import jsonable_encoder
from pydantic import UUID4
from sqlalchemy.orm import Session
from mealie.db.db_setup import create_session
from mealie.db.models.group.webhooks import GroupWebhooksModel
from mealie.repos.all_repositories import get_repositories
last_ran = datetime.now(timezone.utc)
def get_scheduled_webhooks(session: Session, bottom: datetime, top: datetime) -> list[GroupWebhooksModel]:
"""
get_scheduled_webhooks queries the database for all webhooks scheduled between the bottom and
top time ranges. It returns a list of GroupWebhooksModel objects.
"""
return (
session.query(GroupWebhooksModel)
.where(
GroupWebhooksModel.enabled == True, # noqa: E712 - required for SQLAlchemy comparison
GroupWebhooksModel.scheduled_time > bottom.astimezone(timezone.utc).time(),
GroupWebhooksModel.scheduled_time <= top.astimezone(timezone.utc).time(),
)
.all()
)
def post_group_webhooks() -> None:
global last_ran
session = create_session()
results = get_scheduled_webhooks(session, last_ran, datetime.now())
last_ran = datetime.now(timezone.utc)
repos = get_repositories(session)
memo = {}
def get_meals(group_id: UUID4):
if group_id not in memo:
memo[group_id] = repos.meals.get_all(group_id=group_id)
return memo[group_id]
for result in results:
meals = get_meals(result.group_id)
if not meals:
continue
requests.post(result.url, json=jsonable_encoder(meals))