diff --git a/ref-test/app/analysis/static/css/analysis.css b/ref-test/app/analysis/static/css/analysis.css index de45037..b7ad60d 100644 --- a/ref-test/app/analysis/static/css/analysis.css +++ b/ref-test/app/analysis/static/css/analysis.css @@ -1,30 +1,8 @@ -.info-panel { - display: none; -} - -.control-panel { - margin-left: auto; - margin-right: 0; - width:fit-content; -} - #alert-box { margin: 30px auto; max-width: 460px; } -.block { - border: 2px solid black; - border-radius: 10px; - margin: 10px; - padding: 5px; -} - -.question-body, .question-block { - padding: 0px 2em; -} - -blockquote { - padding: 0px 2em; - font-style: italic; +.cell-percentage::after { + content: '%'; } \ No newline at end of file diff --git a/ref-test/app/analysis/static/js/analysis.js b/ref-test/app/analysis/static/js/analysis.js index 0d78304..3fec9d1 100644 --- a/ref-test/app/analysis/static/js/analysis.js +++ b/ref-test/app/analysis/static/js/analysis.js @@ -1,130 +1,27 @@ -// Variable Declarations -const $control_panel = $('.control-panel') -const $info_panel = $('.info-panel') -const $viewer_panel = $('.viewer-panel') +// Analyse Button Listener +$('.button-analyse').click(function(event) { -var element_index = 0 + let buttonClass = $(this).data('class') + let id = null -// Info Button Listener -$control_panel.find('button').click(function(event){ - if ($info_panel.is(":hidden")) { - $viewer_panel.hide() - $info_panel.fadeIn() - $(this).addClass('active') - } else { - $info_panel.hide() - $viewer_panel.fadeIn() - $(this).removeClass('active') + if (buttonClass == 'test' ) { + id = $('#select-test').children('option:selected').val() + } else if (buttonClass == 'dataset' ) { + id = $('#select-dataset').children('option:selected').val() } - event.preventDefault() -}) -function parse_data(data) { - var block - var obj - for (let i = 0; i < data.length; i++) { - block = data[i] - obj = document.createElement('div') - obj.classList = 'block' - if (block['type'] == 'question') { - text = document.createElement('p') - text.innerHTML = `Question ${block['q_no'] + 1}. ${block['text']}` - obj.append(text) - question_body = document.createElement('div') - question_body.className ='question-body' - type = document.createElement('p') - type.innerHTML = `Question Type: ${block['q_type']}` - question_body.append(type) - options = document.createElement('p') - options.innerHTML = 'Options:' - option_list = document.createElement('ul') - for (let _i = 0; _i < block['options'].length; _i++) { - option = document.createElement('li') - option.innerHTML = block['options'][_i] - if (block['correct'] == _i) { - option.innerHTML += ' Correct' - } - option_list.append(option) - } - options.append(option_list) - question_body.append(options) - tags = document.createElement('p') - tags.innerHTML = `Tags:` - tag_list = document.createElement('ul') - for (let _i = 0; _i < block['tags'].length; _i++) { - tag = document.createElement('li') - tag.innerHTML = block['tags'][_i] - tag_list.append(tag) - } - tags.append(tag_list) - question_body.append(tags) - obj.append(question_body) - } else if (block['type'] == 'block') { - meta = document.createElement('p') - meta.innerHTML = `Block ${i+1}. ${block['questions'].length} questions.` - obj.append(meta) - header = document.createElement('blockquote') - header.innerText = block['question_header'] - obj.append(header) - var block_question = document.createElement('div') - var question - block_question.className = 'question-block' - for (let _i = 0; _i < block['questions'].length; _i++) { - question = block['questions'][_i] - text = document.createElement('p') - text.innerHTML = `Question ${question['q_no'] + 1}. ${question['text']}` - block_question.append(text) - question_body = document.createElement('div') - question_body.className ='question-body' - type = document.createElement('p') - type.innerHTML = `Question Type: ${question['q_type']}` - question_body.append(type) - options = document.createElement('p') - options.innerHTML = 'Options:' - option_list = document.createElement('ul') - for (let __i = 0; __i < question['options'].length; __i++) { - option = document.createElement('li') - option.innerHTML = question['options'][__i] - if (question['correct'] == __i) { - option.innerHTML += ' Correct' - } - option_list.append(option) - } - options.append(option_list) - question_body.append(options) - tags = document.createElement('p') - tags.innerHTML = `Tags:` - tag_list = document.createElement('ul') - for (let __i = 0; __i < question['tags'].length; __i++) { - tag = document.createElement('li') - tag.innerHTML = question['tags'][__i] - tag_list.append(tag) - } - tags.append(tag_list) - question_body.append(tags) - block_question.append(question_body) - obj.append(block_question) - } - } - $viewer_panel.append(obj) - } -} - -// Fetch data once page finishes loading -$(window).on('load', function() { $.ajax({ - url: target, + url: `/admin/analysis/`, type: 'POST', - data: JSON.stringify({ - 'id': id, - 'action': 'fetch' - }), + data: JSON.stringify({'id': id, 'class': buttonClass}), contentType: 'application/json', success: function(response) { - parse_data(response['data']) + window.location.href = response + }, + error: function(response){ + error_response(response) }, - error: function(response) { - console.log(response) - } }) + + event.preventDefault() }) \ No newline at end of file diff --git a/ref-test/app/analysis/templates/analysis/analysis.html b/ref-test/app/analysis/templates/analysis/analysis.html index 5de1fed..627d311 100644 --- a/ref-test/app/analysis/templates/analysis/analysis.html +++ b/ref-test/app/analysis/templates/analysis/analysis.html @@ -1,74 +1,22 @@ -{% extends "view/components/base.html" %} +{% extends "analysis/components/datatable.html" %} {% block style %} {% endblock %} {% block content %} -

