2021-11-28 02:30:46 +00:00
|
|
|
|
import os
|
|
|
|
|
import pathlib
|
|
|
|
|
from json import dump, loads
|
2021-11-30 18:06:24 +00:00
|
|
|
|
from datetime import datetime, timedelta
|
2021-12-01 08:26:08 +00:00
|
|
|
|
from glob import glob
|
2021-11-28 18:17:50 +00:00
|
|
|
|
from random import shuffle
|
2021-11-28 02:30:46 +00:00
|
|
|
|
from werkzeug.utils import secure_filename
|
|
|
|
|
|
2021-12-04 20:47:43 +00:00
|
|
|
|
from .security.database import decrypt_find_one
|
|
|
|
|
|
2021-11-28 02:30:46 +00:00
|
|
|
|
def check_data_folder_exists():
|
2021-12-05 03:49:31 +00:00
|
|
|
|
from main import app
|
2021-11-28 02:30:46 +00:00
|
|
|
|
if not os.path.exists(app.config['DATA_FILE_DIRECTORY']):
|
|
|
|
|
pathlib.Path(app.config['DATA_FILE_DIRECTORY']).mkdir(parents='True', exist_ok='True')
|
|
|
|
|
|
2021-11-28 17:28:14 +00:00
|
|
|
|
def check_default_indicator():
|
2021-12-05 03:49:31 +00:00
|
|
|
|
from main import app
|
2021-11-28 17:28:14 +00:00
|
|
|
|
if not os.path.isfile(os.path.join(app.config['DATA_FILE_DIRECTORY'], '.default.txt')):
|
|
|
|
|
open(os.path.join(app.config['DATA_FILE_DIRECTORY'], '.default.txt'),'w').close()
|
2021-11-28 02:30:46 +00:00
|
|
|
|
|
2021-11-28 17:28:14 +00:00
|
|
|
|
def get_default_dataset():
|
|
|
|
|
check_default_indicator()
|
2021-12-05 03:49:31 +00:00
|
|
|
|
from main import app
|
2021-11-28 17:28:14 +00:00
|
|
|
|
default_file_path = os.path.join(app.config['DATA_FILE_DIRECTORY'], '.default.txt')
|
|
|
|
|
with open(default_file_path, 'r') as default_file:
|
|
|
|
|
default = default_file.read()
|
|
|
|
|
return default
|
2021-11-28 02:30:46 +00:00
|
|
|
|
|
2021-12-01 08:26:08 +00:00
|
|
|
|
def available_datasets():
|
2021-12-05 03:49:31 +00:00
|
|
|
|
from main import app
|
|
|
|
|
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
|
2021-12-01 08:26:08 +00:00
|
|
|
|
|
2021-11-28 02:30:46 +00:00
|
|
|
|
def check_json_format(file):
|
|
|
|
|
if not '.' in file.filename:
|
|
|
|
|
return False
|
|
|
|
|
if not file.filename.rsplit('.', 1)[-1] == 'json':
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def validate_json_contents(file):
|
|
|
|
|
file.stream.seek(0)
|
|
|
|
|
data = loads(file.read())
|
|
|
|
|
if not type(data) is dict:
|
|
|
|
|
return False
|
|
|
|
|
elif not all( key in data for key in ['meta', 'questions']):
|
|
|
|
|
return False
|
|
|
|
|
elif not type(data['meta']) is dict:
|
|
|
|
|
return False
|
|
|
|
|
elif not type(data['questions']) is list:
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
2021-11-28 17:28:14 +00:00
|
|
|
|
def store_data_file(file, default:bool=None):
|
2021-11-28 02:30:46 +00:00
|
|
|
|
from admin.views import get_id_from_cookie
|
2021-12-05 03:49:31 +00:00
|
|
|
|
from main import app
|
2021-11-28 17:28:14 +00:00
|
|
|
|
check_default_indicator()
|
2021-11-28 02:30:46 +00:00
|
|
|
|
timestamp = datetime.utcnow()
|
|
|
|
|
filename = '.'.join([timestamp.strftime('%Y%m%d%H%M%S'),'json'])
|
|
|
|
|
filename = secure_filename(filename)
|
|
|
|
|
file_path = os.path.join(app.config['DATA_FILE_DIRECTORY'], filename)
|
|
|
|
|
file.stream.seek(0)
|
|
|
|
|
data = loads(file.read())
|
|
|
|
|
data['meta']['timestamp'] = timestamp.strftime('%Y-%m-%d %H%M%S')
|
|
|
|
|
data['meta']['author'] = get_id_from_cookie()
|
2021-11-28 17:28:14 +00:00
|
|
|
|
data['meta']['tests'] = []
|
2021-11-28 02:30:46 +00:00
|
|
|
|
with open(file_path, 'w') as _file:
|
2021-11-28 17:28:14 +00:00
|
|
|
|
dump(data, _file, indent=2)
|
|
|
|
|
if default:
|
|
|
|
|
with open(os.path.join(app.config['DATA_FILE_DIRECTORY'], '.default.txt'), 'w') as _file:
|
|
|
|
|
_file.write(filename)
|
2021-11-28 18:17:50 +00:00
|
|
|
|
return filename
|
|
|
|
|
|
|
|
|
|
def randomise_list(list:list):
|
|
|
|
|
_list = list.copy()
|
|
|
|
|
shuffle(_list)
|
|
|
|
|
return(_list)
|
|
|
|
|
|
|
|
|
|
def generate_questions(dataset:dict):
|
|
|
|
|
questions_list = dataset['questions']
|
|
|
|
|
output = []
|
|
|
|
|
for block in randomise_list(questions_list):
|
|
|
|
|
if block['type'] == 'question':
|
|
|
|
|
question = {
|
2021-11-30 03:11:28 +00:00
|
|
|
|
'type': 'question',
|
2021-11-28 18:17:50 +00:00
|
|
|
|
'q_no': block['q_no'],
|
|
|
|
|
'question_header': '',
|
2021-11-30 03:11:28 +00:00
|
|
|
|
'text': block['text']
|
2021-11-28 18:17:50 +00:00
|
|
|
|
}
|
2021-11-30 03:11:28 +00:00
|
|
|
|
if block['q_type'] == 'Multiple Choice':
|
|
|
|
|
question['options'] = randomise_list(block['options'])
|
|
|
|
|
else:
|
|
|
|
|
question['options'] = block['options'].copy()
|
2021-11-28 18:17:50 +00:00
|
|
|
|
output.append(question)
|
|
|
|
|
if block['type'] == 'block':
|
|
|
|
|
for key, _question in enumerate(randomise_list(block['questions'])):
|
|
|
|
|
question = {
|
2021-11-30 03:11:28 +00:00
|
|
|
|
'type': 'block',
|
2021-11-28 18:17:50 +00:00
|
|
|
|
'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,
|
2021-11-30 03:11:28 +00:00
|
|
|
|
'text': _question['text']
|
2021-11-28 18:17:50 +00:00
|
|
|
|
}
|
2021-11-30 03:11:28 +00:00
|
|
|
|
if _question['q_type'] == 'Multiple Choice':
|
|
|
|
|
question['options'] = randomise_list(_question['options'])
|
|
|
|
|
else:
|
|
|
|
|
question['options'] = _question['options'].copy()
|
2021-11-28 18:17:50 +00:00
|
|
|
|
output.append(question)
|
2021-11-30 18:06:24 +00:00
|
|
|
|
return output
|
|
|
|
|
|
|
|
|
|
def evaluate_answers(dataset: dict, answers: dict):
|
|
|
|
|
score = 0
|
|
|
|
|
max = 0
|
|
|
|
|
tags = {}
|
|
|
|
|
for block in dataset['questions']:
|
|
|
|
|
if block['type'] == 'question':
|
|
|
|
|
max += 1
|
|
|
|
|
q_no = block['q_no']
|
|
|
|
|
if str(q_no) in answers:
|
|
|
|
|
correct = block['correct']
|
|
|
|
|
correct_answer = block['options'][correct]
|
2021-12-07 15:03:21 +00:00
|
|
|
|
submitted_answer = answers[str(q_no)]
|
|
|
|
|
submitted_answer = submitted_answer.replace('‘', '‘').replace('’', '’').replace('—', '—')
|
|
|
|
|
if submitted_answer == correct_answer:
|
2021-11-30 18:06:24 +00:00
|
|
|
|
score += 1
|
|
|
|
|
for tag in block['tags']:
|
|
|
|
|
if tag not in tags:
|
|
|
|
|
tags[tag] = {
|
|
|
|
|
'scored': 1,
|
|
|
|
|
'max': 1
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
tags[tag]['scored'] += 1
|
|
|
|
|
tags[tag]['max'] += 1
|
|
|
|
|
else:
|
|
|
|
|
for tag in block['tags']:
|
|
|
|
|
if tag not in tags:
|
|
|
|
|
tags[tag] = {
|
|
|
|
|
'scored': 0,
|
|
|
|
|
'max': 1
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
tags[tag]['max'] += 1
|
|
|
|
|
if block['type'] == 'block':
|
|
|
|
|
for question in block['questions']:
|
|
|
|
|
max += 1
|
|
|
|
|
q_no = question['q_no']
|
|
|
|
|
if str(q_no) in answers:
|
|
|
|
|
correct = question['correct']
|
|
|
|
|
correct_answer = question['options'][correct]
|
|
|
|
|
if answers[str(q_no)] == correct_answer:
|
|
|
|
|
score += 1
|
|
|
|
|
for tag in question['tags']:
|
|
|
|
|
if tag not in tags:
|
|
|
|
|
tags[tag] = {
|
|
|
|
|
'scored': 1,
|
|
|
|
|
'max': 1
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
tags[tag]['scored'] += 1
|
|
|
|
|
tags[tag]['max'] += 1
|
|
|
|
|
else:
|
|
|
|
|
for tag in question['tags']:
|
|
|
|
|
if tag not in tags:
|
|
|
|
|
tags[tag] = {
|
|
|
|
|
'scored': 0,
|
|
|
|
|
'max': 1
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
tags[tag]['max'] += 1
|
|
|
|
|
|
|
|
|
|
grade = 'merit' if score/max >= .85 else 'pass' if score/max >= .70 else 'fail'
|
|
|
|
|
return {
|
|
|
|
|
'grade': grade,
|
|
|
|
|
'tags': tags,
|
|
|
|
|
'score': score,
|
|
|
|
|
'max': max
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def get_tags_list(dataset:dict):
|
|
|
|
|
output = []
|
|
|
|
|
blocks = dataset['questions']
|
|
|
|
|
for block in blocks:
|
|
|
|
|
if block['type'] == 'question':
|
|
|
|
|
output = list(set(output) | set(block['tags']))
|
|
|
|
|
if block['type'] == 'block':
|
|
|
|
|
for question in block['questions']:
|
|
|
|
|
output = list(set(output) | set(question['tags']))
|
2021-12-01 08:26:08 +00:00
|
|
|
|
return output
|
2021-11-30 18:06:24 +00:00
|
|
|
|
|
|
|
|
|
def get_time_options():
|
|
|
|
|
time_options = [
|
|
|
|
|
('none', 'None'),
|
|
|
|
|
('60', '1 hour'),
|
|
|
|
|
('90', '1 hour 30 minutes'),
|
|
|
|
|
('120', '2 hours')
|
|
|
|
|
]
|
2021-12-04 20:47:43 +00:00
|
|
|
|
return time_options
|
|
|
|
|
|
|
|
|
|
def get_datasets():
|
2021-12-05 03:49:31 +00:00
|
|
|
|
from main import app, db
|
2021-12-04 20:47:43 +00:00
|
|
|
|
files = glob(os.path.join(app.config["DATA_FILE_DIRECTORY"],'*.json'))
|
|
|
|
|
data = []
|
|
|
|
|
if files:
|
|
|
|
|
for file in files:
|
|
|
|
|
filename = file.rsplit('/')[-1]
|
|
|
|
|
with open(file) as _file:
|
|
|
|
|
load = loads(_file.read())
|
|
|
|
|
_author = load['meta']['author']
|
|
|
|
|
author = decrypt_find_one(db.users, {'_id': _author})['username']
|
|
|
|
|
data_element = {
|
|
|
|
|
'filename': filename,
|
|
|
|
|
'timestamp': datetime.strptime(load['meta']['timestamp'], '%Y-%m-%d %H%M%S'),
|
|
|
|
|
'author': author,
|
|
|
|
|
'use': len(load['meta']['tests'])
|
|
|
|
|
}
|
|
|
|
|
data.append(data_element)
|
2021-12-06 13:42:26 +00:00
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
def get_correct_answers(dataset:dict):
|
|
|
|
|
output = {}
|
|
|
|
|
blocks = dataset['questions']
|
|
|
|
|
for block in blocks:
|
|
|
|
|
if block['type'] == 'question':
|
|
|
|
|
output[str(block['q_no'])] = block['options'][block['correct']]
|
|
|
|
|
if block['type'] == 'block':
|
|
|
|
|
for question in block['questions']:
|
|
|
|
|
output[str(question['q_no'])] = question['options'][question['correct']]
|
|
|
|
|
return output
|