diff --git a/ref-test/admin/forms.py b/ref-test/admin/forms.py index 9d5efd6..c63b8cb 100644 --- a/ref-test/admin/forms.py +++ b/ref-test/admin/forms.py @@ -1,6 +1,6 @@ from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, SelectField -from wtforms.validators import InputRequired, Email, Length, EqualTo, Optional, ValidationError +from wtforms.validators import InputRequired, Email, Length, EqualTo, Optional from datetime import date, timedelta class LoginForm(FlaskForm): @@ -45,11 +45,12 @@ class UpdateAccountForm(FlaskForm): 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() ) 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) ) + expiry_date = 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 index 19c07a1..ce26428 100644 --- a/ref-test/admin/models.py +++ b/ref-test/admin/models.py @@ -2,22 +2,25 @@ import secrets from datetime import datetime from uuid import uuid4 from flask import flash, jsonify +import secrets from main import db -from security import encrypt, decrypt +from security import encrypt class Test: - def __init__(self, _id=None, expiry=None, time_limit=None, creator=None): + def __init__(self, _id=None, start_date=None, expiry_date=None, time_limit=None, creator=None): self._id = _id - self.expiry = expiry + self.start_date = start_date + self.expiry_date = expiry_date 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, + '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() @@ -27,19 +30,66 @@ class Test: 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): + def add_time_adjustment(self, time_adjustment): code = { '_id': uuid4().hex, - 'user_code': user_code, + 'user_code': secrets.token_hex(2).upper(), '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 + return jsonify({'error': 'Failed to add the time adjustment. An error occurred.'}), 400 + + def remove_time_adjustment(self, _id): + if db.tests.find_one_and_update({'_id': self._id}, {'$pull': {'time_adjustments': {'_id': _id} }}): + 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('—', '') \ No newline at end of file + return test_code.replace('—', '') + + def delete(self): + if db.tests.delete_one({'_id': self._id}): + 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): + 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'] = 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/static/css/style.css b/ref-test/admin/static/css/style.css index 736d9c2..083c8fd 100644 --- a/ref-test/admin/static/css/style.css +++ b/ref-test/admin/static/css/style.css @@ -138,6 +138,19 @@ table.dataTable { .user-row-actions { text-align: center; + white-space: nowrap; +} + +.test-row-actions { + text-align: center; + white-space: nowrap; +} + +.dataTables_wrapper .dt-buttons { + left: 50%; + transform: translateX(-50%); + float:none; + text-align:center; } .user-row-actions button { @@ -197,6 +210,10 @@ table.dataTable { z-index: -1; } +.button-icon { + font-size: 20px; +} + /* Fallback for Edge -------------------------------------------------- */ @supports (-ms-ime-align: auto) { diff --git a/ref-test/admin/static/js/script.js b/ref-test/admin/static/js/script.js index f66be39..ad97048 100644 --- a/ref-test/admin/static/js/script.js +++ b/ref-test/admin/static/js/script.js @@ -350,12 +350,49 @@ $('form[name=form-update-account]').submit(function(event) { event.preventDefault(); }); +$('.delete-test').click(function(event) { + + _id = $(this).data('_id') + + $.ajax({ + url: `/admin/tests/delete/${_id}`, + type: 'GET', + success: function(response) { + window.location.href = '/admin/tests/'; + }, + error: function(response) { + if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { + alert.innerHTML = alert.innerHTML + ` + + `; + } else if (response.responseJSON.error instanceof Array) { + for (var i = 0; i < response.responseJSON.error.length; i ++) { + alert.innerHTML = alert.innerHTML + ` + + `; + } + } + } + }); + + event.preventDefault(); +}); + +// Edit and Delete Test Button Handlers + $('form[name=form-create-test]').submit(function(event) { var $form = $(this); var alert = document.getElementById('alert-box'); var data = $form.serialize(); - console.log(data) alert.innerHTML = '' $.ajax({ @@ -364,10 +401,9 @@ $('form[name=form-create-test]').submit(function(event) { data: data, dataType: 'json', success: function(response) { - window.location.reload(); + window.location.href = '/admin/tests/'; }, error: function(response) { - console.log(response.responseJSON) if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { alert.innerHTML = alert.innerHTML + `