Streamlined post form handlers for admin console

This commit is contained in:
Vivek Santayana 2021-12-01 08:26:08 +00:00
parent 40cd1de89f
commit 0a106cb952
18 changed files with 158 additions and 601 deletions

View File

@ -14,7 +14,7 @@ class Test:
self._id = _id self._id = _id
self.start_date = start_date self.start_date = start_date
self.expiry_date = expiry_date self.expiry_date = expiry_date
self.time_limit = None if time_limit == 'none' or time_limit == '' else int(time_limit) self.time_limit = None if time_limit == 'none' or time_limit == '' or time_limit == None else int(time_limit)
self.creator = creator self.creator = creator
self.dataset = dataset self.dataset = dataset

View File

@ -20,7 +20,7 @@ body {
padding-bottom: 40px; padding-bottom: 40px;
} }
.form-signin { .form-display {
width: 100%; width: 100%;
max-width: 420px; max-width: 420px;
padding: 15px; padding: 15px;

View File

@ -13,392 +13,37 @@ for(let i = 0; i< dropdownItems.length; i++) {
} }
} }
// Form Processing Scripts // General Post Method Form Processing Script
$('form[name=form-register]').submit(function(event) { $('form.form-post').submit(function(event) {
var $form = $(this); var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize(); var data = $form.serialize();
var url = $(this).attr('action');
alert.innerHTML = ''; var rel_success = $(this).data('rel-success');
$.ajax({ $.ajax({
url: window.location.pathname, url: url,
type: 'POST', type: 'POST',
data: data, data: data,
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
window.location.href = "/admin/login/"; window.location.href = rel_success;
}, },
error: function(response) { error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { error_response(response);
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
});
event.preventDefault();
});
$('form[name=form-login]').submit(function(event) {
var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize();
alert.innerHTML = '';
$.ajax({
url: window.location.pathname,
type: 'POST',
data: data,
dataType: 'json',
success: function(response) {
window.location.href = "/admin/dashboard/";
},
error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
});
event.preventDefault();
});
$('form[name=form-reset]').submit(function(event) {
var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize();
alert.innerHTML = '';
$.ajax({
url: window.location.pathname,
type: 'POST',
data: data,
dataType: 'json',
success: function(response) {
window.location.href = "/admin/login/";
},
error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
});
event.preventDefault();
});
$('form[name=form-update-password]').submit(function(event) {
var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize();
console.log(data)
alert.innerHTML = '';
$.ajax({
url: window.location.pathname,
type: 'POST',
data: data,
dataType: 'json',
success: function(response) {
window.location.href = "/admin/login/";
},
error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
});
event.preventDefault();
});
$('form[name=form-create-user]').submit(function(event) {
var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize();
alert.innerHTML = '';
$.ajax({
url: window.location.pathname,
type: 'POST',
data: data,
dataType: 'json',
success: function(response) {
window.location.reload();
},
error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
});
event.preventDefault();
});
$('form[name=form-delete-user]').submit(function(event) {
var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize();
alert.innerHTML = '';
$.ajax({
url: window.location.pathname,
type: 'POST',
data: data,
dataType: 'json',
success: function(response) {
window.location.href = '/admin/settings/users/';
},
error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
});
event.preventDefault();
});
$('form[name=form-update-user]').submit(function(event) {
var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize();
alert.innerHTML = '';
$.ajax({
url: window.location.pathname,
type: 'POST',
data: data,
dataType: 'json',
success: function(response) {
window.location.href = '/admin/settings/users';
},
error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
});
event.preventDefault();
});
$('form[name=form-update-account]').submit(function(event) {
var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize();
alert.innerHTML = '';
$.ajax({
url: window.location.pathname,
type: 'POST',
data: data,
dataType: 'json',
success: function(response) {
window.location.href = '/admin/dashboard/';
},
error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
});
event.preventDefault();
});
$('form[name=form-create-test]').submit(function(event) {
var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize();
alert.innerHTML = '';
$.ajax({
url: window.location.pathname,
type: 'POST',
data: data,
dataType: 'json',
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 + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
} }
}); });
event.preventDefault(); event.preventDefault();
}); });
// Form Upload Questions - Special case, needs to handle files.
$('form[name=form-upload-questions]').submit(function(event) { $('form[name=form-upload-questions]').submit(function(event) {
var $form = $(this); var $form = $(this);
var alert = document.getElementById('alert-box');
var data = new FormData($form[0]); var data = new FormData($form[0]);
var file = $('input[name=data_file]')[0].files[0] var file = $('input[name=data_file]')[0].files[0]
data.append('file', file) data.append('file', file)
alert.innerHTML = '';
$.ajax({ $.ajax({
url: window.location.pathname, url: window.location.pathname,
@ -410,152 +55,83 @@ $('form[name=form-upload-questions]').submit(function(event) {
window.location.reload(); window.location.reload();
}, },
error: function(response) { error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { error_response(response);
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
} }
}); });
event.preventDefault(); event.preventDefault();
}); });
// Edit and Delete Test Button Handlers // Edit and Delete Test Button Handlers
$('.delete-test').click(function(event) { $('.delete-test').click(function(event) {
_id = $(this).data('_id') let _id = $(this).data('_id')
$.ajax({ $.ajax({
url: `/admin/tests/delete/${_id}`, url: `/admin/tests/delete/`,
type: 'GET', type: 'POST',
data: JSON.stringify({'_id': _id}),
contentType: 'application/json',
success: function(response) { success: function(response) {
window.location.href = '/admin/tests/'; window.location.href = '/admin/tests/';
}, },
error: function(response){ error: function(response){
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { error_response(response);
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
});
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 + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
}
}); });
};
event.preventDefault(); event.preventDefault();
}); });
// Edit Dataset Button Handlers
$('.edit-question-dataset').click(function(event) { $('.edit-question-dataset').click(function(event) {
var alert = document.getElementById('alert-box');
alert.innerHTML = '';
var filename = $(this).data('filename'); var filename = $(this).data('filename');
var action = $(this).data('action');
var disabled = $(this).hasClass('disabled'); var disabled = $(this).hasClass('disabled');
if ( !disabled ) { if ( !disabled ) {
$.ajax({ $.ajax({
url: `/admin/settings/questions/default/${filename}`, url: `/admin/settings/questions/${action}/`,
type: 'GET', type: 'POST',
data: JSON.stringify({'filename': filename}),
contentType: 'application/json',
success: function(response) { success: function(response) {
window.location.reload(); window.location.reload();
}, },
error: function(response){ error: function(response){
error_response(response);
},
});
};
event.preventDefault();
});
function error_response(response) {
var alert = $("#alert-box");
alert.html('');
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
alert.innerHTML = alert.innerHTML + ` alert.html(`
<div class="alert alert-danger alert-dismissible fade show" role="alert"> <div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i> <i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error} ${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div> </div>
`; `);
} else if (response.responseJSON.error instanceof Array) { } else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) { for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + ` alert.html(`
<div class="alert alert-danger alert-dismissible fade show" role="alert"> <div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i> <i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]} ${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div> </div>
`; `);
} }
} }
} }
});
};
event.preventDefault();
});
// Dismiss Cookie Alert // Dismiss Cookie Alert
$('#dismiss-cookie-alert').click(function(event){ $('#dismiss-cookie-alert').click(function(event){

View File

@ -2,9 +2,9 @@
{% block content %} {% block content %}
<div class="form-container"> <div class="form-container">
<form name="form-update-account" class="form-signin"> <form name="form-update-account" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="{{ url_for('admin_views.home') }}">
{% include "admin/components/server-alerts.html" %} {% include "admin/components/server-alerts.html" %}
<h2 class="form-signin-heading">Update Your Account</h2> <h2 class="form-heading">Update Your Account</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="form-label-group"> <div class="form-label-group">
Please confirm <strong>your current password</strong> before making any changes to your user account. Please confirm <strong>your current password</strong> before making any changes to your user account.

View File

@ -2,7 +2,7 @@
{% block content %} {% block content %}
<div class="form-container"> <div class="form-container">
<form name="form-login" class="form-signin"> <form name="form-login" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="{{ url_for('admin_views.home') }}">
{% include "admin/components/server-alerts.html" %} {% include "admin/components/server-alerts.html" %}
<h2 class="form">Log In</h2> <h2 class="form">Log In</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}

View File

@ -10,9 +10,9 @@
{% block content %} {% block content %}
<div class="form-container"> <div class="form-container">
<form name="form-register" action="" method="" class="form-signin"> <form name="form-register" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="{{ url_for('admin_auth.login') }}">
{% include "admin/components/server-alerts.html" %} {% include "admin/components/server-alerts.html" %}
<h2 class="form-signin-heading">Register an Account</h2> <h2 class="form-heading">Register an Account</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="form-label-group"> <div class="form-label-group">
{{ form.username(class_="form-control", autofocus=true, placeholder="Username") }} {{ form.username(class_="form-control", autofocus=true, placeholder="Username") }}

View File

@ -2,9 +2,9 @@
{% block content %} {% block content %}
<div class="form-container"> <div class="form-container">
<form name="form-reset" class="form-signin"> <form name="form-reset" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="{{ url_for('admin_auth.login') }}">
{% include "admin/components/server-alerts.html" %} {% include "admin/components/server-alerts.html" %}
<h2 class="form-signin-heading">Reset Password</h2> <h2 class="form-heading">Reset Password</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="form-label-group"> <div class="form-label-group">
{{ form.username(class_="form-control", autofocus=true, placeholder="Enter Username") }} {{ form.username(class_="form-control", autofocus=true, placeholder="Enter Username") }}

View File

@ -2,9 +2,9 @@
{% block content %} {% block content %}
<div class="form-container"> <div class="form-container">
<form name="form-update-password" class="form-signin"> <form name="form-update-password" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="{{ url_for('admin_auth.login') }}">
{% include "admin/components/server-alerts.html" %} {% include "admin/components/server-alerts.html" %}
<h2 class="form-signin-heading">Update Password</h2> <h2 class="form-heading">Update Password</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="form-label-group"> <div class="form-label-group">
{{ form.password(class_="form-control", placeholder="Password") }} {{ form.password(class_="form-control", placeholder="Password") }}

View File

@ -2,9 +2,9 @@
{% block content %} {% block content %}
<div class="form-container"> <div class="form-container">
<form name="form-delete-user" class="form-signin"> <form name="form-delete-user" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="{{ url_for('admin_views.users') }}">
{% include "admin/components/server-alerts.html" %} {% include "admin/components/server-alerts.html" %}
<h2 class="form-signin-heading">Delete User &lsquo;{{ user.username }}&rsquo;?</h2> <h2 class="form-heading">Delete User &lsquo;{{ user.username }}&rsquo;?</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<p>This action cannot be undone. Deleting an account will mean {{ user.username }} will no longer be able to log in to the admin console.</p> <p>This action cannot be undone. Deleting an account will mean {{ user.username }} will no longer be able to log in to the admin console.</p>
<p>Are you sure you want to proceed?</p> <p>Are you sure you want to proceed?</p>

View File

@ -1,8 +1,8 @@
{% extends "admin/components/datatable.html" %} {% extends "admin/components/datatable.html" %}
{% block title %} SKA Referee Test | Upload Questions {% endblock %} {% block title %} SKA Referee Test | Upload Questions {% endblock %}
{% block content %} {% block content %}
<h1>Manage Question Datasets</h1>
{% include "admin/components/client-alerts.html" %} {% include "admin/components/client-alerts.html" %}
<h1>Manage Question Datasets</h1>
{% if data %} {% if data %}
<table id="question-datasets-table" class="table table-striped" style="width:100%"> <table id="question-datasets-table" class="table table-striped" style="width:100%">
<thead> <thead>
@ -56,14 +56,16 @@
href="#" href="#"
class="btn btn-primary edit-question-dataset {% if element.filename == default %}disabled{% endif %}" class="btn btn-primary edit-question-dataset {% if element.filename == default %}disabled{% endif %}"
data-filename="{{ element.filename }}" data-filename="{{ element.filename }}"
data-action="default"
title="Make Default" title="Make Default"
> >
<i class="bi bi-file-earmark-text-fill button-icon"></i> <i class="bi bi-file-earmark-text-fill button-icon"></i>
</button> </button>
<a <a
href="#" href="#"
class="btn btn-danger delete-question-dataset {% if element.filename == default %}disabled{% endif %}" class="btn btn-danger edit-question-dataset {% if element.filename == default %}disabled{% endif %}"
data-filename="{{ element.filename }}" data-filename="{{ element.filename }}"
data-action="delete"
title="Delete Dataset" title="Delete Dataset"
> >
<i class="bi bi-file-earmark-excel-fill button-icon"></i> <i class="bi bi-file-earmark-excel-fill button-icon"></i>
@ -80,8 +82,8 @@
</div> </div>
{% endif %} {% endif %}
<div class="form-container"> <div class="form-container">
<form name="form-upload-questions" method="post" action="#" class="form-signin" enctype="multipart/form-data"> <form name="form-upload-questions" class="form-display" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="" enctype="multipart/form-data">
<h2 class="form-signin-heading">Upload Question Dataset</h2> <h2 class="form-heading">Upload Question Dataset</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="form-upload"> <div class="form-upload">
{{ form.data_file() }} {{ form.data_file() }}

View File

@ -2,9 +2,9 @@
{% block content %} {% block content %}
<div class="form-container"> <div class="form-container">
<form name="form-update-user" class="form-signin"> <form name="form-update-user" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="{{ url_for('admin_views.users') }}">
{% include "admin/components/server-alerts.html" %} {% include "admin/components/server-alerts.html" %}
<h2 class="form-signin-heading">Update User &lsquo;{{ user.username }}&rsquo;</h2> <h2 class="form-heading">Update User &lsquo;{{ user.username }}&rsquo;</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="form-label-group"> <div class="form-label-group">
{{ form.email(class_="form-control", placeholder="Email Address", value = user.email) }} {{ form.email(class_="form-control", placeholder="Email Address", value = user.email) }}

View File

@ -71,8 +71,8 @@
</tbody> </tbody>
</table> </table>
<div class="form-container"> <div class="form-container">
<form name="form-create-user" class="form-signin"> <form name="form-create-user" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="">
<h2 class="form-signin-heading">Create User</h2> <h2 class="form-heading">Create User</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="form-label-group"> <div class="form-label-group">
{{ form.username(class_="form-control", placeholder="Enter Username") }} {{ form.username(class_="form-control", placeholder="Enter Username") }}

View File

@ -43,18 +43,18 @@
<td> <td>
{% if test.time_limit == None -%} {% if test.time_limit == None -%}
None None
{% elif test.time_limit == '60' -%} {% elif test.time_limit == 60 -%}
1 hour 1 hour
{% elif test.time_limit == '90' -%} {% elif test.time_limit == 90 -%}
1 hour 30 min 1 hour 30 min
{% elif test.time_limit == '120' -%} {% elif test.time_limit == 120 -%}
2 hours 2 hours
{% else -%} {% else -%}
{{ test.time_limit }} {{ test.time_limit }}
{% endif %} {% endif %}
</td> </td>
<td> <td>
{{ test.attempts|length }} {{ test.entries|length }}
</td> </td>
<td class="row-actions"> <td class="row-actions">
<a <a
@ -86,8 +86,8 @@
{% endif %} {% endif %}
{% if form %} {% if form %}
<div class="form-container"> <div class="form-container">
<form name="form-create-test" class="form-signin"> <form name="form-create-test" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="/admin/tests/">
<h2 class="form-signin-heading">Create Exam</h2> <h2 class="form-heading">Create Exam</h2>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="form-date-input"> <div class="form-date-input">
{{ form.start_date(placeholder="Enter Start Date", class_ = "datepicker") }} {{ form.start_date(placeholder="Enter Start Date", class_ = "datepicker") }}

View File

@ -16,7 +16,7 @@ import secrets
from main import mail from main import mail
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta
from .models.tests import Test from .models.tests import Test
from common.data_tools import get_default_dataset, get_time_options from common.data_tools import get_default_dataset, get_time_options, available_datasets
views = Blueprint( views = Blueprint(
'admin_views', 'admin_views',
@ -69,18 +69,6 @@ def disable_if_logged_in(function):
return function(*args, **kwargs) return function(*args, **kwargs)
return decorated_function return decorated_function
def available_datasets():
files = glob(os.path.join(app.config["DATA_FILE_DIRECTORY"],'*.json'))
default = get_default_dataset()
output = []
for file in files:
filename = file.rsplit('/')[-1]
label = f'{filename[:-5]} (Default)' if filename == default else filename[:-5]
element = (filename, label)
output.append(element)
output.reverse()
return output
@views.route('/') @views.route('/')
@views.route('/home/') @views.route('/home/')
@views.route('/dashboard/') @views.route('/dashboard/')
@ -288,10 +276,11 @@ def questions():
errors = [*form.errors] errors = [*form.errors]
return jsonify({ 'error': errors}), 400 return jsonify({ 'error': errors}), 400
@views.route('/settings/questions/delete/<filename>') @views.route('/settings/questions/delete/', methods=['POST'])
@admin_account_required @admin_account_required
@login_required @login_required
def delete_questions(filename): def delete_questions():
filename = request.get_json()['filename']
data_files = glob(os.path.join(app.config["DATA_FILE_DIRECTORY"],'*.json')) data_files = glob(os.path.join(app.config["DATA_FILE_DIRECTORY"],'*.json'))
if any(filename in file for file in data_files): if any(filename in file for file in data_files):
default = get_default_dataset() default = get_default_dataset()
@ -309,10 +298,11 @@ def delete_questions(filename):
return jsonify({'success': f'Question dataset {filename} has been deleted.'}), 200 return jsonify({'success': f'Question dataset {filename} has been deleted.'}), 200
return abort(404) return abort(404)
@views.route('/settings/questions/default/<filename>') @views.route('/settings/questions/default/', methods=['POST'])
@admin_account_required @admin_account_required
@login_required @login_required
def make_default_questions(filename): def make_default_questions():
filename = request.get_json()['filename']
data_files = glob(os.path.join(app.config["DATA_FILE_DIRECTORY"],'*.json')) data_files = glob(os.path.join(app.config["DATA_FILE_DIRECTORY"],'*.json'))
default_file_path = os.path.join(app.config['DATA_FILE_DIRECTORY'], '.default.txt') default_file_path = os.path.join(app.config['DATA_FILE_DIRECTORY'], '.default.txt')
if any(filename in file for file in data_files): if any(filename in file for file in data_files):
@ -369,7 +359,7 @@ def tests(filter=''):
@views.route('/tests/create/', methods=['POST']) @views.route('/tests/create/', methods=['POST'])
@admin_account_required @admin_account_required
@login_required @login_required
def _tests(): def create_test():
from .models.forms import CreateTest from .models.forms import CreateTest
form = CreateTest() form = CreateTest()
form.dataset.choices = available_datasets() form.dataset.choices = available_datasets()
@ -405,8 +395,15 @@ def _tests():
errors = [*form.expiry.errors, *form.time_limit.errors] errors = [*form.expiry.errors, *form.time_limit.errors]
return jsonify({ 'error': errors}), 400 return jsonify({ 'error': errors}), 400
@views.route('/tests/delete/<_id>/') @views.route('/tests/delete/', methods=['POST'])
def delete_test(_id): def delete_test():
_id = request.get_json()['_id']
if db.tests.find_one({'_id': _id}): if db.tests.find_one({'_id': _id}):
return Test(_id = _id).delete() return Test(_id = _id).delete()
return jsonify({'error': 'Could not find the corresponding test to delete.'}), 404
@views.route('/test/<id>/', methods=['GET','POST'])
def view_test(_id, filter=''):
test = db.tests.find_one({'_id':_id})
if not test:
return abort(404) return abort(404)

View File

@ -2,6 +2,7 @@ import os
import pathlib import pathlib
from json import dump, loads from json import dump, loads
from datetime import datetime, timedelta from datetime import datetime, timedelta
from glob import glob
from flask.json import jsonify from flask.json import jsonify
from main import app from main import app
@ -24,6 +25,18 @@ def get_default_dataset():
default = default_file.read() default = default_file.read()
return default return default
def available_datasets():
files = glob(os.path.join(app.config["DATA_FILE_DIRECTORY"],'*.json'))
default = get_default_dataset()
output = []
for file in files:
filename = file.rsplit('/')[-1]
label = f'{filename[:-5]} (Default)' if filename == default else filename[:-5]
element = (filename, label)
output.append(element)
output.reverse()
return output
def check_json_format(file): def check_json_format(file):
if not '.' in file.filename: if not '.' in file.filename:
return False return False
@ -168,7 +181,6 @@ def evaluate_answers(dataset: dict, answers: dict):
'max': max 'max': max
} }
def get_tags_list(dataset:dict): def get_tags_list(dataset:dict):
output = [] output = []
blocks = dataset['questions'] blocks = dataset['questions']
@ -180,7 +192,6 @@ def get_tags_list(dataset:dict):
output = list(set(output) | set(question['tags'])) output = list(set(output) | set(question['tags']))
return output return output
def get_time_options(): def get_time_options():
time_options = [ time_options = [
('none', 'None'), ('none', 'None'),

View File

@ -28,16 +28,16 @@ $("#btn-toggle-navigator").click(function(event){
$(".navigator-text").fadeIn(); $(".navigator-text").fadeIn();
$(".review-text").fadeOut(); $(".review-text").fadeOut();
toggle_navigator = false; toggle_navigator = false;
$quiz_navigator.focus(); $(window).scrollTop(0);
} else { } else {
$quiz_navigator.fadeOut(); $quiz_navigator.fadeOut();
if (toggle_settings) { if (toggle_settings) {
$quiz_settings.fadeIn(); $quiz_settings.fadeIn();
$quiz_settings.focus() $(window).scrollTop(0);
toggle_settings = false; toggle_settings = false;
} else { } else {
$quiz_render.fadeIn(); $quiz_render.fadeIn();
$question_title.focus(); $(window).scrollTop(0);
} }
} }
event.preventDefault(); event.preventDefault();
@ -51,17 +51,17 @@ $("#btn-toggle-settings").click(function(event){
} }
$quiz_render.fadeOut(); $quiz_render.fadeOut();
$quiz_settings.fadeIn(); $quiz_settings.fadeIn();
$quiz_settings.focus() $(window).scrollTop(0);
toggle_settings = false; toggle_settings = false;
} else { } else {
$quiz_settings.fadeOut(); $quiz_settings.fadeOut();
if (toggle_navigator) { if (toggle_navigator) {
$quiz_navigator.fadeIn(); $quiz_navigator.fadeIn();
toggle_navigator = false; toggle_navigator = false;
$quiz_navigator.focus(); $(window).scrollTop(0);
} else { } else {
$quiz_render.fadeIn(); $quiz_render.fadeIn();
$question_title.focus(); $(window).scrollTop(0);
} }
} }
event.preventDefault(); event.preventDefault();
@ -71,7 +71,7 @@ $(".btn-quiz-return").click(function(event){
$quiz_navigator.fadeOut(); $quiz_navigator.fadeOut();
$quiz_settings.fadeOut(); $quiz_settings.fadeOut();
$quiz_render.fadeIn(); $quiz_render.fadeIn();
$question_title.focus(); $(window).scrollTop(0);
toggle_settings = false; toggle_settings = false;
toggle_navigator = false; toggle_navigator = false;
event.preventDefault(); event.preventDefault();
@ -85,9 +85,10 @@ $("#navigator-container").on("click", ".q-navigator-button", function(event){
check_answered(); check_answered();
update_navigator(); update_navigator();
current_question = parseInt($(this).attr("name")); current_question = parseInt($(this).attr("name"));
$quiz_navigator.fadeOut();
$quiz_render.fadeIn(); $quiz_render.fadeIn();
$question_title.focus(); $question_title.focus();
$quiz_navigator.fadeOut(); $(window).scrollTop(0);
toggle_navigator = false; toggle_navigator = false;
toggle_settings = false; toggle_settings = false;
render_question(); render_question();
@ -164,25 +165,7 @@ $("#btn-start-quiz").click(function(event){
} }
}, },
error: function(response) { error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { error_response(response);
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
} }
}); });
@ -209,7 +192,7 @@ $("#q-review-answers").click(function(event){
$(".navigator-text").fadeOut(); $(".navigator-text").fadeOut();
$(".review-text").fadeIn(); $(".review-text").fadeIn();
toggle_navigator = false; toggle_navigator = false;
$quiz_navigator.focus(); $(window).scrollTop(0);
} else { } else {
$quiz_navigator.fadeOut(); $quiz_navigator.fadeOut();
if (toggle_settings) { if (toggle_settings) {
@ -238,25 +221,7 @@ $(".quiz-button-submit").click(function(event){
window.location.href = `/result/`; window.location.href = `/result/`;
}, },
error: function(response) { error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { error_response(response);
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
} }
}); });
@ -392,6 +357,7 @@ function render_question() {
} }
$question_options.html(options_output); $question_options.html(options_output);
$question_title.focus(); $question_title.focus();
$(window).scrollTop(0);
} }
function check_answered() { function check_answered() {

View File

@ -4,7 +4,7 @@ $(document).ready(function() {
}); });
$('.test-code-input').keyup(function() { $('.test-code-input').keyup(function() {
var input = $(this).val().split("-").join("").split("—").join(""); // remove hyphens and mdashes var input = $(this).val().split("-").join("").split("—").join("");
if (input.length > 0) { if (input.length > 0) {
input = input.match(new RegExp('.{1,4}', 'g')).join("—"); input = input.match(new RegExp('.{1,4}', 'g')).join("—");
} }
@ -15,11 +15,8 @@ $(document).ready(function() {
$('form[name=form-quiz-start]').submit(function(event) { $('form[name=form-quiz-start]').submit(function(event) {
var $form = $(this); var $form = $(this);
var alert = document.getElementById('alert-box');
var data = $form.serialize(); var data = $form.serialize();
alert.innerHTML = ''
$.ajax({ $.ajax({
url: window.location.pathname, url: window.location.pathname,
type: 'POST', type: 'POST',
@ -31,31 +28,39 @@ $('form[name=form-quiz-start]').submit(function(event) {
window.location.href = `/test/`; window.location.href = `/test/`;
}, },
error: function(response) { error: function(response) {
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { error_response(response);
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.innerHTML = alert.innerHTML + `
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
}
}
} }
}); });
event.preventDefault(); event.preventDefault();
}); });
function error_response(response) {
var alert = $("#alert-box");
alert.html('');
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
alert.html(`
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`);
} else if (response.responseJSON.error instanceof Array) {
for (var i = 0; i < response.responseJSON.error.length; i ++) {
alert.html(`
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
${response.responseJSON.error[i]}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`);
}
}
}
// Dismiss Cookie Alert // Dismiss Cookie Alert
$('#dismiss-cookie-alert').click(function(event){ $('#dismiss-cookie-alert').click(function(event){

View File

@ -59,10 +59,10 @@ def start():
test = db.tests.find_one({'test_code': test_code}) test = db.tests.find_one({'test_code': test_code})
if not test: if not test:
return jsonify({'error': 'The exam code you entered is invalid.'}), 400 return jsonify({'error': 'The exam code you entered is invalid.'}), 400
if test['expiry_date'] < datetime.utcnow(): if test['expiry_date'] + timedelta(days=1) < datetime.utcnow():
return jsonify({'error': f'The exam code you entered expired on {test["expiry_date"].strftime("%d %b %Y")}.'}), 400 return jsonify({'error': f'The exam code you entered expired on {test["expiry_date"].strftime("%d %b %Y")} UTC.'}), 400
if test['start_date'] > datetime.utcnow(): if test['start_date'] > datetime.utcnow():
return jsonify({'error': f'The exam has not yet opened. Your exam code will be valid from {test["start_date"].strftime("%d %b %Y %H:%M")}.'}), 400 return jsonify({'error': f'The exam has not yet opened. Your exam code will be valid from {test["start_date"].strftime("%d %b %Y %H:%M")} UTC.'}), 400
entry = { entry = {
'_id': uuid4().hex, '_id': uuid4().hex,
'name': encrypt(name), 'name': encrypt(name),