1.0.1 release #15

Merged
williamp merged 35 commits from testing into master 2023-03-21 18:10:14 +00:00
32 changed files with 335 additions and 96 deletions

View File

@@ -9,6 +9,10 @@ globals:
password: password:
from_secret: REGISTRY_PASSWORD from_secret: REGISTRY_PASSWORD
steps: steps:
- name: brand commit before build
image: bash
commands:
- echo ${DRONE_COMMIT_SHA:0:7} > app/__commit__
- name: build app-testing - name: build app-testing
image: plugins/docker image: plugins/docker
settings: settings:
@@ -35,6 +39,10 @@ globals:
password: password:
from_secret: REGISTRY_PASSWORD from_secret: REGISTRY_PASSWORD
steps: steps:
- name: brand commit before build
image: bash
commands:
- echo ${DRONE_COMMIT_SHA:0:7} > app/__commit__
- name: build-app-prod - name: build-app-prod
image: plugins/docker image: plugins/docker
settings: settings:
@@ -43,7 +51,6 @@ steps:
dockerfile: ./Dockerfile dockerfile: ./Dockerfile
tags: ["${DRONE_COMMIT_SHA:0:7}", "latest-prod"] tags: ["${DRONE_COMMIT_SHA:0:7}", "latest-prod"]
<<: *docker_creds <<: *docker_creds
trigger: trigger:
branch: branch:
- master - master

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
### Database file ### Database file
database.db database.db
database_mysql/
### Python template ### Python template
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files

View File

@@ -1,6 +1,7 @@
FROM python:3.11.0-alpine3.16 FROM python:3.11.0-alpine3.16
COPY ./app /app COPY ./app /app
WORKDIR /app WORKDIR /app
RUN apk add gcc musl-dev mariadb-connector-c-dev
RUN pip3 install -r requirements.txt RUN pip3 install -r requirements.txt
ENV FLASK_APP=app.py ENV FLASK_APP=app.py
CMD ["python3", "-m", "flask", "run", "--host=0.0.0.0"] CMD ["gunicorn", "-b", "0.0.0.0:80", "-w", "4", "app:app"]

1
app/__commit__ Normal file
View File

@@ -0,0 +1 @@
unknown

1
app/__version__ Normal file
View File

@@ -0,0 +1 @@
1.0.1

View File

