From 1980363c1246bd08aed9997899c029881d717e59 Mon Sep 17 00:00:00 2001 From: viveksantayana Date: Tue, 30 Nov 2021 03:11:28 +0000 Subject: [PATCH] Built client interface --- ref-test/common/data_tools.py | 18 +- ref-test/quiz/static/css/quiz.css | 145 +++++++++ ref-test/quiz/static/css/style.css | 44 +-- ref-test/quiz/static/js/quiz.js | 302 ++++++++++++++++++ ref-test/quiz/static/js/script.js | 3 +- ref-test/quiz/templates/quiz/client.html | 199 ++++++++++++ .../quiz/templates/quiz/components/base.html | 4 + .../templates/quiz/components/navbar.html | 11 +- ref-test/quiz/views.py | 24 +- 9 files changed, 702 insertions(+), 48 deletions(-) create mode 100644 ref-test/quiz/static/css/quiz.css create mode 100644 ref-test/quiz/static/js/quiz.js create mode 100644 ref-test/quiz/templates/quiz/client.html diff --git a/ref-test/common/data_tools.py b/ref-test/common/data_tools.py index d21819b..1497b51 100644 --- a/ref-test/common/data_tools.py +++ b/ref-test/common/data_tools.py @@ -74,23 +74,29 @@ def generate_questions(dataset:dict): for block in randomise_list(questions_list): if block['type'] == 'question': question = { - 'q_type': 'question', + 'type': 'question', 'q_no': block['q_no'], 'question_header': '', - 'text': block['text'], - 'options': randomise_list(block['options']) + 'text': block['text'] } + if block['q_type'] == 'Multiple Choice': + question['options'] = randomise_list(block['options']) + else: + question['options'] = block['options'].copy() output.append(question) if block['type'] == 'block': for key, _question in enumerate(randomise_list(block['questions'])): question = { - 'q_type': 'block', + 'type': 'block', 'q_no': _question['q_no'], 'question_header': block['question_header'] if 'question_header' in block else '', 'block_length': len(block['questions']), 'block_q_no': key, - 'text': _question['text'], - 'options': randomise_list(_question['options']) + 'text': _question['text'] } + if _question['q_type'] == 'Multiple Choice': + question['options'] = randomise_list(_question['options']) + else: + question['options'] = _question['options'].copy() output.append(question) return output \ No newline at end of file diff --git a/ref-test/quiz/static/css/quiz.css b/ref-test/quiz/static/css/quiz.css new file mode 100644 index 0000000..e416f9b --- /dev/null +++ b/ref-test/quiz/static/css/quiz.css @@ -0,0 +1,145 @@ +/*! Generated by Font Squirrel (https://www.fontsquirrel.com) on November 23, 2021 */ + +@font-face { + font-family: 'opendyslexic3bold'; + src: url('../fonts/opendyslexic3-bold-webfont.woff2') format('woff2'), + url('../fonts/opendyslexic3-bold-webfont.woff') format('woff'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'opendyslexic3regular'; + src: url('../fonts/opendyslexic3-regular-webfont.woff2') format('woff2'), + url('../fonts/opendyslexic3-regular-webfont.woff') format('woff'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'opendyslexicmonoregular'; + src: url('../fonts/opendyslexicmono-regular-webfont.woff2') format('woff2'), + url('../fonts/opendyslexicmono-regular-webfont.woff') format('woff'); + font-weight: normal; + font-style: normal; + +} + +/* Class Definitions */ + +.form-quiz-configure { + width: 100%; + padding: 15px; + margin: auto; +} + +.q-f-opendyslexic { + font-family: 'opendyslexic3bold'; +} + +.q-f-comicsans { + font-family: 'Comic Sans MS', 'Comic Sans'; +} + +.q-f-osdefault { + font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; +} + +.q-f-verdana { + font-family: Verdana, sans-serif; +} + +.q-f-tahoma { + font-family: Tahoma, sans-serif; +} + +.q-f-arial { + font-family: Arial, Helvetica, sans-serif; +} + +.q-f-12pt { + font-size: 12pt; +} + +.q-f-14pt { + font-size: 14pt; +} + +.q-f-16pt { + font-size: 16pt; +} + +.q-f-18pt { + font-size: 18pt; +} + +.q-settings-element { + margin-bottom: 2rem; +} + +.q-bg-light-1 { + background-color: beige; +} + +.q-bg-light-2 { + background-color: #EBE3E1; +} + +#sample-question { + margin: 2rem auto; + padding: 2rem; +} + +.question-container { + margin: 2rem auto; + padding: 2 rem; +} + +.question-title { + margin: 2rem 0; +} + +.question-header { + margin: 1rem auto 3rem; + width: 90%; +} + +.btn-quiz-control { + width: fit-content; +} + +#q-topbar a.btn { + padding: 2px 6px 0px 6px; + font-size: 14pt; + height: fit-content; + width: fit-content; + margin: 0px 4px; +} + +.q-timer { + padding-top: 0px; + margin: 0px auto; + font-size: 16pt; +} + +.q-navigator-button { + margin: 5px; +} + +.control-button-container { + width: fit-content; + margin: 0 auto; +} + +/* Layout for Mobile Devices */ +@media only screen and (max-width: 576px) { + body { + padding-top: 140px; + } + + .navbar .container { + justify-content: space-evenly; + } +} \ No newline at end of file diff --git a/ref-test/quiz/static/css/style.css b/ref-test/quiz/static/css/style.css index 8d737af..9cfdbb6 100644 --- a/ref-test/quiz/static/css/style.css +++ b/ref-test/quiz/static/css/style.css @@ -1,7 +1,3 @@ -.bg-light { - background-color: #EBE3E1!important; -} - body { padding: 80px 0; line-height: 1.5; @@ -111,7 +107,7 @@ body { color: #777; } -.form-check { +.form-check-margin { margin-bottom: 2rem; } @@ -137,33 +133,17 @@ body { margin: 0 2px; } -/*! Generated by Font Squirrel (https://www.fontsquirrel.com) on November 23, 2021 */ - -@font-face { - font-family: 'opendyslexic3bold'; - src: url('../fonts/opendyslexic3-bold-webfont.woff2') format('woff2'), - url('../fonts/opendyslexic3-bold-webfont.woff') format('woff'); - font-weight: normal; - font-style: normal; - -} - -@font-face { - font-family: 'opendyslexic3regular'; - src: url('../fonts/opendyslexic3-regular-webfont.woff2') format('woff2'), - url('../fonts/opendyslexic3-regular-webfont.woff') format('woff'); - font-weight: normal; - font-style: normal; - -} - -@font-face { - font-family: 'opendyslexicmonoregular'; - src: url('../fonts/opendyslexicmono-regular-webfont.woff2') format('woff2'), - url('../fonts/opendyslexicmono-regular-webfont.woff') format('woff'); - font-weight: normal; - font-style: normal; - +/* Change Autocomplete styles in Chrome*/ +input:-webkit-autofill, +input:-webkit-autofill:hover, +input:-webkit-autofill:focus, +textarea:-webkit-autofill, +textarea:-webkit-autofill:hover, +textarea:-webkit-autofill:focus, +select:-webkit-autofill, +select:-webkit-autofill:hover, +select:-webkit-autofill:focus { + transition: background-color 5000s ease-in-out 0s; } /* Fallback for Edge diff --git a/ref-test/quiz/static/js/quiz.js b/ref-test/quiz/static/js/quiz.js new file mode 100644 index 0000000..73070c9 --- /dev/null +++ b/ref-test/quiz/static/js/quiz.js @@ -0,0 +1,302 @@ +// Click Listeners + +$("input[name='font-select']").change(function(){ + let $choice = $(this).val(); + set_font($choice); +}); + +$("input[name='font-size']").change(function(){ + let $choice = $(this).val(); + set_font_size($choice); +}); + +$("input[name='bg-select']").change(function(){ + let $choice = $(this).val(); + set_bg_colour($choice); +}); + +$("#btn-toggle-navigator").click(function(event){ + if ($quiz_navigator.is(":hidden")) { + if ($quiz_settings.is(":visible")) { + toggle_settings = true; + $quiz_settings.hide(); + } + $quiz_render.hide(); + $quiz_navigator.show(); + toggle_navigator = false; + } else { + $quiz_navigator.hide(); + if (toggle_settings) { + $quiz_settings.show(); + toggle_settings = false; + } else { + $quiz_render.show(); + } + } + event.preventDefault(); +}); + +$("#btn-toggle-settings").click(function(event){ + if (($quiz_settings).is(":hidden")) { + if ($quiz_navigator.is(":visible")) { + toggle_navigator = true; + $quiz_navigator.hide(); + } + $quiz_render.hide(); + $quiz_settings.show(); + toggle_settings = false; + } else { + $quiz_settings.hide(); + if (toggle_navigator) { + $quiz_navigator.show(); + toggle_navigator = false; + } else { + $quiz_render.show(); + } + } + event.preventDefault(); +}); + +$(".btn-dummy").click(function(event){ + event.preventDefault(); +}); + +$(".q-question-nav").click(function(event){ + check_answered(); + if ($(this).attr("id") == "q-nav-next") { + if (current_question < questions.length) { + current_question ++; + } + } else if ($(this).attr("id") == "q-nav-prev") { + if (current_question > 0) { + current_question --; + } + } + render_question(); + check_flag(); + event.preventDefault(); +}); + +$("#q-nav-flag").click(function(event){ + if (question_status[current_question] != 1) { + question_status[current_question] = 1; + $(this).removeClass().addClass("btn btn-warning"); + } else { + question_status[current_question] = 0; + $(this).removeClass().addClass("btn btn-secondary"); + } + event.preventDefault(); +}); + +$("#btn-start-quiz").click(function(event){ + $(this).hide(); + $(".btn-quiz-return").show(); + $(".quiz-console").show(); + $("#quiz-settings").hide(); + $("#quiz-navigator").hide(); + $(".quiz-start-text").hide(); + + $.ajax({ + url: `/api/questions/`, + type: 'POST', + data: JSON.stringify({'_id': _id}), + contentType: "application/json", + success: function(response) { + time_limit_data = response.time_limit; + questions = response.questions; + total_questions = questions.length; + window.localStorage.setItem('questions', JSON.stringify(questions)); + render_question(); + check_flag(); + }, + error: function(response) { + console.log(response); + } + }); + + event.preventDefault(); +}); + +// Functions + +function set_font(value = 'osdefault') { + let font_styles = ['arial', 'comicsans', 'opendyslexic', 'tahoma', 'verdana'] + + for (let i = 0; i < font_styles.length; i ++) { + if (font_styles[i] != value) { + $("body").removeClass( `q-f-${font_styles[i]}` ); + }; + }; + + if (value != 'osdefault') { + $("body").addClass(`q-f-${value}`); + }; + + display_settings['font-select'] = value; + window.localStorage.setItem('display_settings', JSON.stringify(display_settings)); + $('input[name="font-select"][value="' + value + '"]').prop('checked', true); +} + +function set_font_size(value = '14pt') { + let font_sizes = ['12pt', '16pt', '18pt'] + + for (let i = 0; i < font_sizes.length; i ++) { + if (font_sizes[i] != value) { + $("body").removeClass( `q-f-${font_sizes[i]}` ); + }; + }; + + if (value != '14pt') { + $("body").addClass(`q-f-${value}`); + }; + + display_settings['font-size'] = value; + window.localStorage.setItem('display_settings', JSON.stringify(display_settings)); + $('input[name="font-size"][value="' + value + '"]').prop('checked', true); +} + +function set_bg_colour(value = 'bg-light') { + let backgrounds = ['bg-light', 'q-bg-light-1', 'q-bg-light-2', 'alert-primary', 'alert-secondary', 'alert-dark', 'bg-dark'] + + for (let i = 0; i < backgrounds.length; i ++) { + if (backgrounds[i] != value) { + $("body").removeClass(backgrounds[i]); + if (backgrounds[i] == 'bg-dark') { + $("body").removeClass('text-light'); + }; + if (backgrounds[i] == 'alert-primary' || backgrounds[i] == 'alert-secondary' || backgrounds[i] == 'alert-dark') { + $("body").removeClass('text-dark'); + }; + }; + }; + + $("body").addClass(value); + if (value == 'bg-dark') { + $("body").addClass('text-light'); + }; + if (value == 'alert-primary' || value == 'alert-secondary' || value == 'alert-dark') { + $("body").addClass('text-dark'); + }; + + display_settings['bg-select'] = value; + window.localStorage.setItem('display_settings', JSON.stringify(display_settings)); + $('input[name="bg-select"][value="' + value + '"]').prop('checked', true); +} + +function get_settings_from_storage() { + let display_settings = window.localStorage.getItem('display_settings') + if (display_settings != null) { + return JSON.parse(display_settings); + }; + return { + 'font-select': 'osdefault', + 'font-size': '14pt', + 'bg-select': 'bg-light' + } +} + +function apply_settings(settings) { + set_font(settings['font-select']); + set_font_size(settings['font-size']); + set_bg_colour(settings['bg-select']); +} + +function render_question() { + if (current_question == 0) { + $nav_prev.addClass('disabled'); + } + if (current_question == questions.length - 1) { + $nav_next.addClass('disabled'); + } + if ($nav_prev.hasClass('disabled') && current_question > 0) { + $nav_prev.removeClass('disabled'); + } + if ($nav_next.hasClass('disabled') && current_question < questions.length - 1) { + $nav_next.removeClass('disabled'); + } + var question = questions[current_question]; + let header_text = question.question_header; + var block_length = 0; + if ('block_length' in question) { + block_length = question['block_length']; + }; + var block_q_no = 0; + if ('block_q_no' in question) { + block_q_no = question['block_q_no']; + } + header_text = header_text.replace('', (block_length - block_q_no).toString()); + $question_header.html(header_text); + $question_text.html(question.text); + $question_title.html(`Question ${current_question + 1} of ${ questions.length }.`); + + var options = question.options; + var options_output = ''; + for (let i = 0; i < options.length; i ++) { + options_output += `
+ + +
`; + } + $question_options.html(options_output); +} + +function check_answered() { + var question = questions[current_question]; + var name = question.q_no; + if (!$(`input[name='${name}']:checked`).val() && question_status[current_question] == 0) { + question_status[current_question] = -1; + } +} + +function check_flag() { + if (!(current_question in question_status)) { + question_status[current_question] = 0; + } + switch (question_status[current_question]) { + case -1: + $nav_flag.removeClass().addClass('btn btn-danger'); + break; + case 1: + $nav_flag.removeClass().addClass('btn btn-warning'); + break; + default: + $nav_flag.removeClass().addClass('btn btn-secondary'); + } +} + +// Variable Definitions + +const _id = window.localStorage.getItem('_id'); + +var current_question = 0; +var total_questions = 0; +var question_status = {}; +var answers = {}; +var questions = [] +var time_limit_data = '' + +var display_settings = get_settings_from_storage(); +const $quiz_settings = $("#quiz-settings"); +const $quiz_navigator = $("#quiz-navigator"); +const $quiz_render = $("#quiz-render"); +const $nav_flag = $("#q-nav-flag"); +const $nav_next = $("#q-nav-next"); +const $nav_prev = $("#q-nav-prev"); + +var toggle_settings = false; +var toggle_navigator = false; + +const $question_title = $("#quiz-question-title"); +const $question_header = $("#quiz-question-header"); +const $question_text = $("#quiz-question-text"); +const $question_options = $("#quiz-question-options"); + +// Execution on Load + +apply_settings(display_settings); + +// TODO Build navigator +// TODO Navigator Link button behaviour +// TODO Resume Exam button +// TODO Load state from storage +// TODO Answer Registry \ No newline at end of file diff --git a/ref-test/quiz/static/js/script.js b/ref-test/quiz/static/js/script.js index 0a142a4..2cbce8a 100644 --- a/ref-test/quiz/static/js/script.js +++ b/ref-test/quiz/static/js/script.js @@ -27,7 +27,8 @@ $('form[name=form-quiz-start]').submit(function(event) { dataType: 'json', success: function(response) { var _id = response._id - window.location.href = `/api/questions/${_id}`; + window.localStorage.setItem('_id', _id); + window.location.href = `/test/`; }, error: function(response) { if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { diff --git a/ref-test/quiz/templates/quiz/client.html b/ref-test/quiz/templates/quiz/client.html new file mode 100644 index 0000000..36b78b6 --- /dev/null +++ b/ref-test/quiz/templates/quiz/client.html @@ -0,0 +1,199 @@ +{% extends "quiz/components/base.html" %} + +{% block style %} + +{% endblock %} + +{% block content %} + +
+

Adjust Display Settings

+
+ You can use this panel to adjust the display settings for the exam. Please use the menu below to select the font face and font size. Below is a sample question so you can see how the exam will render with your chosen settings. +
+ +
+
+
+
+
+ Select Font +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ Select Font Size +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
Select Background Colour
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+
+

Sample Question

+

+ Korfball is a mixed-sex, controlled-contact, indoor, invasion ball sport. The sport originated in the Netherlands. It is a mixed-sex team sport. Its governing body is the International Korball Federation. There are numerous korfball leagues and associations around the world. A korfball match is officiated by a referee. +

+

+ In order to be a referee, what do you need to know? +

+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+

+ When you are happy with the settings, click ‘Start the Exam’ below to proceed. You can change these settings at any time using the red gear button on the exam console. +

+ +
+
+ + +{% endblock %} +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/ref-test/quiz/templates/quiz/components/base.html b/ref-test/quiz/templates/quiz/components/base.html index fd8f26f..fbc0ed6 100644 --- a/ref-test/quiz/templates/quiz/components/base.html +++ b/ref-test/quiz/templates/quiz/components/base.html @@ -15,6 +15,8 @@ rel="stylesheet" href="{{ url_for('.static', filename='css/style.css') }}" /> + {% block style %} + {% endblock %} {% block title %} SKA Referee Test {% endblock %} @@ -66,5 +68,7 @@ type="text/javascript" src="{{ url_for('.static', filename='js/script.js') }}" > + {% block script %} + {% endblock %} \ No newline at end of file diff --git a/ref-test/quiz/templates/quiz/components/navbar.html b/ref-test/quiz/templates/quiz/components/navbar.html index ae2e10c..e216d85 100644 --- a/ref-test/quiz/templates/quiz/components/navbar.html +++ b/ref-test/quiz/templates/quiz/components/navbar.html @@ -1,5 +1,14 @@ -