Added a whole lot of views.
Finished quiz API views Finished question generator and answer eval
This commit is contained in:
parent
a58f267586
commit
126bf9203c
@ -1,6 +1,7 @@
|
||||
from ..forms.admin import CreateUser, Login, Register, ResetPassword, UpdatePassword
|
||||
from ..models import User
|
||||
from ..forms.admin import CreateUser, DeleteUser, Login, Register, ResetPassword, UpdatePassword, UpdateUser, UploadData
|
||||
from ..models import Dataset, User
|
||||
from ..tools.auth import disable_if_logged_in, require_account_creation
|
||||
from ..tools.data import check_is_json, validate_json
|
||||
|
||||
from flask import Blueprint, flash, jsonify, render_template, redirect, request, session
|
||||
from flask.helpers import url_for
|
||||
@ -18,12 +19,16 @@ admin = Blueprint(
|
||||
@admin.route('/')
|
||||
@admin.route('/home/')
|
||||
@admin.route('/dashboard/')
|
||||
@login_required
|
||||
def _home():
|
||||
return 'Home Page'
|
||||
return 'Home Page' # TODO Dashboard
|
||||
|
||||
@admin.route('/settings/')
|
||||
@login_required
|
||||
def _settings():
|
||||
return 'Settings Page'
|
||||
users = User.query.all()
|
||||
datasets = Dataset.query.all()
|
||||
return render_template('/admin/settings/index.html', users=users, datasets=datasets)
|
||||
|
||||
@admin.route('/login/', methods=['GET','POST'])
|
||||
@disable_if_logged_in
|
||||
@ -64,7 +69,6 @@ def _register():
|
||||
if request.method == 'POST':
|
||||
if form.validate_on_submit():
|
||||
new_user = User()
|
||||
new_user.generate_id()
|
||||
new_user.set_username = request.form.get('username').lower()
|
||||
new_user.set_email = request.form.get('email').lower()
|
||||
new_user.set_password = request.form.get('password').lower()
|
||||
@ -140,3 +144,86 @@ def _users():
|
||||
errors = [*form.username.errors, *form.email.errors, *form.password.errors]
|
||||
return jsonify({ 'error': errors}), 401
|
||||
return render_template('/admin/settings/users.html', form = form, users = users)
|
||||
|
||||
@admin.route('/settings/users/delete/<string:id>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def _delete_user(id:str):
|
||||
user = User.query.filter_by(id=id).first()
|
||||
form = DeleteUser()
|
||||
if request.method == 'POST':
|
||||
if not user: return jsonify({'error': 'User does not exist.'}), 400
|
||||
if id == current_user.id: return jsonify({'error': 'Cannot delete your own account.'}), 400
|
||||
if form.validate_on_submit():
|
||||
password = request.form.get('password')
|
||||
if not current_user.verify_password(password): return jsonify({'error': 'The password you entered is incorrect.'}), 401
|
||||
success, message = user.delete(notify=request.form.get('notify'))
|
||||
if success: return jsonify({'success': message}), 200
|
||||
return jsonify({'error': message}), 400
|
||||
errors = form.password.errors
|
||||
return jsonify({ 'error': errors}), 400
|
||||
|
||||
if id == current_user.id:
|
||||
flash('Cannot delete your own user account.', 'error')
|
||||
return redirect(url_for('admin._users'))
|
||||
if not user:
|
||||
flash('User not found.', 'error')
|
||||
return redirect(url_for('admin._users'))
|
||||
return render_template('/admin/settings/delete_user.html', form=form, id = id, user = user)
|
||||
|
||||
@admin.route('/settings/users/update/<string:id>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def _update_user(id:str):
|
||||
user = User.query.filter_by(id=id).first()
|
||||
form = UpdateUser()
|
||||
if request.method == 'POST':
|
||||
if not user: return jsonify({'error': 'User does not exist.'}), 400
|
||||
if form.validate_on_submit():
|
||||
success, message = user.update(
|
||||
password = request.form.get('password'),
|
||||
email = request.form.get('email'),
|
||||
notify = request.form.get('notify')
|
||||
)
|
||||
if success: return jsonify({'success': message}), 200
|
||||
return jsonify({'error': message}), 400
|
||||
errors = [*form.confirm_password.errors, *form.email.errors, *form.password.errors, *form.password_reenter.errors]
|
||||
return jsonify({ 'error': errors}), 400
|
||||
if not user:
|
||||
flash('User not found.', 'error')
|
||||
return redirect(url_for('admin._users'))
|
||||
return render_template('/admin/settings/delete_user.html', form=form, id = id, user = user)
|
||||
|
||||
@admin.route('/settings/questions/', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def _quesitons():
|
||||
form = UploadData()
|
||||
if request.method == 'POST':
|
||||
if form.validate_on_submit():
|
||||
upload = form.data_file.data
|
||||
if not check_is_json(upload): return jsonify({'error': 'Invalid file. Please upload a JSON file.'}), 400
|
||||
if not validate_json(upload): return jsonify({'error': 'The data in the file is invalid.'}), 400 # TODO Perhaps make a more complex validation script
|
||||
new_dataset = Dataset()
|
||||
success, message = new_dataset.create(
|
||||
upload = upload,
|
||||
default = request.form.get('default')
|
||||
)
|
||||
if success: return jsonify({'success': message}), 200
|
||||
return jsonify({'error': message}), 400
|
||||
errors = form.data_file.errors
|
||||
return jsonify({ 'error': errors}), 400
|
||||
|
||||
data = Dataset.query.all()
|
||||
return render_template('/admin/settings/questions.html', data=data)
|
||||
|
||||
@admin.route('/settings/questions/edit/', methods=['POST'])
|
||||
@login_required
|
||||
def _delete_questions():
|
||||
id = request.get_json()['id']
|
||||
action = request.get_json()['action']
|
||||
dataset = Dataset.query.filter_by(id=id).first()
|
||||
if action == 'delete': success, message = dataset.delete()
|
||||
elif action == 'default': success, message = dataset.make_default()
|
||||
if success: return jsonify({'success': message}), 200
|
||||
return jsonify({'error': message}), 400
|
||||
|
||||
# TODO Test views
|
||||
# TODO Result views
|
@ -1,4 +1,10 @@
|
||||
from flask import Blueprint
|
||||
from ..models import Dataset, Entry
|
||||
from ..tools.test import evaluate_answers, generate_questions
|
||||
|
||||
from flask import Blueprint, jsonify, request
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from json import loads
|
||||
|
||||
api = Blueprint(
|
||||
name='api',
|
||||
@ -7,8 +13,54 @@ api = Blueprint(
|
||||
|
||||
@api.route('/questions/', methods=['POST'])
|
||||
def _fetch_questions():
|
||||
return 'Fetch Questions'
|
||||
id = request.get_json()['id']
|
||||
entry = Entry.query.filter_by(id=id).first()
|
||||
if not entry: return jsonify({'error': 'Invalid entry ID.'}), 400
|
||||
test = entry['test']
|
||||
user_code = entry['user_code']
|
||||
time_limit = test['time_limit']
|
||||
time_adjustment = 0
|
||||
if time_limit:
|
||||
_time_limit = int(time_limit)
|
||||
if user_code:
|
||||
time_adjustment = test['time_adjustments'][user_code]
|
||||
_time_limit += time_adjustment
|
||||
end_delta = timedelta(minutes=_time_limit)
|
||||
end_time = datetime.utcnow() + end_delta
|
||||
else:
|
||||
end_time = None
|
||||
entry.start()
|
||||
dataset = test['dataset']
|
||||
success, message = dataset.check_file()
|
||||
if not success: return jsonify({'error': message}), 500
|
||||
data_path = dataset.get_file()
|
||||
with open(data_path, 'r') as data_file:
|
||||
data = loads(data_file.read())
|
||||
questions = generate_questions(data)
|
||||
return jsonify({
|
||||
'time_limit': end_time,
|
||||
'questions': questions,
|
||||
'start_time': entry['start_time'],
|
||||
'time_adjustment': time_adjustment
|
||||
}), 200
|
||||
|
||||
@api.route('/submit/', methods=['POST'])
|
||||
def _submit_quiz():
|
||||
return 'Submit Quiz'
|
||||
id = request.get_json()['id']
|
||||
answers = request.get_json()['answers']
|
||||
entry = Entry.query.filter_by(id=id).first()
|
||||
if not entry: return jsonify({'error': 'Unrecognised ID.'}), 400
|
||||
test = entry['test']
|
||||
dataset = test['dataset']
|
||||
success, message = dataset.check_file()
|
||||
if not success: return jsonify({'error': message}), 500
|
||||
data_path = dataset.get_file()
|
||||
with open(data_path, 'r') as data_file:
|
||||
data = loads(data_file.read())
|
||||
result = evaluate_answers(answers=answers, key=data)
|
||||
entry.complete(answers=answers, result=result)
|
||||
return jsonify({
|
||||
'success': 'Your submission has been processed. Redirecting you to receive your results.',
|
||||
'id': id
|
||||
}), 200
|
||||
|
@ -11,6 +11,7 @@ from sqlalchemy_utils import database_exists, create_database
|
||||
|
||||
def install_scripts():
|
||||
if not path.isdir(f'./{data}'): mkdir(f'./{data}')
|
||||
if not path.isdir(f'./{data}/questions'): mkdir(f'./{data}/questions')
|
||||
if not path.isfile(f'./{data}/.gitignore'):
|
||||
with open(f'./{data}/.gitignore', 'a+') as file: file.write(f'*')
|
||||
if not path.isfile(f'./{data}/config.json'): save({}, 'config.json')
|
||||
|
@ -1,3 +1,4 @@
|
||||
from .entry import Entry
|
||||
from .test import Test
|
||||
from .user import User
|
||||
from .dataset import Dataset
|
82
ref-test/app/models/dataset.py
Normal file
82
ref-test/app/models/dataset.py
Normal file
@ -0,0 +1,82 @@
|
||||
from ..data import data
|
||||
from ..modules import db
|
||||
from ..tools.logs import write
|
||||
|
||||
from flask import flash, jsonify
|
||||
from flask_login import current_user
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from datetime import datetime
|
||||
from json import dump, loads
|
||||
from os import path, remove
|
||||
from uuid import uuid4
|
||||
|
||||
class Dataset(db.Model):
|
||||
|
||||
id = db.Column(db.String(36), primary_key=True)
|
||||
tests = db.relationship('Test', backref='dataset')
|
||||
creator_id = db.Column(db.String(36), db.ForeignKey('user.id'))
|
||||
date = db.Column(db.DateTime, nullable=False)
|
||||
default = db.Column(db.Boolean, default=False, nullable=True)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Dataset {self.id}> was added.'
|
||||
|
||||
@property
|
||||
def generate_id(self): raise AttributeError('generate_id is not a readable attribute.')
|
||||
|
||||
generate_id.setter
|
||||
def generate_id(self): self.id = uuid4.hex()
|
||||
|
||||
def make_default(self):
|
||||
for dataset in Dataset.query.all():
|
||||
dataset.default = False
|
||||
self.default = True
|
||||
db.session.commit()
|
||||
write('system.log', f'Dataset {self.id} set as default by {current_user.get_username()}.')
|
||||
flash(message='Dataset set as default.', category='success')
|
||||
return True, f'Dataset set as default.'
|
||||
|
||||
def delete(self):
|
||||
if self.default:
|
||||
message = 'Cannot delete the default dataset.'
|
||||
flash(message, 'error')
|
||||
return False, jsonify({'error': message})
|
||||
if Dataset.query.all().count() == 1:
|
||||
message = 'Cannot delete the only dataset.'
|
||||
flash(message, 'error')
|
||||
return False, jsonify({'error': message})
|
||||
write('system.log', f'Dataset {self.id} deleted by {current_user.get_username()}.')
|
||||
filename = secure_filename('.'.join([self.id,'json']))
|
||||
file_path = path.join(data, 'questions', filename)
|
||||
remove(file_path)
|
||||
db.session.delete(self)
|
||||
db.session.commit()
|
||||
return True, 'Dataset deleted.'
|
||||
|
||||
def create(self, upload, default:bool=False):
|
||||
self.generate_id()
|
||||
timestamp = datetime.now()
|
||||
filename = secure_filename('.'.join([self.id,'json']))
|
||||
file_path = path.join(data, 'questions', filename)
|
||||
upload.stream.seek(0)
|
||||
questions = loads(upload.read())
|
||||
with open(file_path, 'w') as file:
|
||||
dump(questions, file, indent=2)
|
||||
self.date = timestamp
|
||||
self.creator = current_user
|
||||
if default: self.make_default()
|
||||
write('system.log', f'New dataset {self.id} added by {current_user.get_username()}.')
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
return True, 'Dataset uploaded.'
|
||||
|
||||
def check_file(self):
|
||||
filename = secure_filename('.'.join([self.id,'json']))
|
||||
file_path = path.join(data, 'questions', filename)
|
||||
if not path.isfile(file_path): return False, 'Data file is missing.'
|
||||
|
||||
def get_file(self):
|
||||
filename = secure_filename('.'.join([self.id,'json']))
|
||||
file_path = path.join(data, 'questions', filename)
|
||||
return file_path
|
@ -8,6 +8,7 @@ from flask import jsonify
|
||||
from flask_login import current_user
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
class Entry(db.Model):
|
||||
|
||||
@ -25,6 +26,15 @@ class Entry(db.Model):
|
||||
answers = db.Column(JsonEncodedDict, nullable=True)
|
||||
result = db.Column(JsonEncodedDict, nullable=True)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<New entry by {self.first_name} {self.surname}> was added with <id {self.id}>.'
|
||||
|
||||
@property
|
||||
def generate_id(self): raise AttributeError('generate_id is not a readable attribute.')
|
||||
|
||||
generate_id.setter
|
||||
def generate_id(self): self.id = uuid4.hex()
|
||||
|
||||
@property
|
||||
def set_first_name(self): raise AttributeError('set_first_name is not a readable attribute.')
|
||||
|
||||
@ -63,10 +73,11 @@ class Entry(db.Model):
|
||||
write('tests.log', f'New test started by {self.get_first_name()} {self.get_surname()}.')
|
||||
db.session.commit()
|
||||
|
||||
def complete(self):
|
||||
def complete(self, answers:dict=None, result:dict=None):
|
||||
self.end_time = datetime.now()
|
||||
self.answers = answers
|
||||
write('tests.log', f'Test completed by {self.get_first_name()} {self.get_surname()}.')
|
||||
delta = timedelta(minutes=self.test.time_limit)
|
||||
delta = timedelta(minutes=self.test.time_limit+1)
|
||||
if not self.test.time_limit or self.end_time <= self.start_time + delta:
|
||||
self.status = 'finished'
|
||||
self.valid = True
|
||||
|
@ -11,7 +11,7 @@ from datetime import datetime
|
||||
from json import dump, loads
|
||||
import os
|
||||
import secrets
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
class Test(db.Model):
|
||||
|
||||
@ -21,18 +21,33 @@ class Test(db.Model):
|
||||
end_date = db.Column(db.DateTime, nullable=True)
|
||||
time_limit = db.Column(db.Integer, nullable=True)
|
||||
creator_id = db.Column(db.String(36), db.ForeignKey('user.id'))
|
||||
data = db.Column(db.String(36), nullable=False)
|
||||
dataset_id = db.Column(db.String(36), db.ForeignKey('dataset.id'))
|
||||
adjustments = db.Column(JsonEncodedDict, nullable=True)
|
||||
entries = db.relationship('Entry', backref='test')
|
||||
|
||||
def __repr__(self):
|
||||
return f'<test with code {self.code} was created by {current_user.get_username()}.>'
|
||||
|
||||
@property
|
||||
def generate_id(self): raise AttributeError('generate_id is not a readable attribute.')
|
||||
|
||||
generate_id.setter
|
||||
def generate_id(self): self.id = uuid4.hex()
|
||||
|
||||
@property
|
||||
def generate_code(self): raise AttributeError('generate_code is not a readable attribute.')
|
||||
|
||||
generate_code.setter
|
||||
def generate_code(self): self.code = secrets.token_hex(6).lower()
|
||||
|
||||
def get_code(self):
|
||||
code = self.code.upper()
|
||||
return '—'.join([code[:4], code[4:8], code[8:]])
|
||||
|
||||
def create(self):
|
||||
self.generate_id()
|
||||
self.generate_code()
|
||||
self.creator = current_user
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
write('system.log', f'Test with code {self.code} created by {current_user.get_username()}.')
|
||||
|
@ -17,6 +17,7 @@ class User(UserMixin, db.Model):
|
||||
reset_token = db.Column(db.String(20), nullable=True)
|
||||
verification_token = db.Column(db.String(20), nullable=True)
|
||||
tests = db.relationship('Test', backref='creator')
|
||||
datasets = db.relationship('Dataset', backref='creator')
|
||||
|
||||
def __repr__(self):
|
||||
return f'<user {self.username}> was added with <id {self.id}>.'
|
||||
@ -52,6 +53,7 @@ class User(UserMixin, db.Model):
|
||||
def get_email(self): return decrypt(self.email)
|
||||
|
||||
def register(self, notify:bool=False):
|
||||
self.generate_id()
|
||||
users = User.query.all()
|
||||
for user in users:
|
||||
if user.get_username() == self.get_username(): return False, f'Username {self.get_username()} already in use.'
|
||||
@ -88,17 +90,19 @@ class User(UserMixin, db.Model):
|
||||
self.reset_token = self.verification_token = None
|
||||
db.session.commit()
|
||||
|
||||
def delete(self):
|
||||
def delete(self, notify:bool=False):
|
||||
username = self.get_username()
|
||||
db.session.delete(self)
|
||||
db.session.commit()
|
||||
write('users.log', f'User \'{username}\' was deleted by \'{current_user.get_username()}\'.')
|
||||
message = f'User \'{username}\' was deleted by \'{current_user.get_username()}\'.'
|
||||
write('users.log', message)
|
||||
return True, message
|
||||
|
||||
def update(self, password:str=None, email:str=None):
|
||||
def update(self, password:str=None, email:str=None, notify:bool=False):
|
||||
if not password and not email: return False, jsonify({'error': 'There were no changes requested.'})
|
||||
if password: self.set_password(password)
|
||||
if email: self.set_email(email)
|
||||
db.session.commit()
|
||||
message = f'Information for user {self.get_username()} has been updated by {current_user.get_username()}.'
|
||||
write('system.log', message)
|
||||
return True, jsonify({'success': message})
|
||||
return True, message
|
||||
|
1
ref-test/app/templates/privacy.html
Normal file
1
ref-test/app/templates/privacy.html
Normal file
@ -0,0 +1 @@
|
||||
<h1>Privacy Policy</h1>
|
@ -1,5 +1,7 @@
|
||||
from ..data import data as data_dir
|
||||
|
||||
import json
|
||||
from random import shuffle
|
||||
|
||||
def load(filename:str):
|
||||
with open(f'./{data_dir}/{filename}') as file:
|
||||
@ -8,3 +10,25 @@ def load(filename:str):
|
||||
def save(data:dict, filename:str):
|
||||
with open(f'./{data_dir}/{filename}', 'w') as file:
|
||||
json.dump(data, file, indent=4)
|
||||
|
||||
def check_is_json(file):
|
||||
if not '.' in file.filename or not file.filename.rsplit('.',1)[-1] == 'json': return False
|
||||
return True
|
||||
|
||||
def validate_json(file):
|
||||
file.stream.seek(0)
|
||||
data = json.loads(file.read())
|
||||
if not type(data) is list: return False
|
||||
|
||||
def randomise_list(list:list):
|
||||
_list = list.copy()
|
||||
shuffle(_list)
|
||||
return(_list)
|
||||
|
||||
def get_tag_list(dataset:list):
|
||||
output = []
|
||||
for block in dataset:
|
||||
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']))
|
||||
return output
|
@ -34,3 +34,12 @@ def value(min:int=0, max:int=None):
|
||||
if value < min or max != None and value > max:
|
||||
raise ValidationError(message)
|
||||
return length
|
||||
|
||||
def get_time_options():
|
||||
time_options = [
|
||||
('none', 'None'),
|
||||
('60', '1 hour'),
|
||||
('90', '1 hour 30 minutes'),
|
||||
('120', '2 hours')
|
||||
]
|
||||
return time_options
|
@ -1,2 +1,104 @@
|
||||
from .data import randomise_list
|
||||
|
||||
def parse_test_code(code):
|
||||
return code.replace('—', '').lower()
|
||||
|
||||
def generate_questions(dataset:list):
|
||||
output = []
|
||||
for block in randomise_list(dataset):
|
||||
if block['type'] == 'question':
|
||||
question = {
|
||||
'type': 'question',
|
||||
'q_no': block['q_no'],
|
||||
'question_header': '',
|
||||
'text': block['text']
|
||||
}
|
||||
if block['q_type'] == 'Multiple Choice': question['options'] = randomise_list([*enumerate(block['options'])])
|
||||
else: question['options'] = block['options'].copy()
|
||||
output.append(question)
|
||||
elif block['type'] == 'block':
|
||||
for key, _question in enumerate(randomise_list(block['questions'])):
|
||||
question = {
|
||||
'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']
|
||||
}
|
||||
if _question['q_type'] == 'Multiple Choice': question['options'] = randomise_list([*enumerate(_question['options'])])
|
||||
else: question['options'] = _question['options'].copy()
|
||||
output.append(question)
|
||||
return output
|
||||
|
||||
def evaluate_answers(answers:dict, key:list):
|
||||
score = 0
|
||||
max = 0
|
||||
tags = {}
|
||||
for block in key:
|
||||
if block['type'] == 'question':
|
||||
max += 1
|
||||
q_no = block['q_no']
|
||||
if str(q_no) in answers:
|
||||
submitted_answer = int(answers[str(q_no)])
|
||||
if submitted_answer == block['correct']:
|
||||
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
|
||||
elif block['type'] == 'block':
|
||||
for question in block['questions']:
|
||||
max += 1
|
||||
q_no = question['q_no']
|
||||
if str(q_no) in answers:
|
||||
submitted_answer = int(answers[str(q_no)])
|
||||
if submitted_answer == question['correct']:
|
||||
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_correct_answers(dataset:list):
|
||||
output = {}
|
||||
for block in dataset:
|
||||
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
|
12
ref-test/app/views.py
Normal file
12
ref-test/app/views.py
Normal file
@ -0,0 +1,12 @@
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
views = Blueprint(
|
||||
name='common',
|
||||
import_name=__name__,
|
||||
template_folder='templates',
|
||||
static_folder='static'
|
||||
)
|
||||
|
||||
@views.route('/privacy/')
|
||||
def _privacy():
|
||||
return render_template('privacy.html')
|
@ -1,10 +0,0 @@
|
||||
from flask import Blueprint
|
||||
|
||||
views = Blueprint(
|
||||
name='common',
|
||||
import_name=__name__,
|
||||
template_folder='templates',
|
||||
static_folder='static'
|
||||
)
|
||||
|
||||
from . import privacy
|
@ -1,5 +0,0 @@
|
||||
from . import views
|
||||
|
||||
@views.route('/privacy/')
|
||||
def _privacy():
|
||||
return 'Privacy Policy'
|
Loading…
Reference in New Issue
Block a user