29 Commits
1.0.1 ... 1.1.0

Author SHA1 Message Date
dbfd1c8b2c Merge pull request 'Version 1.1.0' (#32) from testing into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #32
2023-04-16 19:58:09 +00:00
256c4dddb1 Merge pull request 'garbage-collection' (#31) from garbage-collection into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #31
2023-04-16 19:38:41 +00:00
c4e1a7ab75 add cleanupEvents to worker 2023-04-16 15:24:51 -04:00
723421160f create cleanupEvents function 2023-04-16 15:24:25 -04:00
8d51a75275 bugfix, have hourly run occur at the 59th minute
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-16 10:54:43 -04:00
61f6ea3502 Merge pull request 'Redis Messaging implementation' (#30) from redis-messaging into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #30
2023-04-16 14:44:42 +00:00
e46b1ef4b9 edit docker-compose for celery 2023-04-13 23:56:02 -04:00
85753c5afe rewrite worker to run on celery and redis 2023-04-13 02:11:59 -04:00
b07a9c70d2 Merge pull request '10-detect-empty-db' (#29) from 10-detect-empty-db into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #29
2023-03-24 02:20:17 +00:00
99dc76b25a check for empty database before begin 2023-03-23 22:19:03 -04:00
20af991841 add table detection logic 2023-03-23 20:57:37 -04:00
961573ade1 Merge pull request ''created' time to appear in user's defined tz' (#28) from 22-created-time-localtime into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #28
2023-03-23 23:55:03 +00:00
3a3fd2da9a 'created' time to appear in user's defined tz 2023-03-23 19:53:38 -04:00
ed9f7864e8 Merge pull request 'add podStatus' (#27) from issue-24-liveness-page into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #27
2023-03-23 22:59:03 +00:00
55161f0330 add podStatus 2023-03-23 18:56:59 -04:00
10d0a472b0 Merge pull request 'update base docker image from alpine 3.16/python3.11.0 to alpine 3.17/python3.11.2' (#25) from upgrade-image-python-3.11.2-alpine-3.17 into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #25
2023-03-23 02:15:39 +00:00
f4106f2045 update base docker image from alpine 3.16/python3.11.0 to alpine 3.17/python3.11.2 2023-03-22 22:14:42 -04:00
94bb4c7ac7 Merge pull request 'make links clickable in task descriptions' (#23) from 21-make-links-clickable-tasks into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #23
2023-03-23 02:07:02 +00:00
a58cc0ffcb make links clickable in task descriptions 2023-03-22 21:59:09 -04:00
437c093adf logging bugfix
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-21 22:44:51 -04:00
d0a6b98ddc Merge pull request 'server-logs' (#18) from server-logs into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #18
2023-03-21 23:24:54 +00:00
f1e9c492de Merge branch 'testing' into server-logs 2023-03-21 23:24:48 +00:00
2490fb6698 Merge pull request 'change version branding to 1.1.0' (#17) from version-branding into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #17
2023-03-21 23:24:43 +00:00
fb7df832dc Merge branch 'testing' into version-branding 2023-03-21 23:24:07 +00:00
7372eb625c Merge pull request 'update dependencies' (#16) from dependency-updates into testing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #16
2023-03-21 23:23:15 +00:00
c21cb03546 add logging to apps 2023-03-21 19:18:08 -04:00
b68e48b1cf create logging service 2023-03-21 19:17:35 -04:00
d9391c73e9 change version to 1.1.0 2023-03-21 18:15:01 -04:00
c9b924a1e3 update dependencies 2023-03-21 18:12:48 -04:00
12 changed files with 189 additions and 42 deletions

View File

@@ -1,4 +1,4 @@
FROM python:3.11.0-alpine3.16 FROM python:3.11.2-alpine3.17
COPY ./app /app COPY ./app /app
WORKDIR /app WORKDIR /app
RUN apk add gcc musl-dev mariadb-connector-c-dev RUN apk add gcc musl-dev mariadb-connector-c-dev

View File

@@ -1 +1 @@
1.0.1 1.1.0

View File

@@ -1,12 +1,19 @@
import os import os
import re
from flask import Flask, render_template, redirect, url_for, request, flash from flask import Flask, render_template, redirect, url_for, request, flash
from flask_migrate import Migrate from flask_migrate import Migrate
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import (LoginManager, login_user, login_required, logout_user, current_user) from flask_login import (LoginManager, login_user, login_required, logout_user, current_user)
from misc import datetime, date, time, currDay, prevDay, ZoneInfo, currVersion, currCommit from misc import datetime, date, time, currDay, prevDay, ZoneInfo, currVersion, currCommit
from db import (db, Period, Task, Event, User) from db import (db, Period, Task, Event, User)
from sqlalchemy import inspect
from forms import (TaskForm, EventForm, PeriodForm, SignupForm, LoginForm, SettingsForm) from forms import (TaskForm, EventForm, PeriodForm, SignupForm, LoginForm, SettingsForm)
from create_events import createEvents from log import appLogger
from worker import runCreateEvents
# init logger
logger = appLogger('app')
basedir = os.path.abspath(os.path.dirname(__file__)) basedir = os.path.abspath(os.path.dirname(__file__))
@@ -25,10 +32,6 @@ db.init_app(app)
migrate = Migrate() migrate = Migrate()
migrate.init_app(app, db) migrate.init_app(app, db)
# Schedule creation of events every hour
#with app.app_context():
# scheduleCreateEvents(app, db, currDay, Period, Event, createEvents)
# Authentication stuff # Authentication stuff
login_manager = LoginManager() login_manager = LoginManager()
@@ -70,14 +73,38 @@ def forbidden(e):
def ISEerror(e): def ISEerror(e):
return render_template('errors/500.html', NODE_NAME=NODE_NAME, POD_NAME=POD_NAME), 500 return render_template('errors/500.html', NODE_NAME=NODE_NAME, POD_NAME=POD_NAME), 500
# Pod Status route
@app.route('/podStatus')
def podStatus():
blankPage = ""
try:
db.engine.connect().close()
return blankPage, 200
except:
return blankPage, 500
# Index route # Index route
@app.route('/') @app.route('/', methods=['GET', 'POST'])
def index(): def index():
return redirect('/events') # Check for empty database, go to setup page if not setup
global tablesSetup
required_tables = ['period', 'task', 'event', 'user']
inspector = inspect(db.engine)
tables = inspector.get_table_names()
tablesSetup = all(table in tables for table in required_tables)
if not tablesSetup:
# Initial setup page, should only appear if database is ready but not set up
db.create_all()
logger.info('DB initialized on first run')
return redirect(url_for('createAccount'))
# Otherwise, redirect to /events
else:
return redirect('/events')
# Authentication routes # Authentication routes
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
form = LoginForm() form = LoginForm()
if form.validate_on_submit(): if form.validate_on_submit():
userName = form.userName.data userName = form.userName.data
@@ -87,13 +114,16 @@ def login():
user = User.query.filter_by(userName=userName).first() user = User.query.filter_by(userName=userName).first()
if not user or not check_password_hash(user.password, password): if not user or not check_password_hash(user.password, password):
flash('Credentials incorrect! Please try again') flash('Credentials incorrect! Please try again')
logger.info(f'User \'{userName}\' logon FAILED (bad password) from {sourceIP}')
return redirect(url_for('login')) return redirect(url_for('login'))
login_user(user, remember=remember) login_user(user, remember=remember)
logger.info(f'User \'{userName}\' logged in successfully from {sourceIP}')
return redirect(url_for('events')) return redirect(url_for('events'))
return render_template('login.html', form=form) return render_template('login.html', form=form)
@app.route('/createaccount', methods=['GET', 'POST']) @app.route('/createaccount', methods=['GET', 'POST'])
def createAccount(): def createAccount():
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
if signup_enabled == True: if signup_enabled == True:
form = SignupForm() form = SignupForm()
if form.validate_on_submit(): if form.validate_on_submit():
@@ -105,20 +135,24 @@ def createAccount():
password=generate_password_hash(form.password.data, method='sha256')) password=generate_password_hash(form.password.data, method='sha256'))
db.session.add(new_user) db.session.add(new_user)
db.session.commit() db.session.commit()
logger.info(f'New user \'{new_user.userName}\' created from {sourceIP}')
return redirect(url_for('login')) return redirect(url_for('login'))
return render_template('createAccount.html', form=form) return render_template('createAccount.html', form=form)
else: else:
return 'Account creation is currently disabled' return 'Account creation is currently disabled. <br> Set env var SIGNUP_ENABLED=YES to enable account creation'
@app.route('/logout') @app.route('/logout')
@login_required @login_required
def logout(): def logout():
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
logger.info(f'User \'{current_user.userName}\' logged out from {sourceIP} ')
logout_user() logout_user()
return redirect(url_for('index')) return redirect(url_for('index'))
@app.route('/settings', methods=('GET', 'POST')) @app.route('/settings', methods=('GET', 'POST'))
@login_required @login_required
def settings(): def settings():
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
user = User.query.get_or_404(current_user.id) user = User.query.get_or_404(current_user.id)
form = SettingsForm(obj=user) form = SettingsForm(obj=user)
if form.validate_on_submit(): if form.validate_on_submit():
@@ -127,6 +161,7 @@ def settings():
if form.password.data != '': if form.password.data != '':
user.password = generate_password_hash(form.password.data, method='sha256') user.password = generate_password_hash(form.password.data, method='sha256')
db.session.commit() db.session.commit()
logger.info(f'User \'{current_user.userName}\' settings changed from {sourceIP}')
return render_template('settings.html', form=form) return render_template('settings.html', form=form)
# Periods routes # Periods routes
@@ -139,6 +174,7 @@ def periods():
@app.route('/period/new', methods=('GET', 'POST')) @app.route('/period/new', methods=('GET', 'POST'))
@login_required @login_required
def newPeriod(): def newPeriod():
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
form = PeriodForm() form = PeriodForm()
if form.validate_on_submit(): if form.validate_on_submit():
period = Period(periodTime=form.periodTime.data, period = Period(periodTime=form.periodTime.data,
@@ -146,8 +182,9 @@ def newPeriod():
) )
db.session.add(period) db.session.add(period)
db.session.commit() db.session.commit()
logger.info(f'New period added by \'{current_user.userName}\' from {sourceIP}')
# Run createEvents upon adding new period # Run createEvents upon adding new period
createEvents(db, currDay, Period, Event) runCreateEvents()
return redirect(f'/period/edit/{period.period}') return redirect(f'/period/edit/{period.period}')
return render_template('newPeriod.html', form=form) return render_template('newPeriod.html', form=form)
@@ -155,21 +192,25 @@ def newPeriod():
@app.route('/period/edit/<int:periodNum>', methods=('GET', 'POST')) @app.route('/period/edit/<int:periodNum>', methods=('GET', 'POST'))
@login_required @login_required
def editPeriod(periodNum): def editPeriod(periodNum):
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
period = Period.query.get_or_404(periodNum) period = Period.query.get_or_404(periodNum)
form = PeriodForm(obj=period) form = PeriodForm(obj=period)
if form.validate_on_submit(): if form.validate_on_submit():
period.periodTime = form.periodTime.data period.periodTime = form.periodTime.data
period.weekendSchedule = form.weekendSchedule.data period.weekendSchedule = form.weekendSchedule.data
db.session.commit() db.session.commit()
logger.info(f'Period {periodNum} edited by \'{current_user.userName}\' from {sourceIP}')
return redirect(f'/period/edit/{periodNum}') return redirect(f'/period/edit/{periodNum}')
return render_template('editPeriod.html', period=period, form=form, datetime=datetime) return render_template('editPeriod.html', period=period, form=form, datetime=datetime)
@app.post('/period/delete/<int:periodNum>') @app.post('/period/delete/<int:periodNum>')
@login_required @login_required
def delete_period(periodNum): def delete_period(periodNum):
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
period = Period.query.get_or_404(periodNum) period = Period.query.get_or_404(periodNum)
db.session.delete(period) db.session.delete(period)
db.session.commit() db.session.commit()
logger.info(f'Period {periodNum} deleted by \'{current_user.userName}\' from {sourceIP}')
return redirect('/periods') return redirect('/periods')
@@ -180,11 +221,12 @@ def events():
events = Event.query.all() events = Event.query.all()
periods = Period.query.all() periods = Period.query.all()
return render_template('events.html', events=events, periods=periods, datetime=datetime, date=date, ZoneInfo=ZoneInfo) return render_template('events.html', events=events, periods=periods, datetime=datetime, date=date, ZoneInfo=ZoneInfo, re=re)
@app.route('/event/edit/<int:event_id>/', methods=('GET', 'POST')) @app.route('/event/edit/<int:event_id>/', methods=('GET', 'POST'))
@login_required @login_required
def editEvent(event_id): def editEvent(event_id):
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
event = Event.query.get_or_404(event_id) event = Event.query.get_or_404(event_id)
form = EventForm(obj=event) form = EventForm(obj=event)
if form.validate_on_submit(): if form.validate_on_submit():
@@ -193,6 +235,7 @@ def editEvent(event_id):
else: else:
event.task_id = None event.task_id = None
db.session.commit() db.session.commit()
logger.info(f'Event {event_id} edited by \'{current_user.userName}\' from {sourceIP}')
return redirect('/events') return redirect('/events')
return render_template('editEvent.html', event=event, form=form, datetime=datetime) return render_template('editEvent.html', event=event, form=form, datetime=datetime)
@@ -201,17 +244,18 @@ def editEvent(event_id):
@login_required @login_required
def tasks(): def tasks():
tasks = Task.query.all() tasks = Task.query.all()
return render_template('tasks.html', str=str, tasks=tasks, datetime=datetime, date=date) return render_template('tasks.html', str=str, tasks=tasks, datetime=datetime, date=date, re=re)
@app.route('/task/<int:task_id>/') @app.route('/task/<int:task_id>/')
@login_required @login_required
def task(task_id): def task(task_id):
task = Task.query.get_or_404(task_id) task = Task.query.get_or_404(task_id)
return render_template('task.html', str=str, task=task, datetime=datetime, date=date) return render_template('task.html', str=str, task=task, datetime=datetime, date=date, re=re)
@app.route('/task/new', methods=('GET', 'POST')) @app.route('/task/new', methods=('GET', 'POST'))
@login_required @login_required
def newTask(): def newTask():
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
form = TaskForm() form = TaskForm()
if form.validate_on_submit(): if form.validate_on_submit():
task = Task(title=form.title.data, task = Task(title=form.title.data,
@@ -219,24 +263,29 @@ def newTask():
created_timestamp=int(time.time())) created_timestamp=int(time.time()))
db.session.add(task) db.session.add(task)
db.session.commit() db.session.commit()
logger.info(f'New task added by \'{current_user.userName}\' from {sourceIP}')
return redirect(f'/task/{task.id}') return redirect(f'/task/{task.id}')
return render_template('newtask.html', form=form) return render_template('newtask.html', form=form)
@app.route('/task/<int:task_id>/edit', methods=('GET', 'POST')) @app.route('/task/<int:task_id>/edit', methods=('GET', 'POST'))
@login_required @login_required
def editTask(task_id): def editTask(task_id):
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
task = Task.query.get_or_404(task_id) task = Task.query.get_or_404(task_id)
form = TaskForm(obj=task) form = TaskForm(obj=task)
if form.validate_on_submit(): if form.validate_on_submit():
task.title=form.title.data task.title=form.title.data
task.description=form.description.data task.description=form.description.data
db.session.commit() db.session.commit()
logger.info(f'Task {task_id} edited by \'{current_user.userName}\' from {sourceIP}')
return redirect(f'/task/{task_id}') return redirect(f'/task/{task_id}')
return render_template('edittask.html', task=task, form=form) return render_template('edittask.html', task=task, form=form)
@app.post('/task/<int:task_id>/delete') @app.post('/task/<int:task_id>/delete')
@login_required @login_required
def delete_task(task_id): def delete_task(task_id):
sourceIP = request.environ.get('HTTP_X_REAL_IP', request.remote_addr)
task = Task.query.get_or_404(task_id) task = Task.query.get_or_404(task_id)
db.session.delete(task) db.session.delete(task)
db.session.commit() db.session.commit()
logger.info(f'Task {task_id} deleted by \'{current_user.userName}\' from {sourceIP}')
return redirect('/tasks') return redirect('/tasks')

16
app/cleanup_events.py Normal file
View File

@@ -0,0 +1,16 @@
from datetime import datetime, timedelta
def cleanupEvents(db, Event):
# calculate the date one month ago
one_month_ago = datetime.now() - timedelta(days=30)
# get events older than one month
old_events = db.session.query(Event).filter(
db.func.STR_TO_DATE(Event.scheduled_date, '%m-%d-%Y') < one_month_ago.date()
).all()
# delete old events
for event in old_events:
db.session.delete(event)
db.session.commit()

View File

@@ -1,3 +1,6 @@
from log import appLogger
logger = appLogger('app')
def createEvents(db, currDay, Period, Event): def createEvents(db, currDay, Period, Event):
periods = Period.query.all() periods = Period.query.all()
for period in periods: for period in periods:
@@ -10,4 +13,4 @@ def createEvents(db, currDay, Period, Event):
db.session.add(event) db.session.add(event)
db.session.commit() db.session.commit()
print("createEvents script ran successfully") logger.info('createEvents script ran successfully')

14
app/log.py Normal file
View File

@@ -0,0 +1,14 @@
import logging
import sys
def appLogger(name):
formatter = logging.Formatter(fmt='%(asctime)s %(levelname)-8s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
screen_handler = logging.StreamHandler(stream=sys.stdout)
screen_handler.setFormatter(formatter)
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
if (logger.hasHandlers()):
logger.handlers.clear()
logger.addHandler(screen_handler)
return logger

View File

@@ -6,31 +6,35 @@ gunicorn==20.1.0
# Flask Framework # Flask Framework
click==8.1.3 click==8.1.3
Flask==2.2.2 Flask==2.2.3
itsdangerous==2.1.2 itsdangerous==2.1.2
Jinja2==3.1.2 Jinja2==3.1.2
MarkupSafe==2.1.1 MarkupSafe==2.1.2
Werkzeug==2.2.2 Werkzeug==2.2.3
# Flask Packages # Flask Packages
Flask-Login==0.6.2 Flask-Login==0.6.2
Flask-Migrate==3.1.0 Flask-Migrate==4.0.4
Flask-Script==2.0.6 Flask-Script==2.0.6
Flask-SQLAlchemy==3.0.2 Flask-SQLAlchemy==3.0.3
Flask-WTF==1.0.1 Flask-WTF==1.1.1
Flask-User==1.0.2.2 Flask-User==1.0.2.2
# WTForms Extensions # WTForms Extensions
WTForms-SQLAlchemy==0.3.0 WTForms-SQLAlchemy==0.3.0
# APScheduler automated scheduler # APScheduler automated scheduler
APScheduler==3.9.1.post1 APScheduler==3.10.1
# Celery Task Queue
celery[redis]==5.2.7
redis==4.5.4
# Python Time packages # Python Time packages
pytz==2022.6 pytz==2022.7.1
# Automated tests # Automated tests
pytest==7.2.0 pytest==7.2.2
pytest-cov==4.0.0 pytest-cov==4.0.0
# MySQL Package # MySQL Package

View File

@@ -21,7 +21,17 @@
</b> </b>
<div> <div>
{% if event.tasks.description != None %} {% if event.tasks.description != None %}
<p>{{ event.tasks.description }}</p> {% set description = event.tasks.description %}
{% set url_regex = '(https?://[^\s]+)' %}
{% set match = re.search(url_regex, description) %}
{% if match %}
{% set url = match.group(0) %}
{% set rest = description[match.end(0):] %}
<p>{{ description[:match.start(0)] }}<a href="{{ url }}">{{ url }}</a>{{ rest }}</p>
{% else %}
<p>{{ description }}</p>
{% endif %}
{% endif %} {% endif %}
</div> </div>
{% else %} {% else %}

View File

@@ -1,6 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% set createdTime = datetime.fromtimestamp(task.created_timestamp) %} {% set createdTime = datetime.fromtimestamp(task.created_timestamp) %}
{% set createdTime = datetime.strptime(str(createdTime), '%Y-%m-%d %H:%M:%S') %} {% set createdTime = datetime.strptime(str(createdTime), '%Y-%m-%d %H:%M:%S') %}
{% set createdTime = createdTime.astimezone(ZoneInfo(current_user.timezone)) %}
{% block content %} {% block content %}
<span><h1>{% block title %} {{ task.title }} {% endblock %}</h1></span> <span><h1>{% block title %} {{ task.title }} {% endblock %}</h1></span>
@@ -9,7 +10,17 @@
<div> <div>
<br> <br>
{% if task.description != None %} {% if task.description != None %}
<p>{{ task.description }}</p> {% set description = task.description %}
{% set url_regex = '(https?://[^\s]+)' %}
{% set match = re.search(url_regex, description) %}
{% if match %}
{% set url = match.group(0) %}
{% set rest = description[match.end(0):] %}
<p>{{ description[:match.start(0)] }}<a href="{{ url }}">{{ url }}</a>{{ rest }}</p>
{% else %}
<p>{{ description }}</p>
{% endif %}
{% endif %} {% endif %}
</div> </div>
<div> <div>

View File

@@ -6,13 +6,24 @@
{% for task in tasks %} {% for task in tasks %}
{% set createdTime = datetime.fromtimestamp(task.created_timestamp) %} {% set createdTime = datetime.fromtimestamp(task.created_timestamp) %}
{% set createdTime = datetime.strptime(str(createdTime), '%Y-%m-%d %H:%M:%S') %} {% set createdTime = datetime.strptime(str(createdTime), '%Y-%m-%d %H:%M:%S') %}
{% set createdTime = createdTime.astimezone(ZoneInfo(current_user.timezone)) %}
<div> <div>
<a href="/task/{{ task.id }}"> <a href="/task/{{ task.id }}">
<p><b>{{ task.title }}</b> </p> <p><b>{{ task.title }}</b> </p>
</a> </a>
<b> <b>
{% if task.description != None %} {% if task.description != None %}
{{ task.description }} {% set description = task.description %}
{% set url_regex = '(https?://[^\s]+)' %}
{% set match = re.search(url_regex, description) %}
{% if match %}
{% set url = match.group(0) %}
{% set rest = description[match.end(0):] %}
<p>{{ description[:match.start(0)] }}<a href="{{ url }}">{{ url }}</a>{{ rest }}</p>
{% else %}
<p>{{ description }}</p>
{% endif %}
{% endif %} {% endif %}
</b> </b>
<div> <div>

View File

@@ -1,9 +1,11 @@
from celery import Celery
from celery.schedules import crontab
from create_events import createEvents
from cleanup_events import cleanupEvents
import os import os
from flask import Flask from flask import Flask
from misc import currDay, datetime, time, timedelta from misc import currDay, datetime, time, timedelta
from db import db, Period, Event from db import db, Period, Event
from create_events import createEvents
from apscheduler.schedulers.background import BlockingScheduler
basedir = os.path.abspath(os.path.dirname(__file__)) basedir = os.path.abspath(os.path.dirname(__file__))
@@ -18,18 +20,33 @@ app.config['SQLALCHEMY_DATABASE_URI'] =\
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app) db.init_app(app)
# Define function to run create_events with app context redis_url = 'redis://' + \
def run_create_events(): os.environ['REDIS_HOST'] + \
':' + os.environ['REDIS_PORT'] + \
'/' + os.environ['REDIS_DBNUM']
celerymsg = Celery('tasks', backend=redis_url, broker=redis_url)
# Task definitions
@celerymsg.task
def runCreateEvents():
with app.app_context(): with app.app_context():
createEvents(db, currDay, Period, Event) createEvents(db, currDay, Period, Event)
# Call createEvents on initial launch of the script def runCleanupEvents():
run_create_events() with app.app_context():
cleanupEvents(db, Event)
# Scheduled tasks
# Set up scheduler to run function at 59th minute of every hour celerymsg.conf.beat_schedule = {
scheduler = BlockingScheduler() 'hourly-createevents': {
scheduler.add_job(run_create_events, 'cron', minute=59) 'task': 'worker.runCreateEvents',
# Run hourly
# Start scheduler 'schedule': crontab(hour="*", minute="59"),
scheduler.start() },
'monthly-cleanupevents': {
'task': 'worker.runCleanupEvents',
# Run monthly
'schedule': crontab(day_of_month="29")
}
}

View File

@@ -9,31 +9,43 @@ services:
- MYSQL_HOST=db - MYSQL_HOST=db
- MYSQL_PORT=3306 - MYSQL_PORT=3306
- MYSQL_DB=bellscheduler - MYSQL_DB=bellscheduler
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_DBNUM=0
- SECRET_KEY=notasecuresecretkeyonlyuseforlocaldevelopment - SECRET_KEY=notasecuresecretkeyonlyuseforlocaldevelopment
- NODE_NAME=local - NODE_NAME=local
- POD_NAME=local - POD_NAME=local
- FLASK_ENV=development - FLASK_ENV=development
- FLASK_DEBUG=1 - FLASK_DEBUG=1
- PYTHONUNBUFFERED=1 - PYTHONUNBUFFERED=1
- SIGNUP_ENABLED=YES
ports: ports:
- 127.0.0.1:80:80 - 127.0.0.1:80:80
worker: worker:
image: container-registry.infra.dubyatp.xyz/bellscheduler/app:latest-testing image: container-registry.infra.dubyatp.xyz/bellscheduler/app:latest-testing
restart: always restart: always
entrypoint: python3 entrypoint: celery
command: "-m worker" command: "-A worker.celerymsg worker --loglevel=DEBUG -B"
environment: environment:
- MYSQL_USER=root - MYSQL_USER=root
- MYSQL_PASSWORD=notasecuresecretkeyonlyuseforlocaldevelopment - MYSQL_PASSWORD=notasecuresecretkeyonlyuseforlocaldevelopment
- MYSQL_HOST=db - MYSQL_HOST=db
- MYSQL_PORT=3306 - MYSQL_PORT=3306
- MYSQL_DB=bellscheduler - MYSQL_DB=bellscheduler
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_DBNUM=0
- SECRET_KEY=notasecuresecretkeyonlyuseforlocaldevelopment - SECRET_KEY=notasecuresecretkeyonlyuseforlocaldevelopment
- NODE_NAME=local - NODE_NAME=local
- POD_NAME=local - POD_NAME=local
- FLASK_ENV=development - FLASK_ENV=development
- FLASK_DEBUG=1 - FLASK_DEBUG=1
- PYTHONUNBUFFERED=1 - PYTHONUNBUFFERED=1
redis:
image: redis:7.0.10-alpine3.17
restart: always
ports:
- 127.0.0.1:6379:6379
db: db:
image: mariadb:10.7.8-focal image: mariadb:10.7.8-focal
restart: always restart: always