From 610b6a576661244863920f70271c4e671b925796 Mon Sep 17 00:00:00 2001 From: viveksantayana Date: Wed, 24 Nov 2021 17:17:56 +0000 Subject: [PATCH] Building new test form Added CRUD for tests --- .gitignore | 5 +- ref-test/admin/forms.py | 17 +- ref-test/admin/models.py | 45 +++++ ref-test/admin/static/css/style.css | 50 ++++- ref-test/admin/static/js/script.js | 43 ++++ .../admin/templates/admin/auth/login.html | 2 +- .../templates/admin/components/base.html | 3 - .../templates/admin/components/datatable.html | 27 +++ .../admin/components/server-alerts.html | 8 +- .../admin/templates/admin/settings/users.html | 27 +-- ref-test/admin/templates/admin/tests.html | 139 ++++++++++++- ref-test/admin/views.py | 50 +++-- ref-test/common/__init__.py | 18 ++ ref-test/main.py | 4 +- ref-test/quiz/auth.py | 2 - ref-test/quiz/forms.py | 10 +- ref-test/quiz/static/css/style.css | 190 ++++++++++++++++++ ref-test/quiz/static/js/script.js | 13 ++ ref-test/quiz/templates/quiz/base.html | 43 ---- .../quiz/templates/quiz/components/base.html | 59 ++++++ .../templates/quiz/components/footer.html | 0 .../templates/quiz/components/navbar.html | 5 + .../quiz/components/server-alerts.html | 0 ref-test/quiz/templates/quiz/index.html | 24 +++ ref-test/quiz/templates/quiz/privacy.html | 0 ref-test/quiz/templates/quiz/start-quiz.html | 43 ++++ ref-test/quiz/views.py | 11 +- ref-test/security/database.py | 2 +- 28 files changed, 727 insertions(+), 113 deletions(-) create mode 100644 ref-test/admin/models.py create mode 100644 ref-test/admin/templates/admin/components/datatable.html create mode 100644 ref-test/common/__init__.py delete mode 100644 ref-test/quiz/templates/quiz/base.html create mode 100644 ref-test/quiz/templates/quiz/components/base.html create mode 100644 ref-test/quiz/templates/quiz/components/footer.html create mode 100644 ref-test/quiz/templates/quiz/components/navbar.html create mode 100644 ref-test/quiz/templates/quiz/components/server-alerts.html create mode 100644 ref-test/quiz/templates/quiz/index.html create mode 100644 ref-test/quiz/templates/quiz/privacy.html create mode 100644 ref-test/quiz/templates/quiz/start-quiz.html diff --git a/.gitignore b/.gitignore index 5865410..ae5f512 100644 --- a/.gitignore +++ b/.gitignore @@ -146,4 +146,7 @@ out/ ref-test/testing.py # Ignore Encryption Keyfile -.encryption.key \ No newline at end of file +.encryption.key + +# Ignore Font Binaries +**/fonts/ \ No newline at end of file diff --git a/ref-test/admin/forms.py b/ref-test/admin/forms.py index 89e8d5c..9d5efd6 100644 --- a/ref-test/admin/forms.py +++ b/ref-test/admin/forms.py @@ -1,6 +1,7 @@ from flask_wtf import FlaskForm -from wtforms import StringField, PasswordField, BooleanField -from wtforms.validators import InputRequired, Email, Length, EqualTo, Optional +from wtforms import StringField, PasswordField, BooleanField, DateField, SelectField +from wtforms.validators import InputRequired, Email, Length, EqualTo, Optional, ValidationError +from datetime import date, timedelta class LoginForm(FlaskForm): username = StringField('Username', validators=[InputRequired(), Length(min=4, max=15)]) @@ -41,4 +42,14 @@ 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.')]) \ No newline at end of file + password_reenter = PasswordField('Re-Enter New Password', validators=[EqualTo('password', message='Passwords do not match.')]) + +class CreateTest(FlaskForm): + time_options = [ + ('none', 'None'), + ('60', '1 hour'), + ('90', '1 hour 30 minutes'), + ('120', '2 hours') + ] + expiry = DateField('Expiry Date', format="%Y-%m-%d", validators=[InputRequired()], default = date.today() + timedelta(days=1) ) + time_limit = SelectField('Time Limit', choices=time_options) \ No newline at end of file diff --git a/ref-test/admin/models.py b/ref-test/admin/models.py new file mode 100644 index 0000000..19c07a1 --- /dev/null +++ b/ref-test/admin/models.py @@ -0,0 +1,45 @@ +import secrets +from datetime import datetime +from uuid import uuid4 +from flask import flash, jsonify + +from main import db +from security import encrypt, decrypt + +class Test: + def __init__(self, _id=None, expiry=None, time_limit=None, creator=None): + self._id = _id + self.expiry = expiry + self.time_limit = None if time_limit == 'none' or time_limit == '' else time_limit + self.creator = creator + + def create(self): + test = { + '_id': self._id, + 'date': datetime.today(), + 'expiry': self.expiry, + 'time_limit': self.time_limit, + 'creator': encrypt(self.creator), + 'test_code': secrets.token_hex(6).upper() + } + if db.tests.insert_one(test): + 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_user_code(self, user_code, time_adjustment): + code = { + '_id': uuid4().hex, + 'user_code': user_code, + 'time_adjustment': time_adjustment + } + if db.tests.find_one_and_update({'_id': self._id}, {'$push': {'time_adjustments': code}},upsert=False): + return jsonify({'success': code}) + else: + return jsonify({'error': '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('—', '') \ No newline at end of file diff --git a/ref-test/admin/static/css/style.css b/ref-test/admin/static/css/style.css index c409e0c..736d9c2 100644 --- a/ref-test/admin/static/css/style.css +++ b/ref-test/admin/static/css/style.css @@ -27,7 +27,7 @@ body { margin: auto; } -.form-signin-heading { +.form-heading { margin-bottom: 2rem; } @@ -65,6 +65,10 @@ body { border-bottom: 2px solid #585858; } +.form-label-group input:active, .form-label-group input:focus { + background-color: transparent; +} + .form-label-group input::-webkit-input-placeholder { color: transparent; } @@ -149,6 +153,50 @@ table.dataTable { width: 100%; } +.alert-db-empty { + width: 100%; + max-width: 720px; + font-size: 14pt; + margin: 20px auto; +} + +.form-date-input, .form-select-input { + position: relative; + margin: 2rem 0; +} + +.form-date-input input, +.form-date-input label, .form-select-input select, .form-select-input label { + padding: var(--input-padding-y) var(--input-padding-x); + font-size: 16pt; + width: 100%; + background-color: transparent; + border: none; + border-bottom: 2px solid #585858; +} + +.datepicker::-webkit-calendar-picker-indicator { + border: 1px; + border-color: gray; + border-radius: 10%; +} + +.form-date-input label, .form-select-input label { + /* position: absolute; */ + /* top: 0; + left: 0; */ + display: block; + width: 100%; + margin-bottom: 0; /* Override default `