Files
mealie/mealie/services/scheduler/scheduler_service.py
Hayden c32d7d7486 feat: add user recipe export functionality (#845)
* feat(frontend):  add user recipe export functionality

* remove depreciated folders

* change/remove depreciated folders

* add testing variable in config

* add GUID support for group_id

* improve testing feedback on 422 errors

* remove/cleanup files/folders

* initial user export support

* delete unused css

* update backup page UI

* remove depreciated settings

* feat:  export download links

* fix #813

* remove top level statements

* show footer

* add export purger to scheduler

* update purge glob

* fix meal-planner lockout

* feat:  add bulk delete/purge exports

* style(frontend): 💄 update UI for site settings

* feat:  add version checker

* update documentation

Co-authored-by: hay-kot <hay-kot@pm.me>
2021-12-04 14:18:46 -09:00

105 lines
3.4 KiB
Python

from pathlib import Path
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.schedulers.background import BackgroundScheduler
from mealie.core import root_logger
from mealie.core.config import get_app_dirs
from .scheduled_func import ScheduledFunc
from .scheduler_registry import SchedulerRegistry
logger = root_logger.get_logger()
CWD = Path(__file__).parent
app_dirs = get_app_dirs()
TEMP_DATA = app_dirs.DATA_DIR / ".temp"
SCHEDULER_DB = CWD / ".scheduler.db"
SCHEDULER_DATABASE = f"sqlite:///{SCHEDULER_DB}"
MINUTES_DAY = 1440
MINUTES_15 = 15
MINUTES_HOUR = 60
class SchedulerService:
"""
SchedulerService is a wrapper class around the APScheduler library. It is resonpseible for interacting with the scheduler
and scheduling events. This includes the interval events that are registered in the SchedulerRegistry as well as cron events
that are used for sending webhooks. In most cases, unless the the schedule is dynamic, events should be registered with the
SchedulerRegistry. See app.py for examples.
"""
_scheduler: BackgroundScheduler = None
# Not Sure if this is still needed?
# _job_store: dict[str, ScheduledFunc] = {}
def start():
# Preclean
SCHEDULER_DB.unlink(missing_ok=True)
# Scaffold
TEMP_DATA.mkdir(parents=True, exist_ok=True)
# Register Interval Jobs and Start Scheduler
SchedulerService._scheduler = BackgroundScheduler(jobstores={"default": SQLAlchemyJobStore(SCHEDULER_DATABASE)})
SchedulerService._scheduler.add_job(run_daily, "interval", minutes=MINUTES_DAY, id="Daily Interval Jobs")
SchedulerService._scheduler.add_job(run_hourly, "interval", minutes=MINUTES_HOUR, id="Hourly Interval Jobs")
SchedulerService._scheduler.add_job(run_minutely, "interval", minutes=MINUTES_15, id="Regular Interval Jobs")
SchedulerService._scheduler.start()
@classmethod
@property
def scheduler(cls) -> BackgroundScheduler:
return SchedulerService._scheduler
def add_cron_job(job_func: ScheduledFunc):
SchedulerService.scheduler.add_job(
job_func.callback,
trigger="cron",
name=job_func.id,
hour=job_func.hour,
minute=job_func.minutes,
max_instances=job_func.max_instances,
replace_existing=job_func.replace_existing,
args=job_func.args,
)
# SchedulerService._job_store[job_func.id] = job_func
def update_cron_job(job_func: ScheduledFunc):
SchedulerService.scheduler.reschedule_job(
job_func.id,
trigger="cron",
hour=job_func.hour,
minute=job_func.minutes,
)
# SchedulerService._job_store[job_func.id] = job_func
def _scheduled_task_wrapper(callable):
try:
callable()
except Exception as e:
logger.error(f"Error in scheduled task func='{callable.__name__}': exception='{e}'")
def run_daily():
logger.info("Running daily callbacks")
for func in SchedulerRegistry._daily:
_scheduled_task_wrapper(func)
def run_hourly():
logger.info("Running hourly callbacks")
for func in SchedulerRegistry._hourly:
_scheduled_task_wrapper(func)
def run_minutely():
logger.info("Running minutely callbacks")
for func in SchedulerRegistry._minutely:
_scheduled_task_wrapper(func)