diff --git a/ref-test/admin/auth.py b/ref-test/admin/auth.py deleted file mode 100644 index c614175..0000000 --- a/ref-test/admin/auth.py +++ /dev/null @@ -1,147 +0,0 @@ -from flask import Blueprint, render_template, request, session, redirect -from flask.helpers import flash, url_for -from flask.json import jsonify -from .models.users import User -from uuid import uuid4 -from common.security.database import decrypt_find_one, encrypted_update -from werkzeug.security import check_password_hash - -from .views import admin_account_required, disable_on_registration, login_required, disable_if_logged_in, get_id_from_cookie - -auth = Blueprint( - 'admin_auth', - __name__, - template_folder='templates', - static_folder='static' -) - -@auth.route('/account/', methods = ['GET', 'POST']) -@admin_account_required -@login_required -def account(): - from .models.forms import UpdateAccountForm - from main import db - form = UpdateAccountForm() - _id = get_id_from_cookie() - user = decrypt_find_one(db.users, {'_id': _id}) - if request.method == 'GET': - return render_template('/admin/auth/account.html', form = form, user = user) - if request.method == 'POST': - if form.validate_on_submit(): - password_confirm = request.form.get('password_confirm') - if not check_password_hash(user['password'], password_confirm): - return jsonify({ 'error': 'The password you entered is incorrect.' }), 401 - entry = User( - _id = _id, - password = request.form.get('password'), - email = request.form.get('email') - ) - return entry.update() - else: - errors = [*form.password_confirm.errors, *form.password_reenter.errors, *form.password.errors, *form.email.errors] - return jsonify({ 'error': errors}), 400 - -@auth.route('/login/', methods=['GET','POST']) -@admin_account_required -@disable_if_logged_in -def login(): - from .models.forms import LoginForm - form = LoginForm() - if request.method == 'GET': - return render_template('/admin/auth/login.html', form=form) - if request.method == 'POST': - if form.validate_on_submit(): - entry = User( - username = request.form.get('username').lower(), - password = request.form.get('password'), - remember = request.form.get('remember') - ) - return entry.login() - else: - errors = [*form.username.errors, *form.password.errors] - return jsonify({ 'error': errors}), 400 - -@auth.route('/logout/') -@admin_account_required -@login_required -def logout(): - _id = get_id_from_cookie() - return User(_id=_id).logout() - -@auth.route('/register/', methods=['GET','POST']) -@disable_on_registration -def register(): - from .models.forms import RegistrationForm - form = RegistrationForm() - if request.method == 'GET': - return render_template('/admin/auth/register.html', form=form) - if request.method == 'POST': - if form.validate_on_submit(): - entry = User( - _id = uuid4().hex, - username = request.form.get('username').lower(), - email = request.form.get('email'), - password = request.form.get('password'), - ) - return entry.register() - else: - errors = [*form.username.errors, *form.email.errors, *form.password.errors, *form.password_reenter.errors] - return jsonify({ 'error': errors}), 400 - -@auth.route('/reset/', methods = ['GET', 'POST']) -@admin_account_required -@disable_if_logged_in -def reset(): - from .models.forms import ResetPasswordForm - form = ResetPasswordForm() - if request.method == 'GET': - return render_template('/admin/auth/reset.html', form=form) - if request.method == 'POST': - if form.validate_on_submit(): - entry = User( - username = request.form.get('username').lower(), - email = request.form.get('email'), - ) - return entry.reset_password() - else: - errors = [*form.username.errors, *form.email.errors] - return jsonify({ 'error': errors}), 400 - -@auth.route('/reset///', methods = ['GET']) -@admin_account_required -@disable_if_logged_in -def reset_gateway(token1,token2): - from main import db - user = decrypt_find_one( db.users, {'reset_token' : token1} ) - if not user: - return redirect(url_for('admin_auth.login')) - encrypted_update( db.users, {'reset_token': token1}, {'$unset': {'reset_token' : '', 'verification_token': ''}}) - if not user['verification_token'] == token2: - flash('The verification of your password reset request failed and the token has been invalidated. Please make a new reset password request.', 'error'), 401 - return redirect(url_for('admin_auth.reset')) - session['_id'] = user['_id'] - session['reset_validated'] = True - return redirect(url_for('admin_auth.update_password_')) - -@auth.route('/reset/update/', methods = ['GET','POST']) -@admin_account_required -@disable_if_logged_in -def update_password_(): - from .models.forms import UpdatePasswordForm - form = UpdatePasswordForm() - if request.method == 'GET': - if 'reset_validated' not in session: - return redirect(url_for('admin_auth.login')) - session.pop('reset_validated') - return render_template('/admin/auth/update-password.html', form=form) - if request.method == 'POST': - if form.validate_on_submit(): - entry = User( - _id = session['_id'], - password = request.form.get('password') - ) - session.pop('_id') - return entry.update() - else: - errors = [*form.password.errors, *form.password_reenter.errors] - return jsonify({ 'error': errors}), 400 \ No newline at end of file diff --git a/ref-test/admin/models/entries.py b/ref-test/admin/models/entries.py deleted file mode 100644 index 71ca3c5..0000000 --- a/ref-test/admin/models/entries.py +++ /dev/null @@ -1,51 +0,0 @@ -from database import db -from common.security import encrypt, decrypt -from . import JsonEncodedDict - -class Entry(db.Model): - - id = db.Column(db.String(36), 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')) - test_code = db.Column(db.String(36), db.ForeignKey('test.test_code')) - user_code = db.Column(db.String(6), nullable=True) - start_time = db.Column(db.DateTime, nullable=False) - end_time = db.Column(db.DateTime, nullable=True) - status = db.Column(db.String(16), nullable=True) - late_ignore = db.Column(db.Boolean, default=False, nullable=True) - answers = db.Column(JsonEncodedDict, nullable=True) - - @property - def set_first_name(self): raise AttributeError('set_first_name is not a readable attribute.') - - set_first_name.setter - def set_first_name(self, name:str): self.first_name = encrypt(name) - - def get_first_name(self): return decrypt(self.first_name) - - @property - def set_surname(self): raise AttributeError('set_first_name is not a readable attribute.') - - set_surname.setter - def set_surname(self, name:str): self.surname = encrypt(name) - - def get_surname(self): return decrypt(self.surname) - - @property - def set_email(self): raise AttributeError('set_email is not a readable attribute.') - - set_name.setter - def set_email(self, email:str): self.email = encrypt(email) - - def get_email(self): return decrypt(self.email) - - @property - def set_club(self): raise AttributeError('set_club is not a readable attribute.') - - set_name.setter - def set_club(self, club:str): self.club = encrypt(club) - - def get_club(self): return decrypt(self.club) \ No newline at end of file diff --git a/ref-test/admin/models/forms/__init__.py b/ref-test/admin/models/forms/__init__.py deleted file mode 100644 index 5bf60a4..0000000 --- a/ref-test/admin/models/forms/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -from flask_wtf import FlaskForm -from flask_wtf.file import FileField, FileRequired, FileAllowed -from wtforms import StringField, PasswordField, BooleanField, DateField, SelectField, IntegerField -from wtforms.validators import InputRequired, Email, Length, EqualTo, Optional -from datetime import date, timedelta - -from .validators import value - -class LoginForm(FlaskForm): - username = StringField('Username', validators=[InputRequired(), Length(min=4, max=15)]) - password = PasswordField('Password', validators=[InputRequired(), Length(min=6, max=30, message='The password must be between 6 and 20 characters long.')]) - remember = BooleanField('Remember Log In', render_kw={'checked': True}) - -class RegistrationForm(FlaskForm): - username = StringField('Username', validators=[InputRequired(), Length(min=4, max=15)]) - email = StringField('Email Address', validators=[InputRequired(), Email(message='You must enter a valid email address.'), Length(max=50)]) - password = PasswordField('Password', validators=[InputRequired(), Length(min=6, max=30, message='The password must be between 6 and 20 characters long.')]) - password_reenter = PasswordField('Re-Enter Password', validators=[InputRequired(), Length(min=6, max=30, message='The password must be between 6 and 20 characters long.'), EqualTo('password', message='Passwords do not match.')]) - -class ResetPasswordForm(FlaskForm): - username = StringField('Username', validators=[InputRequired(), Length(min=4, max=15)]) - email = StringField('Email Address', validators=[InputRequired(), Email(message='You must enter a valid email address.'), Length(max=50)]) - -class UpdatePasswordForm(FlaskForm): - password = PasswordField('Password', validators=[InputRequired(), Length(min=6, max=30, message='The password must be between 6 and 20 characters long.')]) - password_reenter = PasswordField('Re-Enter Password', validators=[InputRequired(), Length(min=6, max=30, message='The password must be between 6 and 20 characters long.'), EqualTo('password', message='Passwords do not match.')]) - -class CreateUserForm(FlaskForm): - username = StringField('Username', validators=[InputRequired(), Length(min=4, max=15)]) - email = StringField('Email Address', validators=[InputRequired(), Email(message='You must enter a valid email address.'), Length(max=50)]) - password = PasswordField('Password (Optional)', validators=[Optional(),Length(min=6, max=30, message='The password must be between 6 and 20 characters long.')]) - -class DeleteUserForm(FlaskForm): - password = PasswordField('Confirm Your Password', validators=[InputRequired(), Length(min=6, max=30, message='The password must be between 6 and 20 characters long.')]) - notify = BooleanField('Notify deletion by email', render_kw={'checked': True}) - -class UpdateUserForm(FlaskForm): - user_password = PasswordField('Confirm Your Password', validators=[InputRequired(), Length(min=6, max=30, message='The password must be between 6 and 20 characters long.')]) - email = StringField('Email Address', validators=[Optional(), Email(message='You must enter a valid email address.'), Length(max=50)]) - password = PasswordField('Change Password', validators=[Optional(),Length(min=6, max=30, message='The password must be between 6 and 20 characters long.')]) - password_reenter = PasswordField('Re-Enter New Password', validators=[EqualTo('password', message='Passwords do not match.')]) - notify = BooleanField('Notify changes by email', render_kw={'checked': True}) - -class UpdateAccountForm(FlaskForm): - password_confirm = PasswordField('Current Password', validators=[InputRequired(), Length(min=6, max=30, message='The password must be between 6 and 20 characters long.')]) - email = StringField('Email Address', validators=[Optional(), Email(message='You must enter a valid email address.'), Length(max=50)]) - password = PasswordField('Change Password', validators=[Optional(),Length(min=6, max=30, message='The password must be between 6 and 20 characters long.')]) - password_reenter = PasswordField('Re-Enter New Password', validators=[EqualTo('password', message='Passwords do not match.')]) - -class CreateTest(FlaskForm): - start_date = DateField('Start Date', format="%Y-%m-%d", validators=[InputRequired()], default = date.today() ) - expiry_date = DateField('Expiry Date', format="%Y-%m-%d", validators=[InputRequired()], default = date.today() + timedelta(days=1) ) - time_limit = SelectField('Time Limit') - dataset = SelectField('Question Dataset') - -class UploadDataForm(FlaskForm): - data_file = FileField('Data File', validators=[FileRequired(), FileAllowed(['json'])]) - default = BooleanField('Make Default', render_kw={'checked': True}) - -class AddTimeAdjustment(FlaskForm): - time = IntegerField('Extra Time (Minutes)', validators=[InputRequired(), value(max=60)]) - \ No newline at end of file diff --git a/ref-test/admin/models/forms/validators.py b/ref-test/admin/models/forms/validators.py deleted file mode 100644 index 7caeb05..0000000 --- a/ref-test/admin/models/forms/validators.py +++ /dev/null @@ -1,11 +0,0 @@ -from wtforms.validators import ValidationError - -def value(min=0, max=None): - message = f'Value must be between {min} and {max}.' - - def _length(form, field): - value = field.data or 0 - if value < min or max != None and value > max: - raise ValidationError(message) - - return _length \ No newline at end of file diff --git a/ref-test/admin/models/tests.py b/ref-test/admin/models/tests.py deleted file mode 100644 index 123bc14..0000000 --- a/ref-test/admin/models/tests.py +++ /dev/null @@ -1,122 +0,0 @@ -import secrets -from datetime import datetime -from uuid import uuid4 -from flask import flash, jsonify -import secrets -import os -from json import dump, loads - -from common.security import encrypt - -class Test: - - def __init__(self, _id=None, start_date=None, expiry_date=None, time_limit=None, creator=None, dataset=None): - self._id = _id - self.start_date = start_date - self.expiry_date = expiry_date - self.time_limit = None if time_limit == 'none' or time_limit == '' or time_limit == None else int(time_limit) - self.creator = creator - self.dataset = dataset - - def create(self): - from main import app, db - test = { - '_id': self._id, - 'date_created': datetime.today(), - 'start_date': self.start_date, - 'expiry_date': self.expiry_date, - 'time_limit': self.time_limit, - 'creator': encrypt(self.creator), - 'test_code': secrets.token_hex(6).upper(), - 'dataset': self.dataset - } - if db.tests.insert_one(test): - dataset_file_path = os.path.join(app.config["DATA_FILE_DIRECTORY"],self.dataset) - with open(dataset_file_path, 'r') as dataset_file: - data = loads(dataset_file.read()) - data['meta']['tests'].append(self._id) - with open(dataset_file_path, 'w') as dataset_file: - dump(data, dataset_file, indent=2) - flash(f'Created a new exam with Exam Code {self.render_test_code(test["test_code"])}.', 'success') - return jsonify({'success': test}), 200 - return jsonify({'error': f'Could not create exam. An error occurred.'}), 400 - - def add_time_adjustment(self, time_adjustment): - from main import db - user_code = secrets.token_hex(3).upper() - adjustment = { - user_code: time_adjustment - } - if db.tests.find_one_and_update({'_id': self._id}, {'$set': {'time_adjustments': adjustment}},upsert=False): - flash(f'Time adjustment for {time_adjustment} minutes has been added. This can be enabled using the user code {user_code}.') - return jsonify({'success': adjustment}) - return jsonify({'error': 'Failed to add the time adjustment. An error occurred.'}), 400 - - def remove_time_adjustment(self, user_code): - from main import db - if db.tests.find_one_and_update({'_id': self._id}, {'$unset': {f'time_adjustments.{user_code}': {}}}): - message = 'Time adjustment has been deleted.' - flash(message, 'success') - return jsonify({'success': message}) - return jsonify({'error': 'Failed to delete the time adjustment. An error occurred.'}), 400 - - def render_test_code(self, test_code): - return '—'.join([test_code[:4], test_code[4:8], test_code[8:]]) - - def parse_test_code(self, test_code): - return test_code.replace('—', '') - - def delete(self): - from main import app, db - test = db.tests.find_one({'_id': self._id}) - if 'entries' in test: - if test['entries']: - return jsonify({'error': 'Cannot delete an exam that has entries submitted to it.'}), 400 - if self.dataset is None: - self.dataset = db.tests.find_one({'_id': self._id})['dataset'] - if db.tests.delete_one({'_id': self._id}): - dataset_file_path = os.path.join(app.config["DATA_FILE_DIRECTORY"],self.dataset) - with open(dataset_file_path, 'r') as dataset_file: - data = loads(dataset_file.read()) - data['meta']['tests'].remove(self._id) - with open(dataset_file_path, 'w') as dataset_file: - dump(data, dataset_file, indent=2) - message = 'Deleted exam.' - flash(message, 'alert') - return jsonify({'success': message}), 200 - return jsonify({'error': f'Could not create exam. An error occurred.'}), 400 - - def update(self): - from main import db - test = {} - updated = [] - if not self.start_date == '' and self.start_date is not None: - test['start_date'] = self.start_date - updated.append('start date') - if not self.expiry_date == '' and self.expiry_date is not None: - test['expiry_date'] = self.expiry_date - updated.append('expiry date') - if not self.time_limit == '' and self.time_limit is not None: - test['time_limit'] = int(self.time_limit) - updated.append('time limit') - output = '' - if len(updated) == 0: - flash(f'There were no changes requested for your account.', 'alert'), 200 - return jsonify({'success': 'There were no changes requested for your account.'}), 200 - elif len(updated) == 1: - output = updated[0] - elif len(updated) == 2: - output = ' and '.join(updated) - elif len(updated) > 2: - output = updated[0] - for index in range(1,len(updated)): - if index < len(updated) - 2: - output = ', '.join([output, updated[index]]) - elif index == len(updated) - 2: - output = ', and '.join([output, updated[index]]) - else: - output = ''.join([output, updated[index]]) - db.tests.find_one_and_update({'_id': self._id}, {'$set': test}) - _output = f'The {output} of the test {"has" if len(updated) == 1 else "have"} been updated.' - flash(_output) - return jsonify({'success': _output}), 200 \ No newline at end of file diff --git a/ref-test/admin/models/users.py b/ref-test/admin/models/users.py deleted file mode 100644 index 71c9da1..0000000 --- a/ref-test/admin/models/users.py +++ /dev/null @@ -1,207 +0,0 @@ -from flask import flash, make_response, Response, session -from flask.helpers import url_for -from flask.json import jsonify -from werkzeug.security import generate_password_hash, check_password_hash -from werkzeug.utils import redirect -from flask_mail import Message -import secrets - -from common.security import encrypt, decrypt -from common.security.database import decrypt_find_one, encrypted_update -from datetime import datetime, timedelta - -class User: - - def __init__(self, _id=None, username=None, password=None, email=None, remember=False): - self._id = _id - self.username = username - self.email = email - self.password = password - self.remember = remember - - def start_session(self, resp:Response): - from main import app - resp.set_cookie( - key = '_id', - value = self._id, - max_age = timedelta(days=14) if self.remember else None, - path = '/', - expires = datetime.utcnow() + timedelta(days=14) if self.remember else None, - domain = f'.{app.config["SERVER_NAME"]}', - secure = True - ) - if self.remember: - resp.set_cookie ( - key = 'remember', - value = 'True', - max_age = timedelta(days=14), - path = '/', - expires = datetime.utcnow() + timedelta(days=14), - domain = f'.{app.config["SERVER_NAME"]}', - secure = True - ) - - def register(self): - from main import db - from ..views import get_id_from_cookie - user = { - '_id': self._id, - 'email': encrypt(self.email), - 'password': generate_password_hash(self.password, method='sha256'), - 'username': encrypt(self.username) - } - if decrypt_find_one(db.users, { 'username': self.username }): - return jsonify({ 'error': f'Username {self.username} is not available.' }), 400 - if db.users.insert_one(user): - flash(f'User {self.username} has been created successfully. You may now use it to log in and administer the tests.', 'success') - resp = make_response(jsonify(user), 200) - if not get_id_from_cookie: - self.start_session(resp) - return resp - return jsonify({ 'error': f'Registration failed. An error occurred.' }), 400 - - def login(self): - from main import db - user = decrypt_find_one( db.users, { 'username': self.username }) - if not user: - return jsonify({ 'error': f'Username {self.username} does not exist.' }), 401 - if not check_password_hash( user['password'], self.password ): - return jsonify({ 'error': f'The password you entered is incorrect.' }), 401 - response = { - 'success': f'Successfully logged in user {self.username}.' - } - if 'prev_page' in session: - response['redirect_to'] = session['prev_page'] - session.pop('prev_page') - resp = make_response(jsonify(response), 200) - self._id = user['_id'] - self.start_session(resp) - return resp - - def logout(self): - resp = make_response(redirect(url_for('admin_auth.login'))) - from main import app - resp.set_cookie( - key = '_id', - value = '', - max_age = timedelta(days=-1), - path = '/', - expires= datetime.utcnow() + timedelta(days=-1), - domain = f'.{app.config["SERVER_NAME"]}', - secure = True - ) - resp.set_cookie ( - key = 'cookie_consent', - value = 'True', - max_age = None, - path = '/', - expires = None, - domain = f'.{app.config["SERVER_NAME"]}', - secure = True - ) - resp.set_cookie ( - key = 'remember', - value = 'True', - max_age = timedelta(days=-1), - path = '/', - expires = datetime.utcnow() + timedelta(days=-1), - domain = f'.{app.config["SERVER_NAME"]}', - secure = True - ) - flash('You have been logged out. All cookies pertaining to your account have been deleted. Have a nice day.', 'alert') - return resp - - def reset_password(self): - from main import db, mail - user = decrypt_find_one(db.users, { 'username': self.username }) - if not user: - return jsonify({ 'error': f'Username {self.username} does not exist.' }), 401 - if not user['email'] == self.email: - return jsonify({ 'error': f'The email address {self.email} does not match the user account {self.username}.' }), 401 - new_password = secrets.token_hex(12) - reset_token = secrets.token_urlsafe(16) - verification_token = secrets.token_urlsafe(16) - user['password'] = generate_password_hash(new_password, method='sha256') - if encrypted_update(db.users, { 'username': self.username }, { '$set': {'password': user['password'], 'reset_token': reset_token, 'verification_token': verification_token } } ): - flash(f'Your password has been reset. Instructions to recover your account have been sent to {self.email}. Please be sure to check your spam folder in case you have not received the email.', 'alert') - email = Message( - subject = 'RefTest | Password Reset', - recipients = [self.email], - body = f""" - Hello {user['username']}, \n\n - This email was generated because we received a request to reset the password for your administrator account for the SKA RefTest app.\n\n - If you did not make this request, please ignore this email.\n\n - If you did make this request, then you have two options to recover your account.\n\n - For the time being, your password has been reset to the following:\n\n - {new_password}\n\n - You may use this to log back in to your account, and subsequently change your password to something more suitable.\n\n - Alternatively, you may visit the following private link using your unique token to override your password. Copy and paste the following link in a web browser. Please note that this token is only valid once:\n\n - {url_for('admin_auth.reset_gateway', token1 = reset_token, token2 = verification_token, _external = True)}\n\n - Have a nice day. - """, - html = f""" -

Hello {user['username']},

-

This email was generated because we received a request to reset the password for your administrator account for the SKA RefTest app.

-

If you did not make this request, please ignore this email.

-

If you did make this request, then you have two options to recover your account.

-

For the time being, your password has been reset to the following:

- {new_password} -

You may use this to log back in to your account, and subsequently change your password to something more suitable.

-

Alternatively, you may visit the following private link using your unique token to override your password. Click on the following link, or copy and paste it into a browser. Please note that this token is only valid once:

-

{url_for('admin_auth.reset_gateway', token1 = reset_token, token2 = verification_token, _external = True)}

-

Have a nice day.

- """ - ) - mail.send(email) - return jsonify({ 'success': 'Password reset request has been processed.'}), 200 - - def update(self): - from main import db - from ..views import get_id_from_cookie - retrieved_user = decrypt_find_one(db.users, { '_id': self._id }) - if not retrieved_user: - return jsonify({ 'error': f'User {retrieved_user["username"]} does not exist.' }), 401 - user = {} - updated = [] - if not self.email == '' and self.email is not None: - user['email'] = self.email - updated.append('email') - if not self.password == '' and self.password is not None: - user['password'] = generate_password_hash(self.password, method='sha256') - updated.append('password') - output = '' - if len(updated) == 0: - flash(f'There were no changes requested for your account.', 'alert'), 200 - return jsonify({'success': 'There were no changes requested for your account.'}), 200 - elif len(updated) == 1: - output = updated[0] - elif len(updated) == 2: - output = ' and '.join(updated) - elif len(updated) > 2: - output = updated[0] - for index in range(1,len(updated)): - if index < len(updated) - 2: - output = ', '.join([output, updated[index]]) - elif index == len(updated) - 2: - output = ', and '.join([output, updated[index]]) - else: - output = ''.join([output, updated[index]]) - encrypted_update(db.users, {'_id': self._id}, { '$set': user }) - if self._id == get_id_from_cookie(): - _output = 'Your ' - elif retrieved_user['username'][-1] == 's': - _output = '’'.join([retrieved_user['username'], '']) - else: - _output = '’'.join([retrieved_user['username'], 's']) - _output = f'{_output} {output} {"has" if len(updated) == 1 else "have"} been updated.' - flash(_output) - return jsonify({'success': _output}), 200 - - def delete(self): - from main import db - retrieved_user = decrypt_find_one(db.users, { '_id': self._id }) - if not retrieved_user: - return jsonify({ 'error': f'User does not exist.' }), 401 - db.users.find_one_and_delete({'_id': self._id}) - flash(f'User {retrieved_user["username"]} has been deleted.') - return jsonify({'success': f'User {retrieved_user["username"]} has been deleted.'}), 200 diff --git a/ref-test/admin/results.py b/ref-test/admin/results.py deleted file mode 100644 index aaf2274..0000000 --- a/ref-test/admin/results.py +++ /dev/null @@ -1,15 +0,0 @@ -from flask import Blueprint, render_template -from .views import login_required, admin_account_required - -results = Blueprint( - 'results', - __name__, - template_folder='templates', - static_folder='static' -) - -@results.route('/') -@admin_account_required -@login_required -def _results(): - return render_template('/admin/results.html') \ No newline at end of file diff --git a/ref-test/admin/static/css/style.css b/ref-test/admin/static/css/style.css deleted file mode 100644 index 7a22f31..0000000 --- a/ref-test/admin/static/css/style.css +++ /dev/null @@ -1,260 +0,0 @@ -body { - padding: 80px 0; -} - -.site-footer { - background-color: lightgray; - font-size: small; -} - -.site-footer p { - margin: 0; -} - -.form-container { - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; - padding-top: 40px; - padding-bottom: 40px; -} - -.form-display { - width: 100%; - max-width: 420px; - padding: 15px; - margin: auto; -} - -.form-heading { - margin-bottom: 2rem; -} - -.form-label-group { - position: relative; - margin-bottom: 2rem; -} - -.form-label-group input, -.form-label-group label { - padding: var(--input-padding-y) var(--input-padding-x); - font-size: 16pt; -} - -.form-label-group label { - position: absolute; - top: 0; - left: 0; - display: block; - width: 100%; - margin-bottom: 0; /* Override default `