From fdc68079dca93a953e155b1e3bdb4cc805eac90c Mon Sep 17 00:00:00 2001 From: viveksantayana Date: Sun, 28 Nov 2021 17:28:14 +0000 Subject: [PATCH] Added functionality for default datasets. Incorporated dataset selector into test creation. --- ref-test/admin/models/forms.py | 4 +- ref-test/admin/models/tests.py | 24 +++- ref-test/admin/static/css/style.css | 18 +-- ref-test/admin/static/js/script.js | 131 +++++++++++++----- .../templates/admin/components/navbar.html | 2 +- .../templates/admin/settings/questions.html | 112 ++++++++++++++- .../admin/templates/admin/settings/users.html | 4 +- ref-test/admin/templates/admin/tests.html | 8 +- ref-test/admin/views.py | 94 +++++++++++-- ref-test/common/data_tools.py | 32 +++-- ref-test/quiz/views.py | 6 +- 11 files changed, 350 insertions(+), 85 deletions(-) diff --git a/ref-test/admin/models/forms.py b/ref-test/admin/models/forms.py index 9e762f9..ee05b74 100644 --- a/ref-test/admin/models/forms.py +++ b/ref-test/admin/models/forms.py @@ -55,6 +55,8 @@ class CreateTest(FlaskForm): ] 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) + dataset = SelectField('Question Dataset') class UploadDataForm(FlaskForm): - data_file = FileField('Data File', validators=[FileRequired(), FileAllowed(['json'])]) \ No newline at end of file + data_file = FileField('Data File', validators=[FileRequired(), FileAllowed(['json'])]) + default = BooleanField('Make Default', render_kw={'checked': True}) \ No newline at end of file diff --git a/ref-test/admin/models/tests.py b/ref-test/admin/models/tests.py index 45196a7..a0e44cd 100644 --- a/ref-test/admin/models/tests.py +++ b/ref-test/admin/models/tests.py @@ -3,17 +3,20 @@ from datetime import datetime from uuid import uuid4 from flask import flash, jsonify import secrets +import os +from json import dump, loads -from main import db +from main import app, db from common.security import encrypt class Test: - def __init__(self, _id=None, start_date=None, expiry_date=None, time_limit=None, creator=None): + 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 == '' else time_limit self.creator = creator + self.dataset = dataset def create(self): test = { @@ -23,9 +26,16 @@ class Test: 'expiry_date': self.expiry_date, 'time_limit': self.time_limit, 'creator': encrypt(self.creator), - 'test_code': secrets.token_hex(6).upper() + '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 @@ -54,7 +64,15 @@ class Test: return test_code.replace('—', '') def delete(self): + 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 diff --git a/ref-test/admin/static/css/style.css b/ref-test/admin/static/css/style.css index 083c8fd..d719dc2 100644 --- a/ref-test/admin/static/css/style.css +++ b/ref-test/admin/static/css/style.css @@ -132,16 +132,11 @@ table.dataTable { width: 100%; } -.user-table-row { +.table-row { vertical-align: middle; } -.user-row-actions { - text-align: center; - white-space: nowrap; -} - -.test-row-actions { +.row-actions { text-align: center; white-space: nowrap; } @@ -153,8 +148,8 @@ table.dataTable { text-align:center; } -.user-row-actions button { - margin: 0px 10px; +.row-actions button, .row-actions a { + margin: 0px 5px; } #cookie-alert { @@ -214,6 +209,11 @@ table.dataTable { font-size: 20px; } +.form-upload { + margin: 2rem 0; + font-size: 14pt; +} + /* 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 e6e4d73..ad4cd34 100644 --- a/ref-test/admin/static/js/script.js +++ b/ref-test/admin/static/js/script.js @@ -20,7 +20,7 @@ $('form[name=form-register]').submit(function(event) { var alert = document.getElementById('alert-box'); var data = $form.serialize(); - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -62,7 +62,7 @@ $('form[name=form-login]').submit(function(event) { var alert = document.getElementById('alert-box'); var data = $form.serialize(); - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -104,7 +104,7 @@ $('form[name=form-reset]').submit(function(event) { var alert = document.getElementById('alert-box'); var data = $form.serialize(); - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -146,7 +146,7 @@ $('form[name=form-update-password]').submit(function(event) { var alert = document.getElementById('alert-box'); var data = $form.serialize(); console.log(data) - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -188,7 +188,7 @@ $('form[name=form-create-user]').submit(function(event) { var alert = document.getElementById('alert-box'); var data = $form.serialize(); - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -230,7 +230,7 @@ $('form[name=form-delete-user]').submit(function(event) { var alert = document.getElementById('alert-box'); var data = $form.serialize(); - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -272,7 +272,7 @@ $('form[name=form-update-user]').submit(function(event) { var alert = document.getElementById('alert-box'); var data = $form.serialize(); - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -314,7 +314,7 @@ $('form[name=form-update-account]').submit(function(event) { var alert = document.getElementById('alert-box'); var data = $form.serialize(); - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -355,7 +355,7 @@ $('form[name=form-create-test]').submit(function(event) { var $form = $(this); var alert = document.getElementById('alert-box'); var data = $form.serialize(); - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -398,7 +398,7 @@ $('form[name=form-upload-questions]').submit(function(event) { var data = new FormData($form[0]); var file = $('input[name=data_file]')[0].files[0] data.append('file', file) - alert.innerHTML = '' + alert.innerHTML = ''; $.ajax({ url: window.location.pathname, @@ -407,25 +407,7 @@ $('form[name=form-upload-questions]').submit(function(event) { processData: false, contentType: false, success: function(response) { - if (typeof response.success === 'string' || response.success instanceof String) { - alert.innerHTML = alert.innerHTML + ` - - `; - } else if (response.success instanceof Array) { - for (var i = 0; i < response.success.length; i ++) { - alert.innerHTML = alert.innerHTML + ` - - `; - } - } + window.location.reload(); }, error: function(response) { if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { @@ -455,10 +437,10 @@ $('form[name=form-upload-questions]').submit(function(event) { // Edit and Delete Test Button Handlers - $('.delete-test').click(function(event) { _id = $(this).data('_id') + $.ajax({ url: `/admin/tests/delete/${_id}`, @@ -492,6 +474,89 @@ $('.delete-test').click(function(event) { event.preventDefault(); }); +// Edit and Delete Dataset Button Handlers +$('.delete-question-dataset').click(function(event) { + + var alert = document.getElementById('alert-box'); + alert.innerHTML = ''; + + var filename = $(this).data('filename'); + var disabled = $(this).hasClass('disabled'); + + if ( !disabled ) { + $.ajax({ + url: `/admin/settings/questions/delete/${filename}`, + type: 'GET', + success: function(response) { + window.location.reload(); + }, + 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-question-dataset').click(function(event) { + + var alert = document.getElementById('alert-box'); + alert.innerHTML = ''; + + var filename = $(this).data('filename'); + var disabled = $(this).hasClass('disabled'); + + if ( !disabled ) { + $.ajax({ + url: `/admin/settings/questions/default/${filename}`, + type: 'GET', + success: function(response) { + window.location.reload(); + }, + 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(); +}); + // Dismiss Cookie Alert $('#dismiss-cookie-alert').click(function(event){ @@ -503,13 +568,13 @@ $('#dismiss-cookie-alert').click(function(event){ }, dataType: 'json', success: function(response){ - console.log(response) + console.log(response); }, error: function(response){ - console.log(response) + console.log(response); } }) - event.preventDefault() + event.preventDefault(); }) \ No newline at end of file diff --git a/ref-test/admin/templates/admin/components/navbar.html b/ref-test/admin/templates/admin/components/navbar.html index 12fe70a..7fa655f 100644 --- a/ref-test/admin/templates/admin/components/navbar.html +++ b/ref-test/admin/templates/admin/components/navbar.html @@ -24,7 +24,7 @@ View Results