From f8cd526f0eaa728d953e3ac724e7f24121d4e6bf Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sun, 31 Oct 2021 00:12:28 +0200
Subject: [PATCH] #9 - schedule automatic job via cron syntax from settings

---
 settings-example.json               |  6 +++++-
 src/StorageLeaf.py                  | 20 ++++++++++++++++++--
 src/logic/JobScheduler.py           |  8 ++++----
 src/logic/routers/DatabaseRouter.py |  2 +-
 4 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/settings-example.json b/settings-example.json
index 7ad89b6..b6b00b7 100644
--- a/settings-example.json
+++ b/settings-example.json
@@ -32,7 +32,11 @@
                     "numberOfMeasurementsPerDay": 2,
                     "ageInDays": 60
                 }
-            ]
+            ],
+            "automatic": {
+                "enable": true,
+                "cronSchedule": "0 0 * * 0"
+            }
         }
     },
     "api": {
diff --git a/src/StorageLeaf.py b/src/StorageLeaf.py
index f746f23..8b432ca 100644
--- a/src/StorageLeaf.py
+++ b/src/StorageLeaf.py
@@ -1,8 +1,8 @@
-import json
 import os
 
 import uvicorn
 from TheCodeLabs_BaseUtils.DefaultLogger import DefaultLogger
+from apscheduler.triggers.cron import CronTrigger
 from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware
 from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
@@ -10,6 +10,8 @@ from starlette.responses import RedirectResponse, FileResponse
 
 from Settings import SETTINGS, VERSION
 from logic import Constants
+from logic.DatabaseCleanupService import DatabaseCleanupService
+from logic.Dependencies import get_database
 from logic.DiscoveryService import DiscoveryService
 from logic.database import Models
 from logic.database.Database import engine
@@ -20,7 +22,6 @@ LOGGER = DefaultLogger().create_logger_if_not_exists(Constants.APP_NAME)
 
 DATABASE_SETTINGS = SETTINGS['database']
 
-
 # create database tables
 Models.Base.metadata.create_all(bind=engine)
 
@@ -62,6 +63,21 @@ def overridden_redoc():
                           redoc_favicon_url=app.url_path_for('favicon'))
 
 
+@app.on_event("startup")
+async def startup_event():
+    cleanupSettings = SETTINGS['database']['cleanup']
+    if cleanupSettings['automatic']['enable']:
+        cleanupService = DatabaseCleanupService(cleanupSettings)
+
+        try:
+            cronTrigger = CronTrigger.from_crontab(cleanupSettings['automatic']['cronSchedule'])
+        except ValueError as e:
+            raise ValueError(f'Invalid syntax for settings option "cronSchedule": {str(e)}') from e
+
+        from logic import JobScheduler
+        JobScheduler.SCHEDULER.schedule_automatic_job(cleanupService.cleanup, [next(get_database())], cronTrigger)
+
+
 app.include_router(GeneralRouter.router)
 app.include_router(DatabaseRouter.router)
 app.include_router(DeviceRouter.router)
diff --git a/src/logic/JobScheduler.py b/src/logic/JobScheduler.py
index fc4548a..ade6378 100644
--- a/src/logic/JobScheduler.py
+++ b/src/logic/JobScheduler.py
@@ -5,6 +5,7 @@ from typing import Callable, List
 import pytz
 from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR, JobExecutionEvent
 from apscheduler.schedulers.asyncio import AsyncIOScheduler
+from apscheduler.triggers.cron import CronTrigger
 
 from logic import Constants
 from logic.database import Schemas
@@ -45,10 +46,9 @@ class JobScheduler:
         else:
             LOGGER.debug(f'Successfully finished job "{event.job_id}" (retval: {event.retval})')
 
-    def schedule_automatic_job(self, func: Callable, args: List, interval_in_minutes: int):
+    def schedule_automatic_job(self, func: Callable, args: List, cronTrigger: CronTrigger):
         self._jobAutomatic = self._jobAutomatic.modify(func=func, args=args)
-        self._jobAutomatic = self._jobAutomatic.reschedule(trigger='interval', minutes=interval_in_minutes,
-                                                           timezone=TIMEZONE)
+        self._jobAutomatic = self._jobAutomatic.reschedule(trigger=cronTrigger, timezone=TIMEZONE)
         self._jobStatus[self.ID_AUTO] = self.STATE_RUNNING
 
     def run_manual_job(self, func: Callable, args: List):
@@ -79,4 +79,4 @@ class JobAlreadyRunningError(Exception):
     pass
 
 
-SCHEDULER = JobScheduler()
+SCHEDULER: JobScheduler = JobScheduler()
diff --git a/src/logic/routers/DatabaseRouter.py b/src/logic/routers/DatabaseRouter.py
index d96d617..c46fe10 100644
--- a/src/logic/routers/DatabaseRouter.py
+++ b/src/logic/routers/DatabaseRouter.py
@@ -37,6 +37,6 @@ async def databaseCleanup(db: Session = Depends(get_database)):
 @router.get('/databaseCleanup',
             summary='Provides the status of the all scheduled database cleanup jobs',
             response_model=Schemas.ScheduledJobs)
-async def databaseCleanup():
+async def getStatus():
     from logic import JobScheduler
     return JobScheduler.SCHEDULER.get_scheduled_jobs()
-- 
GitLab