@@ -3,17 +3,21 @@ 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 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 forms import (TaskForm, EventForm, PeriodForm, SignupForm, LoginForm) from forms import (TaskForm, EventForm, PeriodForm, SignupForm, LoginForm, SettingsForm)
from create_events import createEvents from create_events import createEvents
basedir = os.path.abspath(os.path.dirname(__file__)) basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__) app = Flask(__name__)
app.config['SECRET_KEY'] = 'HwG55rpe83jcaglifXm8NuF4WEeXyJV4' app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
app.config['SQLALCHEMY_DATABASE_URI'] =\ app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, os.environ['SQLITE_DB']) 'mysql://' + os.environ['MYSQL_USER'] + \
':' + os.environ['MYSQL_PASSWORD'] + \
'@' + os.environ['MYSQL_HOST'] + \
':' + os.environ['MYSQL_PORT'] + \
'/' + os.environ['MYSQL_DB']
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app) db.init_app(app)
@@ -21,14 +25,21 @@ 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()
login_manager.login_view = 'login' login_manager.login_view = 'login'
login_manager.init_app(app) login_manager.init_app(app)
if os.environ['SIGNUP_ENABLED'] == "YES": if 'SIGNUP_ENABLED' in os.environ:
if os.environ['SIGNUP_ENABLED'] == "YES":
signup_enabled = True signup_enabled = True
else:
signup_enabled = False
else: else:
signup_enabled = False signup_enabled = False
@@ -36,10 +47,32 @@ else:
def load_user(user_id): def load_user(user_id):
return User.query.get(int(user_id)) return User.query.get(int(user_id))
# Context processor injects current version and commit
@app.context_processor
def injectVerCommit():
return dict(currVersion=currVersion, currCommit=currCommit, datetime=datetime, ZoneInfo=ZoneInfo)
# Error handling
# Pass env variables for debugging in prod
NODE_NAME = os.environ['NODE_NAME']
POD_NAME = os.environ['POD_NAME']
# Error Routes
@app.errorhandler(404)
def notFound(e):
return render_template('errors/404.html', NODE_NAME=NODE_NAME, POD_NAME=POD_NAME), 404
@app.errorhandler(403)
def forbidden(e):
return render_template('errors/403.html', NODE_NAME=NODE_NAME, POD_NAME=POD_NAME), 403
@app.errorhandler(500)
def ISEerror(e):
return render_template('errors/500.html', NODE_NAME=NODE_NAME, POD_NAME=POD_NAME), 500
# Index route # Index route
@app.route('/') @app.route('/')
def index(): def index():
createEvents(db, currDay, Period, Event)
return redirect('/events') return redirect('/events')
# Authentication routes # Authentication routes
@@ -49,12 +82,13 @@ def login():
if form.validate_on_submit(): if form.validate_on_submit():
userName = form.userName.data userName = form.userName.data
password = form.password.data password = form.password.data
remember = form.rememberMe.data
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')
return redirect(url_for('login')) return redirect(url_for('login'))
login_user(user) login_user(user, remember=remember)
return redirect(url_for('events')) return redirect(url_for('events'))
return render_template('login.html', form=form) return render_template('login.html', form=form)
@@ -82,6 +116,19 @@ def logout():
logout_user() logout_user()
return redirect(url_for('index')) return redirect(url_for('index'))
@app.route('/settings', methods=('GET', 'POST'))
@login_required
def settings():
user = User.query.get_or_404(current_user.id)
form = SettingsForm(obj=user)
if form.validate_on_submit():
user.realName = form.realName.data
user.timezone = form.timezone.data
if form.password.data != '':
user.password = generate_password_hash(form.password.data, method='sha256')
db.session.commit()
return render_template('settings.html', form=form)
# Periods routes # Periods routes
@app.route('/periods') @app.route('/periods')
@login_required @login_required
@@ -99,6 +146,7 @@ def newPeriod():
) )
db.session.add(period) db.session.add(period)
db.session.commit() db.session.commit()
# Run createEvents upon adding new period
createEvents(db, currDay, Period, Event) createEvents(db, currDay, Period, Event)
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)
@@ -131,9 +179,8 @@ def delete_period(periodNum):
def events(): def events():
events = Event.query.all() events = Event.query.all()
periods = Period.query.all() periods = Period.query.all()
createEvents(db, currDay, Period, Event)
return render_template('events.html', events=events, periods=periods, datetime=datetime, date=date) return render_template('events.html', events=events, periods=periods, datetime=datetime, date=date, ZoneInfo=ZoneInfo)
@app.route('/event/edit/<int:event_id>/', methods=('GET', 'POST')) @app.route('/event/edit/<int:event_id>/', methods=('GET', 'POST'))
@login_required @login_required

View File

@@ -9,3 +9,5 @@ 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")

View File

@@ -43,3 +43,4 @@ class User(UserMixin, db.Model):
email = db.Column(db.String(100), unique=True) email = db.Column(db.String(100), unique=True)
password = db.Column(db.String(100)) password = db.Column(db.String(100))
realName = db.Column(db.String(1000)) realName = db.Column(db.String(1000))
timezone = db.Column(db.String(20), default='UTC')

View File

