From c9ad8e87cd60d6e7176242d34a0370410dd1f503 Mon Sep 17 00:00:00 2001 From: Vivek Santayana Date: Sat, 4 Mar 2023 19:48:49 +0000 Subject: [PATCH 1/9] Bugfix: rendering htm elements in results page --- ref-test/app/admin/templates/admin/result-detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ref-test/app/admin/templates/admin/result-detail.html b/ref-test/app/admin/templates/admin/result-detail.html index daffa8e..4709eaa 100644 --- a/ref-test/app/admin/templates/admin/result-detail.html +++ b/ref-test/app/admin/templates/admin/result-detail.html @@ -108,7 +108,7 @@ {% for tag, scores in entry.result.tags.items() %} - {{ tag }} + {{ tag|safe }} {{ scores.scored }} From 9a02048199dad314b7f8388d1b32b135d583fea2 Mon Sep 17 00:00:00 2001 From: Vivek Santayana Date: Sun, 5 Mar 2023 00:26:39 +0000 Subject: [PATCH 2/9] Added get_file method to datasets --- ref-test/app/admin/views.py | 5 +---- ref-test/app/models/dataset.py | 8 +++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ref-test/app/admin/views.py b/ref-test/app/admin/views.py index 45aeb1b..a901d82 100644 --- a/ref-test/app/admin/views.py +++ b/ref-test/app/admin/views.py @@ -435,10 +435,7 @@ def _view_entry(id:str=None): flash('Invalid entry ID.', 'error') return redirect(url_for('admin._view_entries')) test = entry.test - dataset = test.dataset - dataset_path = dataset.get_file() - with open(dataset_path, 'r') as _dataset: - data = loads(_dataset.read()) + data = test.dataset.get_data() correct = get_correct_answers(dataset=data) answers = answer_options(dataset=data) return render_template('/admin/result-detail.html', entry = entry, correct = correct, answers = answers) diff --git a/ref-test/app/models/dataset.py b/ref-test/app/models/dataset.py index 88dc05d..0455301 100644 --- a/ref-test/app/models/dataset.py +++ b/ref-test/app/models/dataset.py @@ -8,7 +8,7 @@ from flask_login import current_user from werkzeug.utils import secure_filename from datetime import datetime -from json import dump +from json import dump, loads from os import path, remove from pathlib import Path from uuid import uuid4 @@ -116,6 +116,12 @@ class Dataset(db.Model): file_path = path.join(data, 'questions', filename) return file_path + def get_data(self): + dataset_path = self.get_file() + with open(dataset_path, 'r') as _dataset: + data = loads(_dataset.read()) + return data + def update(self, data:list=None, default:bool=False): self.date = datetime.now() if default: self.make_default() From 76d60546e245731ddf4935791cc59903835b0a5f Mon Sep 17 00:00:00 2001 From: Vivek Santayana Date: Sun, 5 Mar 2023 00:27:03 +0000 Subject: [PATCH 3/9] Cleared whitespace --- ref-test/app/admin/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ref-test/app/admin/views.py b/ref-test/app/admin/views.py index a901d82..2f6e8e1 100644 --- a/ref-test/app/admin/views.py +++ b/ref-test/app/admin/views.py @@ -251,7 +251,6 @@ def _questions(): if success: return jsonify({'success': message}), 200 return jsonify({'error': message}), 400 return send_errors_to_client(form=form) - try: data = Dataset.query.all() except Exception as exception: write('system.log', f'Database error when processing request \'{request.url}\': {exception}') From ea86fd9ae66c3dade605f672aab0a92e577444c6 Mon Sep 17 00:00:00 2001 From: Vivek Santayana Date: Sun, 5 Mar 2023 00:27:30 +0000 Subject: [PATCH 4/9] Updated parameter for new library version --- ref-test/app/admin/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ref-test/app/admin/views.py b/ref-test/app/admin/views.py index 2f6e8e1..50fc804 100644 --- a/ref-test/app/admin/views.py +++ b/ref-test/app/admin/views.py @@ -280,7 +280,7 @@ def _download(id:str): return abort(500) if not dataset: return abort(404) data_path = path.abspath(dataset.get_file()) - return send_file(data_path, as_attachment=True, attachment_filename=f'{dataset.get_name()}.json') + return send_file(data_path, as_attachment=True, download_name=f'{dataset.get_name()}.json') @admin.route('/tests//', methods=['GET']) @admin.route('/tests/', methods=['GET']) From a1289da09cef9a5befaeb141ef7b4b1a338fa294 Mon Sep 17 00:00:00 2001 From: Vivek Santayana Date: Sun, 5 Mar 2023 00:28:54 +0000 Subject: [PATCH 5/9] Added analysis button and scripting --- ref-test/app/admin/static/js/script.js | 26 +++++++++++++++++++ .../templates/admin/settings/questions.html | 11 +++++++- ref-test/app/admin/templates/admin/test.html | 12 ++++++++- ref-test/app/admin/templates/admin/tests.html | 9 +++++++ ref-test/app/view/static/js/view.js | 21 +++++++++++++++ ref-test/app/view/templates/view/console.html | 13 +++++++++- 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/ref-test/app/admin/static/js/script.js b/ref-test/app/admin/static/js/script.js index caaee59..7a6a3e6 100644 --- a/ref-test/app/admin/static/js/script.js +++ b/ref-test/app/admin/static/js/script.js @@ -88,6 +88,19 @@ $('.test-action').click(function(event) { }) } else if (action == 'edit') { window.location.href = `/admin/test/${id}/` + } else if (action == 'analyse') { + $.ajax({ + url: `/admin/analysis/`, + type: 'POST', + data: JSON.stringify({'id': id, 'class': 'test'}), + contentType: 'application/json', + success: function(response) { + window.location.href = response + }, + error: function(response){ + error_response(response) + }, + }) } event.preventDefault() @@ -123,6 +136,19 @@ $('.edit-question-dataset').click(function(event) { window.location.href = `/admin/view/${id}` } else if (action == 'download') { window.location.href = `/admin/settings/questions/download/${id}/` + } else if (action == 'analyse') { + $.ajax({ + url: `/admin/analysis/`, + type: 'POST', + data: JSON.stringify({'id': id, 'class': 'dataset'}), + contentType: 'application/json', + success: function(response) { + window.location.href = response + }, + error: function(response){ + error_response(response) + }, + }) } } event.preventDefault() diff --git a/ref-test/app/admin/templates/admin/settings/questions.html b/ref-test/app/admin/templates/admin/settings/questions.html index d9e941a..0dd67c8 100644 --- a/ref-test/app/admin/templates/admin/settings/questions.html +++ b/ref-test/app/admin/templates/admin/settings/questions.html @@ -52,6 +52,15 @@ {{ element.tests|length }} + + +
- {% endblock %} From 179a608089d9ed8c33c7fd1728e97f6c25604330 Mon Sep 17 00:00:00 2001 From: Vivek Santayana Date: Sun, 5 Mar 2023 00:29:25 +0000 Subject: [PATCH 6/9] Made randomising of question order optional --- ref-test/app/tools/test.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ref-test/app/tools/test.py b/ref-test/app/tools/test.py index ec80a38..07dcd4b 100644 --- a/ref-test/app/tools/test.py +++ b/ref-test/app/tools/test.py @@ -10,9 +10,10 @@ from functools import wraps def parse_test_code(code): return code.replace('—', '').lower() -def generate_questions(dataset:list): +def generate_questions(dataset:list, randomise:bool=True): output = [] - for block in randomise_list(dataset): + question_dataset = randomise_list(dataset) if randomise else dataset + for block in question_dataset: if block['type'] == 'question': question = { 'type': 'question', @@ -20,11 +21,12 @@ def generate_questions(dataset:list): 'question_header': '', 'text': block['text'] } - if block['q_type'] == 'Multiple Choice': question['options'] = randomise_list([*enumerate(block['options'])]) + if block['q_type'] == 'Multiple Choice' and randomise: question['options'] = randomise_list([*enumerate(block['options'])]) else: question['options'] = [*enumerate(block['options'])] output.append(question) elif block['type'] == 'block': - for key, _question in enumerate(randomise_list(block['questions'])): + block_questions = randomise_list(block['questions']) if randomise else block['questions'] + for key, _question in enumerate(block_questions): question = { 'type': 'block', 'q_no': _question['q_no'], @@ -33,7 +35,7 @@ def generate_questions(dataset:list): 'block_q_no': key, 'text': _question['text'] } - if _question['q_type'] == 'Multiple Choice': question['options'] = randomise_list([*enumerate(_question['options'])]) + if _question['q_type'] == 'Multiple Choice' and randomise: question['options'] = randomise_list([*enumerate(_question['options'])]) else: question['options'] = [*enumerate(_question['options'])] output.append(question) return output From a56358b8ddcbae2a1562ef0b2c84491c0e07f06b Mon Sep 17 00:00:00 2001 From: Vivek Santayana Date: Sun, 5 Mar 2023 00:31:33 +0000 Subject: [PATCH 7/9] Added question parser for analysis --- ref-test/app/analysis/views.py | 1 + ref-test/app/tools/data.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/ref-test/app/analysis/views.py b/ref-test/app/analysis/views.py index 6473581..c5234dd 100644 --- a/ref-test/app/analysis/views.py +++ b/ref-test/app/analysis/views.py @@ -1,6 +1,7 @@ from ..models import Dataset, Test 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.helpers import abort, flash, redirect, url_for diff --git a/ref-test/app/tools/data.py b/ref-test/app/tools/data.py index a1660ab..af41acc 100644 --- a/ref-test/app/tools/data.py +++ b/ref-test/app/tools/data.py @@ -145,4 +145,26 @@ def analyse(subject:Union[Dataset,Test]) -> dict: output['scores']['mean'] = mean(scores_raw) output['scores']['median'] = median(scores_raw) output['scores']['stdev'] = stdev(scores_raw, output['scores']['mean']) if len(scores_raw) > 1 else None + return output + +def parse_questions(dataset:list): + output = [] + for block in dataset: + if block['type'] == 'question': + question = { + 'q_no': block['q_no'], + 'tags': block['tags'], + 'correct': block['correct'] + } + question['options'] = [*enumerate(block['options'])] + output.append(question) + elif block['type'] == 'block': + for _question in block['questions']: + question = { + 'q_no': _question['q_no'], + 'tags': _question['tags'], + 'correct': _question['correct'] + } + question['options'] = [*enumerate(_question['options'])] + output.append(question) return output \ No newline at end of file From fcc4d559477fda5209387a4c2d68761a9d6982bb Mon Sep 17 00:00:00 2001 From: Vivek Santayana Date: Sun, 5 Mar 2023 00:32:15 +0000 Subject: [PATCH 8/9] Delete redundant lines --- ref-test/app/tools/data.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ref-test/app/tools/data.py b/ref-test/app/tools/data.py index af41acc..3085b21 100644 --- a/ref-test/app/tools/data.py +++ b/ref-test/app/tools/data.py @@ -117,7 +117,6 @@ def analyse(subject:Union[Dataset,Test]) -> dict: } } scores_raw = [] - dataset = subject if isinstance(subject, Dataset) else subject.dataset if isinstance(subject, Test): for entry in subject.entries: if entry.answers: @@ -131,7 +130,6 @@ def analyse(subject:Union[Dataset,Test]) -> dict: scores_raw.append(int(entry.result['score'])) else: for test in subject.tests: - output['entries'] += len(test.entries) for entry in test.entries: if entry.answers: for question, answer in entry.answers.items(): From ba851cb7dc30f1b334856ddd2d527ab82240120c Mon Sep 17 00:00:00 2001 From: Vivek Santayana Date: Sun, 5 Mar 2023 00:33:15 +0000 Subject: [PATCH 9/9] Added analysis UI --- ref-test/app/analysis/static/css/analysis.css | 26 +-- ref-test/app/analysis/static/js/analysis.js | 135 ++------------ .../analysis/templates/analysis/analysis.html | 174 ++++++++++++------ .../templates/analysis/components/base.html | 16 +- .../analysis/components/datatable.html | 2 +- .../analysis/components/input-forms.html | 2 +- .../analysis/templates/analysis/index.html | 31 +++- ref-test/app/analysis/views.py | 34 +++- 8 files changed, 202 insertions(+), 218 deletions(-) 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: -

    -
  • - Yes/No for when there is only a yes or no option, -
  • -
  • - Multiple Choice for your regular multiple choice questions, and -
  • -
  • - Ordered List for multiple choice questions that will be displayed in the same order as listed here. -
  • -
-

-

- 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 %}
{% block footer %} - {% include "view/components/footer.html" %} + {% include "analysis/components/footer.html" %} {% 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