View Questions

+

Analysis

- This page lists all the questions in the selected dataset. + Analysis for {{ type }} {{ subject }}.

-
- -
-
+

- Information -

-

- Questions in the test are arranged in blocks. Blocks can be of two types: Blocks of multiple related questions, and Single Questions that are not part of a block. - You can add, remove, or edit both Blockss and Questions through this editor. -

-

- Blocks are useful when you have a section of the test that contains multiple questions that are related to each other, for example if there is a scenario-based section where a series of questions are about the same situation. -

-

- Blocks can contain any number of questions within them, but cannot contain nested blocks. -

-

- When you set up a block, you can also add header text that will be displayed with each question. - You can use this to provide common information for a scenario across a series of questions. -

-

- Questions come in three types: -

-

-

- Normally, multiple choice questions will have the order of the options randomised. -

-

- Questions will be displayed to candidates in a randomised order. - Blocks of questions will be kept together, but the order within the block will also be randomised. -

-

- Questions can also be categorised using tags. -

-

- Placeholder for Questions Remaining in a Block -

-

- In order to show how many questions are remaining inside a block, e.g. to say ‘the next n questions are about a specific scenario’, the app uses the placeholder <block_remaining_questions>. -

-
-
-

- Question Dataset + Question List

- +
+ + + + + + + + + {% for question in questions %} + + + + + + + {% endfor %} + +
+ Question + + Percent Correct + + Answers + + Tags +
+ {{ question.q_no + 1 }} + + {{ ((analysis.answers[question.q_no][question.correct] or 0)*100/(analysis.answers[question.q_no].values())|sum())|round(2) }} + + + {% for option in question.options %} + + + + + {% endfor %} +
+ {{ option[1] }} + + {% if question.correct == option[0] %} +
+
{{ ((analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum())|round(2) }}%
+
+ {% else %} +
+
{{ ((analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum())|round(2) }}%
+
+ {% endif %} +
+
+
    + {% for tag in question.tags %} +
  • {{ tag|safe }}
  • + {% endfor %} +
+
+
{% endblock %} @@ -111,6 +117,54 @@ +{% endblock %} + +{% block custom_data_script %} + {% endblock %} \ No newline at end of file diff --git a/ref-test/app/analysis/templates/analysis/components/base.html b/ref-test/app/analysis/templates/analysis/components/base.html index 887b851..a819c11 100644 --- a/ref-test/app/analysis/templates/analysis/components/base.html +++ b/ref-test/app/analysis/templates/analysis/components/base.html @@ -22,24 +22,24 @@ {% block style %} {% endblock %} {% block title %} SKA Referee Test | Admin Console {% endblock %} - {% include "view/components/og-meta.html" %} + {% include "analysis/components/og-meta.html" %} {% block navbar %} - {% include "view/components/navbar.html" %} + {% include "analysis/components/navbar.html" %} {% endblock %}
{% block top_alerts %} - {% include "view/components/server-alerts.html" %} + {% include "analysis/components/server-alerts.html" %} {% endblock %} {% block content %}{% endblock %}
@@ -78,7 +78,15 @@ type="text/javascript" src="{{ url_for('.static', filename='js/script.js') }}" > + {% block script %} {% endblock %} + {% block datatable_scripts %} + {% endblock %} + {% block custom_data_script %} + {% endblock %} \ No newline at end of file diff --git a/ref-test/app/analysis/templates/analysis/components/datatable.html b/ref-test/app/analysis/templates/analysis/components/datatable.html index 5eb0cb6..0de44ad 100644 --- a/ref-test/app/analysis/templates/analysis/components/datatable.html +++ b/ref-test/app/analysis/templates/analysis/components/datatable.html @@ -1,4 +1,4 @@ -{% extends "view/components/base.html" %} +{% extends "analysis/components/base.html" %} {% block datatable_css %} diff --git a/ref-test/app/analysis/templates/analysis/components/input-forms.html b/ref-test/app/analysis/templates/analysis/components/input-forms.html index 1136a28..caae994 100644 --- a/ref-test/app/analysis/templates/analysis/components/input-forms.html +++ b/ref-test/app/analysis/templates/analysis/components/input-forms.html @@ -1,4 +1,4 @@ -{% extends "view/components/base.html" %} +{% extends "analysis/components/base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block top_alerts %} {% endblock %} \ No newline at end of file diff --git a/ref-test/app/analysis/templates/analysis/index.html b/ref-test/app/analysis/templates/analysis/index.html index 3e8fd80..76bde8e 100644 --- a/ref-test/app/analysis/templates/analysis/index.html +++ b/ref-test/app/analysis/templates/analysis/index.html @@ -1,4 +1,4 @@ -{% extends "view/components/input-forms.html" %} +{% extends "analysis/components/input-forms.html" %} {% block content %}

Analysis

@@ -9,7 +9,19 @@
Exams
- {{ tests }} +
+ +
+
@@ -19,11 +31,24 @@
Datasets
- {{ datasets }} +
+ +
+
+ {% include "analysis/components/client-alerts.html" %} {% endblock %} \ No newline at end of file diff --git a/ref-test/app/analysis/views.py b/ref-test/app/analysis/views.py index c5234dd..44ead9a 100644 --- a/ref-test/app/analysis/views.py +++ b/ref-test/app/analysis/views.py @@ -3,7 +3,7 @@ from ..tools.data import analyse, check_dataset_exists, check_test_exists from ..tools.logs import write from ..tools.data import parse_questions -from flask import Blueprint, jsonify, render_template, request +from flask import Blueprint, render_template, request, jsonify from flask.helpers import abort, flash, redirect, url_for from flask_login import login_required @@ -19,10 +19,33 @@ analysis = Blueprint( @check_dataset_exists @check_test_exists def _analysis(): - _tests = Test.query.all() + try: + _tests = Test.query.all() + _datasets = Dataset.query.all() + except Exception as exception: + write('system.log', f'Database error when processing request \'{request.url}\': {exception}') + return abort(500) tests = [ test for test in _tests if test.entries ] - _datasets = Dataset.query.all() datasets = [ dataset for dataset in _datasets if dataset.entries ] + if request.method == 'POST': + selection = request.get_json() + if selection['class'] == 'test': + try: + test = Test.query.filter_by(id=selection['id']).first() + except Exception as exception: + write('system.log', f'Database error when processing request \'{request.url}\': {exception}') + return abort(500) + if not test: return jsonify({'error': 'Invalid entry ID.'}), 404 + return url_for('analysis._test', id=selection['id']), 200 + if selection['class'] == 'dataset': + try: + dataset = Dataset.query.filter_by(id=selection['id']).first() + except Exception as exception: + write('system.log', f'Database error when processing request \'{request.url}\': {exception}') + return abort(500) + if not dataset: return jsonify({'error': 'Invalid entry ID.'}), 404 + return url_for('analysis._dataset', id=selection['id']), 200 + return jsonify({'error': 'Invalid entry ID.'}), 404 return render_template('/analysis/index.html', tests=tests, datasets=datasets) @analysis.route('/test/') @@ -41,8 +64,7 @@ def _test(id:str=None): if not test: flash('Invalid exam.', 'error') return redirect(url_for('analysis._analysis')) - return jsonify(analyse(test)) - return render_template('/analysis/analysis.html', analysis=None, text='Exam') + return render_template('/analysis/analysis.html', analysis=analyse(test), subject=test.get_code(), type='exam', dataset=test.dataset, questions=parse_questions(test.dataset.get_data())) @analysis.route('/dataset/') @analysis.route('/dataset/') @@ -60,4 +82,4 @@ def _dataset(id:str=None): if not dataset: flash('Invalid dataset.', 'error') return redirect(url_for('analysis._analysis')) - return jsonify(analyse(dataset)) \ No newline at end of file + return render_template('/analysis/analysis.html', analysis=analyse(dataset), subject=dataset.get_name(), type='dataset', dataset=dataset, questions=parse_questions(dataset.get_data())) \ No newline at end of file