@@ -1,6 +1,7 @@
import pytz
from db import Task from db import Task
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import (StringField, DateField, TimeField, TextAreaField, IntegerField, BooleanField, from wtforms import (StringField, DateField, TimeField, TextAreaField, IntegerField, SelectField, BooleanField,
RadioField, EmailField, PasswordField) RadioField, EmailField, PasswordField)
from wtforms.validators import InputRequired, Length from wtforms.validators import InputRequired, Length
from wtforms_sqlalchemy.orm import QuerySelectField from wtforms_sqlalchemy.orm import QuerySelectField
@@ -10,8 +11,7 @@ def get_tasks():
class TaskForm(FlaskForm): class TaskForm(FlaskForm):
title = StringField('Title', validators=[InputRequired(), title = StringField('Title', validators=[InputRequired(),
Length(min=5, max=100)]) Length(min=5, max=100)])
description = TextAreaField('Description', validators=[InputRequired(), description = TextAreaField('Description', validators=[Length(max=200)])
Length(max=200)])
class EventForm(FlaskForm): class EventForm(FlaskForm):
# eventDate = DateField('Date', validators=[InputRequired()], format='m-%d-%Y') # eventDate = DateField('Date', validators=[InputRequired()], format='m-%d-%Y')
# period_num = IntegerField(validators=[InputRequired()]) # period_num = IntegerField(validators=[InputRequired()])
@@ -21,6 +21,11 @@ class PeriodForm(FlaskForm):
weekendSchedule = BooleanField(label='Include on Weekends?', false_values=None) weekendSchedule = BooleanField(label='Include on Weekends?', false_values=None)
periodTime = TimeField('Time', format="%H:%M") periodTime = TimeField('Time', format="%H:%M")
class SettingsForm(FlaskForm):
password = PasswordField('Password')
realName = StringField('Real Name')
timezone = SelectField('Time Zone', choices=pytz.all_timezones)
class SignupForm(FlaskForm): class SignupForm(FlaskForm):
userName = StringField('Username', validators=[InputRequired()]) userName = StringField('Username', validators=[InputRequired()])
password = PasswordField('Password', validators=[InputRequired()]) password = PasswordField('Password', validators=[InputRequired()])
@@ -30,3 +35,4 @@ class SignupForm(FlaskForm):
class LoginForm(FlaskForm): class LoginForm(FlaskForm):
userName = StringField('Username', validators=[InputRequired()]) userName = StringField('Username', validators=[InputRequired()])
password = PasswordField('Password', validators=[InputRequired()]) password = PasswordField('Password', validators=[InputRequired()])
rememberMe = BooleanField(label='Remember me?')

View File

