17 Commits

Author SHA1 Message Date
d1d52fa4b6 source /home/vivek/Git/ska-referee-test/ref-test/env/bin/activateMerge branch 'development' 2022-09-13 12:05:37 +01:00
80dc8b3cff Fixed docker-compose depends_on mappings 2022-09-13 12:03:40 +01:00
a9ccd64de2 Updated dependency list 2022-09-13 11:17:17 +01:00
f5b9758bb1 Removed unused imports 2022-09-13 11:17:03 +01:00
84570d5974 Added indices to various database fields 2022-09-13 11:01:28 +01:00
edb8241ad3 Removed the word Beta from site title 2022-09-13 11:00:53 +01:00
644a539ed9 Changed to conventional extension for sqlite db 2022-09-13 11:00:07 +01:00
f05568b0de source /home/vivek/Git/ska-referee-test/ref-test/env/bin/activateMerge branch 'development' 2022-08-27 09:44:07 +01:00
da4a3e41c6 Bugfix: Wrong account password for updating user 2022-08-27 09:42:48 +01:00
77f86f7102 Bugfix: Corrected dataset name in test editor 2022-08-23 11:03:18 +01:00
358695977f Docker compose glitches 2022-08-20 18:21:52 +01:00
ddfd75c1f8 Added selecting database to Readme 2022-08-20 17:46:45 +01:00
f4642767ac Tweaking formatting of docker-compose file 2022-08-20 17:28:45 +01:00
2f729de40b mysql compose 2022-08-20 17:25:07 +01:00
d68beb938f Tweaking docker-compose 2022-08-20 17:21:21 +01:00
ca667f7896 Create database before first request 2022-08-20 16:51:13 +01:00
0cc00ef911 Updated install script to only create SQLite file 2022-08-20 16:50:34 +01:00
23 changed files with 143 additions and 38 deletions

View File

@ -57,6 +57,16 @@ Once in the destination folder, clone all the relevant files you will need for t
(Remember to include the trailing dot at the end, as that indicates to Git to download the files in the current directory.)
#### Choose What Database Engine You Will Use
This app is designed to use an SQLite database by default.
You can set it up to use a MySQL database by configuring the environment variables accordingly.
If your database is being hosted remotely, make sure the MySQL database has the proper authentication for the user from a remote server.
Alternatively, you can also use the second `docker-compose-mysql.yml` file which provides a MySQL database as part of the cluster.
To use the second `docker-compose-mysql.yml` file, use the following command at the last step of the installation:
```sudo docker compose -f docker-compose-mysql.yml up```
#### Populate Environment Variables
Configuration values for the app are stored in the environment variables file.

90
docker-compose-mysql.yml Normal file
View File

@ -0,0 +1,90 @@
version: '3.9'
volumes:
app:
mysql:
services:
nginx:
container_name: reftest_server
image: nginx:alpine
volumes:
- ./certbot:/etc/letsencrypt:ro
- ./nginx:/etc/nginx
- ./src/html/certbot:/usr/share/nginx/html/certbot:ro
- ./src/html/robots.txt:/usr/share/nginx/html/robots.txt:ro
- ./ref-test/app/root:/usr/share/nginx/html/root:ro
- ./ref-test/app/admin/static:/usr/share/nginx/html/admin/static:ro
- ./ref-test/app/editor/static:/usr/share/nginx/html/editor/static:ro
- ./ref-test/app/quiz/static:/usr/share/nginx/html/quiz/static:ro
- ./ref-test/app/view/static:/usr/share/nginx/html/view/static:ro
ports:
- 80:80
- 443:443
restart: unless-stopped
networks:
- frontend
depends_on:
- app
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
app:
container_name: reftest_app
image: reftest
build: ./ref-test
env_file:
- ./.env
ports:
- 5000
volumes:
- app:/ref-test/data
- ./logs:/ref-test/data/logs
restart: unless-stopped
networks:
- frontend
- backend
depends_on:
postfix:
mysql:
condition: service_healthy
postfix:
container_name: reftest_postfix
image: catatnight/postfix:latest
restart: unless-stopped
env_file:
- ./.env
ports:
- 25
networks:
- backend
certbot:
container_name: reftest_certbot
image: certbot/certbot
volumes:
- ./certbot:/etc/letsencrypt
- ./src/html/certbot:/var/www/html
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
mysql:
container_name: reftest_db
image: mysql:8.0
env_file:
- ./.env
volumes:
- mysql:/var/lib/mysql
ports:
- 3306
networks:
- backend
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
timeout: 10s
retries: 10
networks:
frontend:
external: false
backend:
external: false

View File

@ -1,7 +1,7 @@
version: '3.9'
volumes:
data:
app:
services:
nginx:
@ -36,7 +36,7 @@ services:
ports:
- 5000
volumes:
- data:/ref-test/data
- app:/ref-test/data
- ./logs:/ref-test/data/logs
restart: unless-stopped
networks:

View File

@ -1,5 +1,5 @@
from .config import Production as Config
from .models import User
from .models import *
from .extensions import bootstrap, csrf, db, login_manager, mail
from .tools.logs import write
@ -59,4 +59,10 @@ def create_app():
app.register_blueprint(editor, url_prefix='/admin/editor')
app.register_blueprint(view, url_prefix='/admin/view')
"""Create Database Tables before First Request"""
@app.before_first_request
def _create_database_tables():
with app.app_context():
db.create_all()
return app

View File

@ -1,6 +1,6 @@
<nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark">
<div class="container">
<a href="{{ url_for('admin._home') }}" class="navbar-brand mb-0 h1">RefTest (Beta) | Admin</a>
<a href="{{ url_for('admin._home') }}" class="navbar-brand mb-0 h1">RefTest | Admin</a>
<button
class="navbar-toggler"
type="button"

View File

@ -19,7 +19,7 @@
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Dataset</h5>
</div>
{{ test.dataset.date.strftime('%Y%m%d%H%M%S') }}
<a href="{{ url_for('view._view_console', id=test.dataset.id) }}">{{ test.dataset.get_name() }}</a>
</li>
<li class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">

View File

@ -215,7 +215,7 @@ def _update_user(id:str):
if request.method == 'POST':
if not user: return jsonify({'error': 'User does not exist.'}), 400
if form.validate_on_submit():
if not user.verify_password(request.form.get('confirm_password')): return jsonify({'error': 'Invalid password for your account.'}), 401
if not current_user.verify_password(request.form.get('confirm_password')): return jsonify({'error': 'Invalid password for your account.'}), 401
success, message = user.update(
password = request.form.get('password'),
email = request.form.get('email'),

View File

@ -37,7 +37,7 @@ class Config(object):
MYSQL_USER = os.getenv('MYSQL_USER')
MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD')
SQLALCHEMY_DATABASE_URI = f'mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{DATABASE_HOST}:{DATABASE_PORT}/{MYSQL_DATABASE}'
else: SQLALCHEMY_DATABASE_URI = f'sqlite:///{Path(os.path.abspath(f"{DATA}/database.db"))}'
else: SQLALCHEMY_DATABASE_URI = f'sqlite:///{Path(os.path.abspath(f"{DATA}/db.sqlite"))}'
class Production(Config):
pass

View File

@ -1,6 +1,6 @@
<nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark">
<div class="container">
<a href="{{ url_for('admin._home') }}" class="navbar-brand mb-0 h1">RefTest (Beta) | Admin</a>
<a href="{{ url_for('admin._home') }}" class="navbar-brand mb-0 h1">RefTest | Admin</a>
<button
class="navbar-toggler"
type="button"

View File

@ -14,8 +14,7 @@ from pathlib import Path
from uuid import uuid4
class Dataset(db.Model):
id = db.Column(db.String(36), primary_key=True)
id = db.Column(db.String(36), index=True, primary_key=True)
name = db.Column(db.String(128), nullable=False)
tests = db.relationship('Test', backref='dataset')
creator_id = db.Column(db.String(36), db.ForeignKey('user.id'))

View File

@ -11,16 +11,15 @@ from datetime import datetime, timedelta
from uuid import uuid4
class Entry(db.Model):
id = db.Column(db.String(36), primary_key=True)
id = db.Column(db.String(36), index=True, primary_key=True)
first_name = db.Column(db.String(128), nullable=False)
surname = db.Column(db.String(128), nullable=False)
email = db.Column(db.String(128), nullable=False)
club = db.Column(db.String(128), nullable=True)
test_id = db.Column(db.String(36), db.ForeignKey('test.id'))
user_code = db.Column(db.String(6), nullable=True)
start_time = db.Column(db.DateTime, nullable=True)
end_time = db.Column(db.DateTime, nullable=True)
start_time = db.Column(db.DateTime, index=True, nullable=True)
end_time = db.Column(db.DateTime, index=True, nullable=True)
status = db.Column(db.String(16), nullable=True)
valid = db.Column(db.Boolean, default=True, nullable=True)
answers = db.Column(MutableJson, nullable=True)

View File

@ -9,10 +9,9 @@ import secrets
from uuid import uuid4
class Test(db.Model):
id = db.Column(db.String(36), primary_key=True)
code = db.Column(db.String(36), nullable=False)
start_date = db.Column(db.DateTime, nullable=True)
id = db.Column(db.String(36), index=True, primary_key=True)
code = db.Column(db.String(36), index=True, nullable=False)
start_date = db.Column(db.DateTime, index=True, nullable=True)
end_date = db.Column(db.DateTime, nullable=True)
time_limit = db.Column(db.Integer, nullable=True)
creator_id = db.Column(db.String(36), db.ForeignKey('user.id'))

View File

@ -11,11 +11,11 @@ from werkzeug.security import check_password_hash, generate_password_hash
import secrets
from uuid import uuid4
class User(UserMixin, db.Model):
id = db.Column(db.String(36), primary_key=True)
id = db.Column(db.String(36), index=True, primary_key=True)
username = db.Column(db.String(128), nullable=False)
password = db.Column(db.String(128), nullable=False)
email = db.Column(db.String(128), nullable=False)
reset_token = db.Column(db.String(20), nullable=True)
reset_token = db.Column(db.String(20), index=True, nullable=True)
verification_token = db.Column(db.String(20), nullable=True)
tests = db.relationship('Test', backref='creator')
datasets = db.relationship('Dataset', backref='creator')

View File

@ -17,7 +17,7 @@
/>
{% block style %}
{% endblock %}
<title>{% block title %} SKA Referee Test Beta {% endblock %}</title>
<title>{% block title %} SKA Referee Test {% endblock %}</title>
{% include "quiz/components/og-meta.html" %}
</head>
<body class="bg-light">

View File

@ -1,6 +1,6 @@
<nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark" id="primary-nav">
<div class="container">
<p class="navbar-brand mb-0 h1">SKA Refereeing Test (Beta)</p>
<p class="navbar-brand mb-0 h1">SKA Refereeing Test</p>
<div class="quiz-console w-100" style="display: none;" id="q-topbar">
<div class="d-flex justify-content align-middle">
<div class="container d-flex justify-content-center">

View File

@ -18,7 +18,7 @@
<link rel="shortcut icon" href="{{ url_for('views.static', filename='favicon.ico') }}">
{% block style %}
{% endblock %}
<title>{% block title %} SKA Referee Test Beta {% endblock %}</title>
<title>{% block title %} SKA Referee Test {% endblock %}</title>
</head>
<body class="bg-light">

View File

@ -17,7 +17,7 @@
/>
{% block style %}
{% endblock %}
<title>{% block title %} SKA Referee Test Beta {% endblock %}</title>
<title>{% block title %} SKA Referee Test {% endblock %}</title>
{% include "components/og-meta.html" %}
</head>
<body class="bg-light">

View File

@ -1,6 +1,6 @@
<nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark" id="primary-nav">
<div class="container">
<p class="navbar-brand mb-0 h1">SKA Refereeing Test (Beta)</p>
<p class="navbar-brand mb-0 h1">SKA Refereeing Test</p>
<div class="quiz-console w-100" style="display: none;" id="q-topbar">
<div class="d-flex justify-content align-middle">
<div class="container d-flex justify-content-center">

View File

@ -1,4 +1,3 @@
from .data import load
from ..models import User
from ..tools.logs import write

View File

@ -1,5 +1,3 @@
from ..extensions import db
from ..tools.logs import write
from flask import jsonify

View File

@ -1,6 +1,6 @@
<nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark">
<div class="container">
<a href="{{ url_for('admin._home') }}" class="navbar-brand mb-0 h1">RefTest (Beta) | Admin</a>
<a href="{{ url_for('admin._home') }}" class="navbar-brand mb-0 h1">RefTest | Admin</a>
<button
class="navbar-toggler"
type="button"

View File

@ -27,7 +27,9 @@ with app.app_context():
with open(f'./{data}/.encryption.key', 'wb') as key_file:
key = Fernet.generate_key()
key_file.write(key)
if not database_exists(database_uri):
create_database(database_uri)
write('system.log', 'No database found. Creating a new database.')
db.create_all()
"""Create File for SQLite Database"""
if database_uri[0:6].lower() == 'sqlite':
if not database_exists(database_uri):
create_database(database_uri)
write('system.log', 'No SQLite file found. Creating a new database.')

View File

@ -1,7 +1,7 @@
blinker==1.5
cffi==1.15.1
click==8.1.3
cryptography==37.0.4
cryptography==38.0.1
dnspython==2.2.1
dominate==2.7.0
email-validator==1.2.1
@ -11,19 +11,22 @@ Flask-Login==0.6.2
Flask-Mail==0.9.1
Flask-SQLAlchemy==2.5.1
Flask-WTF==1.0.1
greenlet==1.1.2
greenlet==1.1.3
gunicorn==20.1.0
idna==3.3
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
pip==22.2.2
pycparser==2.21
PyMySQL==1.0.2
python-dotenv==0.20.0
python-dotenv==0.21.0
setuptools==65.3.0
six==1.16.0
SQLAlchemy==1.4.40
SQLAlchemy==1.4.41
sqlalchemy-json==0.5.0
SQLAlchemy-Utils==0.38.3
visitor==0.1.3
Werkzeug==2.2.2
wheel==0.37.1
WTForms==3.0.1