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.) (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 #### Populate Environment Variables
Configuration values for the app are stored in the environment variables file. 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' version: '3.9'
volumes: volumes:
data: app:
services: services:
nginx: nginx:
@ -36,7 +36,7 @@ services:
ports: ports:
- 5000 - 5000
volumes: volumes:
- data:/ref-test/data - app:/ref-test/data
- ./logs:/ref-test/data/logs - ./logs:/ref-test/data/logs
restart: unless-stopped restart: unless-stopped
networks: networks:

View File

@ -1,5 +1,5 @@
from .config import Production as Config from .config import Production as Config
from .models import User from .models import *
from .extensions import bootstrap, csrf, db, login_manager, mail from .extensions import bootstrap, csrf, db, login_manager, mail
from .tools.logs import write from .tools.logs import write
@ -59,4 +59,10 @@ def create_app():
app.register_blueprint(editor, url_prefix='/admin/editor') app.register_blueprint(editor, url_prefix='/admin/editor')
app.register_blueprint(view, url_prefix='/admin/view') 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 return app

View File

@ -1,6 +1,6 @@
<nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark"> <nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark">
<div class="container"> <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 <button
class="navbar-toggler" class="navbar-toggler"
type="button" type="button"

View File

@ -19,7 +19,7 @@
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Dataset</h5> <h5 class="mb-1">Dataset</h5>
</div> </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>
<li class="list-group-item list-group-item-action"> <li class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between"> <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 request.method == 'POST':
if not user: return jsonify({'error': 'User does not exist.'}), 400 if not user: return jsonify({'error': 'User does not exist.'}), 400
if form.validate_on_submit(): 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( success, message = user.update(
password = request.form.get('password'), password = request.form.get('password'),
email = request.form.get('email'), email = request.form.get('email'),

View File

@ -37,7 +37,7 @@ class Config(object):
MYSQL_USER = os.getenv('MYSQL_USER') MYSQL_USER = os.getenv('MYSQL_USER')
MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD') MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD')
SQLALCHEMY_DATABASE_URI = f'mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{DATABASE_HOST}:{DATABASE_PORT}/{MYSQL_DATABASE}' 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): class Production(Config):
pass pass

View File

@ -1,6 +1,6 @@
<nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark"> <nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark">
<div class="container"> <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 <button
class="navbar-toggler" class="navbar-toggler"
type="button" type="button"

View File

@ -14,8 +14,7 @@ from pathlib import Path
from uuid import uuid4 from uuid import uuid4
class Dataset(db.Model): class Dataset(db.Model):
id = db.Column(db.String(36), index=True, primary_key=True)
id = db.Column(db.String(36), primary_key=True)
name = db.Column(db.String(128), nullable=False) name = db.Column(db.String(128), nullable=False)
tests = db.relationship('Test', backref='dataset') tests = db.relationship('Test', backref='dataset')
creator_id = db.Column(db.String(36), db.ForeignKey('user.id')) 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 from uuid import uuid4
class Entry(db.Model): class Entry(db.Model):
id = db.Column(db.String(36), index=True, primary_key=True)
id = db.Column(db.String(36), primary_key=True)
first_name = db.Column(db.String(128), nullable=False) first_name = db.Column(db.String(128), nullable=False)
surname = db.Column(db.String(128), nullable=False) surname = db.Column(db.String(128), nullable=False)
email = db.Column(db.String(128), nullable=False) email = db.Column(db.String(128), nullable=False)
club = db.Column(db.String(128), nullable=True) club = db.Column(db.String(128), nullable=True)
test_id = db.Column(db.String(36), db.ForeignKey('test.id')) test_id = db.Column(db.String(36), db.ForeignKey('test.id'))
user_code = db.Column(db.String(6), nullable=True) user_code = db.Column(db.String(6), nullable=True)
start_time = db.Column(db.DateTime, nullable=True) start_time = db.Column(db.DateTime, index=True, nullable=True)
end_time = db.Column(db.DateTime, nullable=True) end_time = db.Column(db.DateTime, index=True, nullable=True)
status = db.Column(db.String(16), nullable=True) status = db.Column(db.String(16), nullable=True)
valid = db.Column(db.Boolean, default=True, nullable=True) valid = db.Column(db.Boolean, default=True, nullable=True)
answers = db.Column(MutableJson, nullable=True) answers = db.Column(MutableJson, nullable=True)

View File

@ -9,10 +9,9 @@ import secrets
from uuid import uuid4 from uuid import uuid4
class Test(db.Model): class Test(db.Model):
id = db.Column(db.String(36), index=True, primary_key=True)
id = db.Column(db.String(36), primary_key=True) code = db.Column(db.String(36), index=True, nullable=False)
code = db.Column(db.String(36), nullable=False) start_date = db.Column(db.DateTime, index=True, nullable=True)
start_date = db.Column(db.DateTime, nullable=True)
end_date = db.Column(db.DateTime, nullable=True) end_date = db.Column(db.DateTime, nullable=True)
time_limit = db.Column(db.Integer, nullable=True) time_limit = db.Column(db.Integer, nullable=True)
creator_id = db.Column(db.String(36), db.ForeignKey('user.id')) 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 import secrets
from uuid import uuid4 from uuid import uuid4
class User(UserMixin, db.Model): 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) username = db.Column(db.String(128), nullable=False)
password = db.Column(db.String(128), nullable=False) password = db.Column(db.String(128), nullable=False)
email = 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) verification_token = db.Column(db.String(20), nullable=True)
tests = db.relationship('Test', backref='creator') tests = db.relationship('Test', backref='creator')
datasets = db.relationship('Dataset', backref='creator') datasets = db.relationship('Dataset', backref='creator')