@@ -16,49 +16,49 @@ period9 = Period(period=9, periodTime='15:30:00', weekendSchedule=False)
task1 = Task(id=1, task1 = Task(id=1,
title="Sexy ERP time", title="Sexy ERP time",
description="Be the dominant partner and fuck so much XD", description="Be the dominant partner and fuck so much XD",
created_timestamp='1668278862', due_timestamp='') created_timestamp=1668278862, due_timestamp=None)
task2 = Task(id=2, title="Test task", task2 = Task(id=2, title="Test task",
description="Test", description="Test",
created_timestamp='1668278862', created_timestamp=1668278862,
due_timestamp='') due_timestamp=None)
task3 = Task(id=3, title="La Nager", task3 = Task(id=3, title="La Nager",
description="Francis stuff", description="Francis stuff",
created_timestamp='1668278862', created_timestamp=1668278862,
due_timestamp='') due_timestamp=None)
task4 = Task(id=4, title="Ops/Tech Meeting", task4 = Task(id=4, title="Ops/Tech Meeting",
description="HEIL GEORGE!", description="HEIL GEORGE!",
created_timestamp='1668278862', created_timestamp=1668278862,
due_timestamp='') due_timestamp=None)
task5 = Task(id=5, title="Tech Team Meeting", task5 = Task(id=5, title="Tech Team Meeting",
description="Awkward AF", description="Awkward AF",
created_timestamp='1668278862', created_timestamp=1668278862,
due_timestamp='') due_timestamp=None)
task6 = Task(id=6, title="Write BellScheduler", task6 = Task(id=6, title="Write BellScheduler",
description="heh", description="heh",
created_timestamp='1668278862', created_timestamp=1668278862,
due_timestamp='') due_timestamp=None)
task7 = Task(id=7, title="Fap to cunny porn", task7 = Task(id=7, title="Fap to cunny porn",
description="FBI OPEN UP!", description="FBI OPEN UP!",
created_timestamp='1668278862', created_timestamp=1668278862,
due_timestamp='') due_timestamp=None)
task8 = Task(id=8, title="Go on vrchat", task8 = Task(id=8, title="Go on vrchat",
description="Mmm... virtual headpats!", description="Mmm... virtual headpats!",
created_timestamp='1668278862', created_timestamp=1668278862,
due_timestamp='') due_timestamp=None)
task9 = Task(id=9, title="Brush teeth", task9 = Task(id=9, title="Brush teeth",
description="Ya dont do that more often, ya gross fuck", description="Ya dont do that more often, ya gross fuck",
created_timestamp='1668278862', created_timestamp=1668278862,
due_timestamp='') due_timestamp=None)
event1 = Event(id=1, scheduled_date='11-12-2022', period_num=1, task_id=4) event1 = Event(id=1, scheduled_date='03-17-2023', period_num=1, task_id=4)
event2 = Event(id=2, scheduled_date='11-12-2022', period_num=2, task_id=2) event2 = Event(id=2, scheduled_date='03-17-2023', period_num=2, task_id=2)
event3 = Event(id=3, scheduled_date='11-12-2022', period_num=3, task_id=5) event3 = Event(id=3, scheduled_date='03-17-2023', period_num=3, task_id=5)
event4 = Event(id=4, scheduled_date='11-12-2022', period_num=4, task_id=6) event4 = Event(id=4, scheduled_date='03-17-2023', period_num=4, task_id=6)
event5 = Event(id=5, scheduled_date='11-12-2022', period_num=5, task_id=1) event5 = Event(id=5, scheduled_date='03-17-2023', period_num=5, task_id=1)
event6 = Event(id=6, scheduled_date='11-12-2022', period_num=6, task_id=3) event6 = Event(id=6, scheduled_date='03-17-2023', period_num=6, task_id=3)
event7 = Event(id=7, scheduled_date='11-12-2022', period_num=7, task_id=7) event7 = Event(id=7, scheduled_date='03-17-2023', period_num=7, task_id=7)
event8 = Event(id=8, scheduled_date='11-12-2022', period_num=8, task_id=8) event8 = Event(id=8, scheduled_date='03-17-2023', period_num=8, task_id=8)
event9 = Event(id=9, scheduled_date='11-12-2022', period_num=9, task_id=9) event9 = Event(id=9, scheduled_date='03-17-2023', period_num=9, task_id=9)
db.session.add_all([period1, period2, period3, period4, period5, period6, period7, period8, period9]) db.session.add_all([period1, period2, period3, period4, period5, period6, period7, period8, period9])
db.session.add_all([task1, task2, task3, task4, task5, task6, task7, task8, task9]) db.session.add_all([task1, task2, task3, task4, task5, task6, task7, task8, task9])

View File

