commit 3c56072e557df1fa110bd1101e79eda4c368a2b1 Author: nils Date: Sat Feb 24 19:47:04 2024 +0100 check-in first files diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..9293090 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File with Arguments", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/config/runCfg.json b/config/runCfg.json new file mode 100644 index 0000000..af3f339 --- /dev/null +++ b/config/runCfg.json @@ -0,0 +1,47 @@ +{ + "_comment_jobs": "JobTypes can be: cyclic, minutely, daily, weekly, once", + "_comment_jobFunctions": "JobFunctions can be: printScheduler, generateOnePMTTechRoadmap", + "jobs": [ + { + "jobName": "Print schedule", + "jobType": "cyclic", + "jobFunction": "printScheduler", + "dtTimeDelta": { + "minutes": 15 + } + }, + { + "jobName": "Example to show all possible scheduling options...", + "jobType": "once", + "jobFunction": "foo", + "dtTime": { + "_comment": "for minutely,hourly,daily,weekly & once jobs....", + "second": 0, + "minute": 0, + "hour": 0, + "dayOfWeek": [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday" + ] + }, + "dtTimeDelta": { + "_comment": "for cyclic & once jobs, in x minutes....", + "minutes": 0 + }, + "dtDateTime": { + "_comment": "for once jobs, at point in time....", + "year": 2022, + "month": 1, + "day": 1, + "hour": 0, + "minute": 0 + } + + } + ] +} diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..7cb4222 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,35 @@ +version: '3.4' + +services: + + watchdog: + image: watchdog + restart: always + build: + context: . + dockerfile: ./Dockerfile + + mongo: + image: mongo + restart: always + environment: + MONGO_INITDB_ROOT_USERNAME: nils + MONGO_INITDB_ROOT_PASSWORD: mymongopw + ports: + - '27017-27019:27017-27019' + volumes: + - mongo-db:/data/db + - mongo-db:/data/configdb + + mongo-express: + image: mongo-express + restart: always + ports: + - 8081:8081 + environment: + ME_CONFIG_MONGODB_ADMINUSERNAME: nils + ME_CONFIG_MONGODB_ADMINPASSWORD: mymongopw + ME_CONFIG_MONGODB_URL: mongodb://nils:mymongopw@mongo:27017/ + +volumes: + mongo-db: \ No newline at end of file diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..6157ce7 --- /dev/null +++ b/dockerfile @@ -0,0 +1,25 @@ +FROM python:3.11-slim-bullseye + +# Keeps Python from generating .pyc files in the container +ENV PYTHONDONTWRITEBYTECODE=1 + +#install ping for some network tests: +RUN apt-get update || : && apt-get install -y iputils-ping + +# Turns off buffering for easier container logging +ENV PYTHONUNBUFFERED=1 + +# Install pip requirements +COPY . . +RUN python -m pip install -r requirements.txt + +WORKDIR /app +COPY ./lib /app/src/lib +COPY ./services/scheduler /app + +# Creates a non-root user with an explicit UID and adds permission to access the /app folder +RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app +USER appuser + +# Start Server for API: +CMD ["python", "./src/watchdog.py"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c799a4e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +scheduler==0.8.5 +scripts==3.0 diff --git a/src/watchdog.py b/src/watchdog.py new file mode 100644 index 0000000..07d4b59 --- /dev/null +++ b/src/watchdog.py @@ -0,0 +1,180 @@ +import datetime as dt +from scheduler import Scheduler +import scheduler.trigger as trigger +import json +import os.path +import time + +def foo(): + # print("\nJust chilling...\n") + return + +def printScheduler(): + date = dt.datetime.now() + print(str(date)+": The currently available jobs are:\n") + print(schedule) + +def addParameter(name, object, dict): + if name in object: + if type(object[name]) is int: + dict[name] = object[name] + elif type(object[name]) is str: + dict[name] = int(object[name]) + else: + print("ERROR: Element cannot be interpreted correctly:", + object[name]) + +def addDtTimeDeltaJob(job, schedule: Scheduler, function, once): + dtTimeDelta = job['dtTimeDelta'] + if 'minutes' in dtTimeDelta: + parameters = {} + addParameter('minutes', dtTimeDelta, parameters) + if once: + schedule.once(dt.timedelta(**parameters), function) + else: + schedule.cyclic(dt.timedelta(**parameters), function) + else: + print("ERROR: missing minutes in job: ", job['jobName']) + return + +def scheduleDayOfWeek(day, daylist, schedule, function, parameters, once): + if day in daylist: + day_to_call = getattr(trigger, day) + if once: + if len(parameters) > 0: + # schedule.once(day_to_call(dt.time(**parameters)),function) + print("WARN: weekly jobs are currenlty not supported...") + else: + # schedule.once(day_to_call(),function()) + print("WARN: weekly jobs are currenlty not supported...") + else: + if len(parameters) > 0: + # schedule.weekly(day_to_call(dt.time(**parameters)),function) + print("WARN: weekly jobs are currenlty not supported...") + else: + # schedule.weekly(day_to_call(),function) + print("WARN: weekly jobs are currenlty not supported...") + +def addDtTimeJob(job, schedule: Scheduler, function, once): + dtTime = job['dtTime'] + jobType = job['jobType'] + parameters = {} + addParameter('second', dtTime, parameters) + addParameter('minute', dtTime, parameters) + addParameter('hour', dtTime, parameters) + if (jobType == "minutely") or (jobType == "hourly") or (jobType == "daily"): + cycle_to_call = getattr(schedule, jobType) + cycle_to_call(dt.time(**parameters), function) + pass + elif (jobType == 'once') or (jobType == 'weekly'): + if 'dayOfWeek' in dtTime: + scheduleDayOfWeek( + 'Monday', dtTime['dayOfWeek'], schedule, function, parameters, once) + scheduleDayOfWeek( + 'Tuesday', dtTime['dayOfWeek'], schedule, function, parameters, once) + scheduleDayOfWeek( + 'Wednesday', dtTime['dayOfWeek'], schedule, function, parameters, once) + scheduleDayOfWeek( + 'Thursday', dtTime['dayOfWeek'], schedule, function, parameters, once) + scheduleDayOfWeek( + 'Friday', dtTime['dayOfWeek'], schedule, function, parameters, once) + scheduleDayOfWeek( + 'Saturday', dtTime['dayOfWeek'], schedule, function, parameters, once) + scheduleDayOfWeek( + 'Sunday', dtTime['dayOfWeek'], schedule, function, parameters, once) + else: + print( + "ERROR: dayOfWeek is mandatory for once and weekly jobs in job:", job['jobName']) + return + +def addDtDateTimeOnceJob(job, schedule: Scheduler, function): + dtDateTime = job['dtDateTime'] + parameters = {} + addParameter('year', dtDateTime, parameters) + addParameter('month', dtDateTime, parameters) + addParameter('day', dtDateTime, parameters) + addParameter('hour', dtDateTime, parameters) + addParameter('minute', dtDateTime, parameters) + schedule.once(dt.datetime(**parameters), function) + return + +def addJob(job, schedule: Scheduler): + if 'jobFunction' in job: + jobFunction = job['jobFunction'] + else: + print("ERROR: jobFunction is missing in job: ", job['jobName']) + return + if 'jobType' in job: + jobType = job['jobType'] + else: + print("ERROR: jobType is missing in job: ", job['jobName']) + return + + if jobFunction in globals(): + function = globals()[jobFunction] + if jobType == "cyclic": + if 'dtTimeDelta' in job: + addDtTimeDeltaJob(job, schedule, function, once=False) + else: + print("ERROR: Missing dtTimeDelta in: ", job['jobName']) + elif (jobType == "minutely") or (jobType == "hourly") or (jobType == "daily") or (jobType == "weekly"): + if 'dtTime' in job: + addDtTimeJob(job, schedule, function, once=False) + else: + print("ERROR: Missing dtTime in: ", job['jobName']) + elif jobType == "once": + found = False + if 'dtTimeDelta' in job: + addDtTimeDeltaJob(job, schedule, function, once=True) + found = True + if 'dtDateTime' in job: + addDtDateTimeOnceJob(job, schedule, function) + found = True + if 'dtTime' in job: + addDtTimeJob(job, schedule, function, once=True) + found = True + else: + if not found: + print( + "ERROR: Missing either dtTime, dtTimeDelta or dtDateTime in: ", job['jobName']) + else: + print("Unkown JobType: ", jobType) + else: + print("\nERROR: Unknown Function: ", jobFunction) + print(" Ignoring job: ", job['jobName']) + print("\n") + +def addJobs(schedule: Scheduler): + cfgFile = "./config/runCfg.json" + if os.path.isfile(cfgFile): + f = open("./config/runCfg.json") + data = json.load(f) + f.close() + if 'jobs' in data: + for job in data['jobs']: + if 'jobName' in job: + print("Setting up job: ", job['jobName']) + addJob(job, schedule) + else: + print("ERROR: Element is missing a jobName: ", job) + print("\n") + else: + print("WARN: No jobs configured in config file: ", cfgFile) + else: + print("ERROR: No config file found for scheduler: ", cfgFile) + + + +# schedule = Scheduler(n_threads=0) +schedule = Scheduler() +addJobs(schedule) +printScheduler() + +while True: + start_time = time.perf_counter() + n_exec = schedule.exec_jobs() + total_seconds = time.perf_counter() - start_time + if n_exec > 0: + print("Workers started: "+str(n_exec) + + ". Total execution time: {:10.4f} s.\n\n".format(total_seconds)) + time.sleep(1) diff --git a/update_reqs.sh b/update_reqs.sh new file mode 100755 index 0000000..3675f47 --- /dev/null +++ b/update_reqs.sh @@ -0,0 +1 @@ +pipreqs . --force \ No newline at end of file