View File

@ -17,7 +17,7 @@
/> />
{% block style %} {% block style %}
{% endblock %} {% endblock %}
<title>{% block title %} SKA Referee Test Beta {% endblock %}</title> <title>{% block title %} SKA Referee Test {% endblock %}</title>
{% include "quiz/components/og-meta.html" %} {% include "quiz/components/og-meta.html" %}
</head> </head>
<body class="bg-light"> <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"> <nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark" id="primary-nav">
<div class="container"> <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="quiz-console w-100" style="display: none;" id="q-topbar">
<div class="d-flex justify-content align-middle"> <div class="d-flex justify-content align-middle">
<div class="container d-flex justify-content-center"> <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') }}"> <link rel="shortcut icon" href="{{ url_for('views.static', filename='favicon.ico') }}">
{% block style %} {% block style %}
{% endblock %} {% endblock %}
<title>{% block title %} SKA Referee Test Beta {% endblock %}</title> <title>{% block title %} SKA Referee Test {% endblock %}</title>
</head> </head>
<body class="bg-light"> <body class="bg-light">

View File

@ -17,7 +17,7 @@
/> />
{% block style %} {% block style %}
{% endblock %} {% endblock %}
<title>{% block title %} SKA Referee Test Beta {% endblock %}</title> <title>{% block title %} SKA Referee Test {% endblock %}</title>
{% include "components/og-meta.html" %} {% include "components/og-meta.html" %}
</head> </head>
<body class="bg-light"> <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"> <nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark" id="primary-nav">
<div class="container"> <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="quiz-console w-100" style="display: none;" id="q-topbar">
<div class="d-flex justify-content align-middle"> <div class="d-flex justify-content align-middle">
<div class="container d-flex justify-content-center"> <div class="container d-flex justify-content-center">

View File

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

View File

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

View File

@ -1,6 +1,6 @@
<nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark"> <nav class="navbar fixed-top navbar-expand-md navbar-dark bg-dark">
<div class="container"> <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 <button
class="navbar-toggler" class="navbar-toggler"
type="button" type="button"

View File

@ -27,7 +27,9 @@ with app.app_context():
with open(f'./{data}/.encryption.key', 'wb') as key_file: with open(f'./{data}/.encryption.key', 'wb') as key_file:
key = Fernet.generate_key() key = Fernet.generate_key()
key_file.write(key) key_file.write(key)
if not database_exists(database_uri):
create_database(database_uri) """Create File for SQLite Database"""
write('system.log', 'No database found. Creating a new database.') if database_uri[0:6].lower() == 'sqlite':
db.create_all() 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 blinker==1.5
cffi==1.15.1 cffi==1.15.1
click==8.1.3 click==8.1.3
cryptography==37.0.4 cryptography==38.0.1
dnspython==2.2.1 dnspython==2.2.1
dominate==2.7.0 dominate==2.7.0
email-validator==1.2.1 email-validator==1.2.1
@ -11,19 +11,22 @@ Flask-Login==0.6.2
Flask-Mail==0.9.1 Flask-Mail==0.9.1
Flask-SQLAlchemy==2.5.1 Flask-SQLAlchemy==2.5.1
Flask-WTF==1.0.1 Flask-WTF==1.0.1
greenlet==1.1.2 greenlet==1.1.3
gunicorn==20.1.0 gunicorn==20.1.0
idna==3.3 idna==3.3
itsdangerous==2.1.2 itsdangerous==2.1.2
Jinja2==3.1.2 Jinja2==3.1.2
MarkupSafe==2.1.1 MarkupSafe==2.1.1
pip==22.2.2
pycparser==2.21 pycparser==2.21
PyMySQL==1.0.2 PyMySQL==1.0.2
python-dotenv==0.20.0 python-dotenv==0.21.0
setuptools==65.3.0
six==1.16.0 six==1.16.0
SQLAlchemy==1.4.40 SQLAlchemy==1.4.41
sqlalchemy-json==0.5.0 sqlalchemy-json==0.5.0
SQLAlchemy-Utils==0.38.3 SQLAlchemy-Utils==0.38.3
visitor==0.1.3 visitor==0.1.3
Werkzeug==2.2.2 Werkzeug==2.2.2
wheel==0.37.1
WTForms==3.0.1 WTForms==3.0.1