@@ -1,36 +0,0 @@
"""empty message
Revision ID: bf65c9f77f9f
Revises:
Create Date: 2022-11-19 09:49:20.565127
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'bf65c9f77f9f'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('userName', sa.String(length=1000), nullable=True),
sa.Column('email', sa.String(length=100), nullable=True),
sa.Column('password', sa.String(length=100), nullable=True),
sa.Column('realName', sa.String(length=1000), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('user')
# ### end Alembic commands ###

View File

@@ -1,4 +1,17 @@
import time import time
from datetime import datetime, date from datetime import datetime, date, timedelta
from zoneinfo import ZoneInfo
# Get current day in UTC and put it in str format
currDay = datetime.now() currDay = datetime.now()
currDay = currDay.strftime('%m-%d-%Y') currDay = currDay.strftime('%m-%d-%Y')
# Get previous day in UTC and put it in str format
prevDay = datetime.now() + timedelta(days=-1)
prevDay = prevDay.strftime('%m-%d-%Y')
with open('__version__','r') as file:
currVersion = file.read()
with open('__commit__','r') as file:
currCommit = file.read()

View File

@@ -1,6 +1,9 @@
# This file is used by pip to install required python packages # This file is used by pip to install required python packages
# Usage: pip install -r requirements.txt # Usage: pip install -r requirements.txt
# Gunicorn WSGI Server
gunicorn==20.1.0
# Flask Framework # Flask Framework
click==8.1.3 click==8.1.3
Flask==2.2.2 Flask==2.2.2
@@ -20,6 +23,15 @@ Flask-User==1.0.2.2
# WTForms Extensions # WTForms Extensions
WTForms-SQLAlchemy==0.3.0 WTForms-SQLAlchemy==0.3.0
# APScheduler automated scheduler
APScheduler==3.9.1.post1
# Python Time packages
pytz==2022.6
# Automated tests # Automated tests
pytest==7.2.0 pytest==7.2.0
pytest-cov==4.0.0 pytest-cov==4.0.0
# MySQL Package
mysqlclient==2.1.1

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
app/static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

1
app/static/manifest.json Normal file
View File

@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/static/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/static/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<!-- Favicon manifest tags -->
<link rel="manifest" href="/static/manifest.json" />
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<!-- Required meta tags --> <!-- Required meta tags -->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
@@ -10,14 +14,14 @@
<title>{% block title %} {% endblock %} - BellScheduler</title> <title>{% block title %} {% endblock %} - BellScheduler</title>
</head> </head>
<body> <body class="d-flex flex-column min-vh-100">
<nav class="navbar navbar-expand-md navbar-light bg-light"> <nav class="navbar navbar-expand-md navbar-light bg-light">
<a class="navbar-brand" href="{{ url_for('index') }}">BellScheduler</a> <a class="navbar-brand" href="{{ url_for('index') }}">BellScheduler</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav"> <ul class="navbar-nav mr-auto mt-3 mt-lg-0">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/events">Events</a> <a class="nav-link" href="/events">Events</a>
</li> </li>
@@ -27,14 +31,17 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/periods">Periods</a> <a class="nav-link" href="/periods">Periods</a>
</li> </li>
</ul>
</div>
<div class="float-right">
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
<a href="{{ url_for('logout') }}" class="nav-link"> Logout {{current_user.userName}} </a> <li class="nav-item">
<a class="nav-link" href="/settings">Settings</a>
</li>
{% endif %}
</ul>
{% if current_user.is_authenticated %}
<a href="{{ url_for('logout') }}"> Logout {{current_user.userName}} </a>
{% endif %} {% endif %}
{% if not current_user.is_authenticated %} {% if not current_user.is_authenticated %}
<a href="{{ url_for('login') }}" class="nav-link"> Login </a> <a href="{{ url_for('login') }}"> Login </a>
{% endif %} {% endif %}
</div> </div>
</nav> </nav>
@@ -42,7 +49,9 @@
<div class="container"> <div class="container">
{% block content %} {% endblock %} {% block content %} {% endblock %}
</div> </div>
<footer class="card-footer mt-auto">
Version {{ currVersion }} &nbsp; &nbsp; Commit: {{ currCommit }}
</footer>
<!-- Optional JavaScript --> <!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS --> <!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>

View File

@@ -9,7 +9,10 @@
<p> <p>
<b><a href="/task/{{ event.tasks.id }}"> <b><a href="/task/{{ event.tasks.id }}">
{{ event.tasks.title }} {{ event.tasks.title }}
</a></b> <br> {{ event.tasks.description }}
</a></b> <br>{% if event.tasks.description != None %}
<p>{{ event.tasks.description }}</p>
{% endif %}
</p> </p>
{% else %} {% else %}
<div><p>(no task assigned... yet)</p></div> <div><p>(no task assigned... yet)</p></div>

View File

@@ -0,0 +1,22 @@
{% extends 'base.html' %}
{% set currDay = datetime.now() %}
{% set currTime = currDay.strftime('%I:%M %p') %}
{% set currDay = currDay.strftime('%m-%d-%Y') %}
{% block content %}
<span><h1><b>{% block title %} Error 403 {% endblock %}</h1></b></span>
<div>
<div>
<h2> Forbidden <br> <br> </h2>
</div>
<div>
Date: {{ currDay }} <br>
Time {{ currTime }} (UTC) <br> <br>
</div>
<div>
Node: {{ NODE_NAME }} <br>
Pod: {{ POD_NAME }}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,22 @@
{% extends 'base.html' %}
{% set currDay = datetime.now() %}
{% set currTime = currDay.strftime('%I:%M %p') %}
{% set currDay = currDay.strftime('%m-%d-%Y') %}
{% block content %}
<span><h1><b>{% block title %} Error 404 {% endblock %}</h1></b></span>
<div>
<div>
<h2> Page Not Found <br> <br> </h2>
</div>
<div>
Date: {{ currDay }} <br>
Time {{ currTime }} (UTC) <br> <br>
</div>
<div>
Node: {{ NODE_NAME }} <br>
Pod: {{ POD_NAME }}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,22 @@
{% extends 'base.html' %}
{% set currDay = datetime.now() %}
{% set currTime = currDay.strftime('%I:%M %p') %}
{% set currDay = currDay.strftime('%m-%d-%Y') %}
{% block content %}
<span><h1><b>{% block title %} Error 500 {% endblock %}</b></h1></span>
<div>
<div>
<h2> Internal Server Error <br> <br> </h2>
</div>
<div>
Date: {{ currDay }} <br>
Time {{ currTime }} (UTC) <br> <br>
</div>
<div>
Node: {{ NODE_NAME }} <br>
Pod: {{ POD_NAME }}
</div>
</div>
{% endblock %}

View File

@@ -1,10 +1,12 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% set currDay = datetime.now() %} {% set currDay = datetime.now() %}
{% set currDay = currDay.astimezone(ZoneInfo(current_user.timezone)) %}
{% set currTime = currDay.strftime('%I:%M %p') %}
{% set currDay = currDay.strftime('%m-%d-%Y') %} {% set currDay = currDay.strftime('%m-%d-%Y') %}
{% block content %} {% block content %}
<span><h1>{% block title %} Events {% endblock %}</h1></span> <span><h1>{% block title %} Events {% endblock %}</h1></span>
<b>Current Date: {{ currDay }} </b> <br> <br> <b>Current Date: {{ currDay }} </b> <br> <b>Current Time: {{ currTime }}</b> <br> <br>
<div> <div>
{% for period in periods %} {% for period in periods %}
<div> <div>
@@ -18,7 +20,9 @@
</a> </a>
</b> </b>
<div> <div>
{% if event.tasks.description != None %}
<p>{{ event.tasks.description }}</p> <p>{{ event.tasks.description }}</p>
{% endif %}
</div> </div>
{% else %} {% else %}
<div><p>(no task assigned... yet)</p></div> <div><p>(no task assigned... yet)</p></div>

View File

@@ -19,6 +19,9 @@
{{ form.password.label }} {{ form.password.label }}
{{ form.password }} {{ form.password }}
</p> </p>
<p>
{{ form.rememberMe.label}} {{ form.rememberMe }}
</p>
<p> <p>
<button class="btn btn-primary" type="submit">Submit</button> <button class="btn btn-primary" type="submit">Submit</button>
</p> </p>

View File

@@ -0,0 +1,23 @@
{% extends 'base.html' %}
{% block content %}
<span><h1>{% block title %} Settings {% endblock %}</h1></span>
<form method="post">
{{ form.csrf_token }}
<p>
<b>User: {{ current_user.userName }}</b>
<div>
{{ form.realName.label }} {{ form.realName }}
</div> <br>
<div>
{{ form.password.label }} {{ form.password }}
</div> <br>
<div>
{{ form.timezone.label }} {{ form.timezone }}
</div>
</p>
<p>
<button class="btn btn-primary" type="submit">Submit</button>
</p>
</form>
{% endblock %}

View File

@@ -8,7 +8,9 @@
<div> <div>
<div> <div>
<br> <br>
{% if task.description != None %}
<p>{{ task.description }}</p> <p>{{ task.description }}</p>
{% endif %}
</div> </div>
<div> <div>
<p>Created: {{ createdTime.strftime('%Y-%m-%d %I:%M %p') }} </p> <p>Created: {{ createdTime.strftime('%Y-%m-%d %I:%M %p') }} </p>

View File

@@ -11,7 +11,9 @@
<p><b>{{ task.title }}</b> </p> <p><b>{{ task.title }}</b> </p>
</a> </a>
<b> <b>
{% if task.description != None %}
{{ task.description }} {{ task.description }}
{% endif %}
</b> </b>
<div> <div>
<p>Created: {{ createdTime.strftime('%Y-%m-%d %I:%M %p') }}</p> <p>Created: {{ createdTime.strftime('%Y-%m-%d %I:%M %p') }}</p>
@@ -19,4 +21,5 @@
<hr> <hr>
</div> </div>
{% endfor %} {% endfor %}
</div>
{% endblock %} {% endblock %}

35
app/worker.py Normal file
View File

@@ -0,0 +1,35 @@
import os
from flask import Flask
from misc import currDay, datetime, time, timedelta
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__))
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
app.config['SQLALCHEMY_DATABASE_URI'] =\
'mysql://' + os.environ['MYSQL_USER'] + \
':' + os.environ['MYSQL_PASSWORD'] + \
'@' + os.environ['MYSQL_HOST'] + \
':' + os.environ['MYSQL_PORT'] + \
'/' + os.environ['MYSQL_DB']
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
# Define function to run create_events with app context
def run_create_events():
with app.app_context():
createEvents(db, currDay, Period, Event)
# Call createEvents on initial launch of the script
run_create_events()
# Set up scheduler to run function at 59th minute of every hour
scheduler = BlockingScheduler()
scheduler.add_job(run_create_events, 'cron', minute=59)
# Start scheduler
scheduler.start()

View File

@@ -4,8 +4,42 @@ services:
image: container-registry.infra.dubyatp.xyz/bellscheduler/app:latest-testing image: container-registry.infra.dubyatp.xyz/bellscheduler/app:latest-testing
restart: always restart: always
environment: environment:
- SQLITE_DB=database.db - MYSQL_USER=root
volumes: - MYSQL_PASSWORD=notasecuresecretkeyonlyuseforlocaldevelopment
- ./database.db:/app/database.db - MYSQL_HOST=db
- MYSQL_PORT=3306
- MYSQL_DB=bellscheduler
- SECRET_KEY=notasecuresecretkeyonlyuseforlocaldevelopment
- NODE_NAME=local
- POD_NAME=local
- FLASK_ENV=development
- FLASK_DEBUG=1
- PYTHONUNBUFFERED=1
ports: ports:
- 127.0.0.1:5000:5000 - 127.0.0.1:80:80
worker:
image: container-registry.infra.dubyatp.xyz/bellscheduler/app:latest-testing
restart: always
entrypoint: python3
command: "-m worker"
environment:
- MYSQL_USER=root
- MYSQL_PASSWORD=notasecuresecretkeyonlyuseforlocaldevelopment
- MYSQL_HOST=db
- MYSQL_PORT=3306
- MYSQL_DB=bellscheduler
- SECRET_KEY=notasecuresecretkeyonlyuseforlocaldevelopment
- NODE_NAME=local
- POD_NAME=local
- FLASK_ENV=development
- FLASK_DEBUG=1
- PYTHONUNBUFFERED=1
db:
image: mariadb:10.7.8-focal
restart: always
environment:
- MARIADB_ROOT_PASSWORD=notasecuresecretkeyonlyuseforlocaldevelopment
volumes:
- ./database_mysql:/var/lib/mysql
ports:
- 127.0.0.1:3306:3306