-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
104 changed files
with
25,242 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import os | ||
|
||
from flask import Flask | ||
from flask_wtf.csrf import CSRFProtect | ||
|
||
def create_app(test_config=None): | ||
# create and configure the app | ||
app = Flask(__name__, instance_relative_config=True) | ||
app.config.from_mapping( | ||
SECRET_KEY='dev', | ||
DATABASE=os.path.join(app.instance_path, 'quiz.sqlite'), | ||
) | ||
|
||
if test_config is None: | ||
# load the instance config, if it exists, when not testing | ||
app.config.from_pyfile('config.py', silent=True) | ||
else: | ||
# load the test config if passed in | ||
app.config.from_mapping(test_config) | ||
|
||
# ensure the instance folder exists | ||
try: | ||
os.makedirs(app.instance_path) | ||
except OSError: | ||
pass | ||
|
||
csrf = CSRFProtect(app) | ||
|
||
from . import db | ||
db.init_app(app) | ||
|
||
from . import auth | ||
app.register_blueprint(auth.bp) | ||
|
||
from . import quizzes | ||
app.register_blueprint(quizzes.bp) | ||
app.add_url_rule('/', endpoint='index') | ||
app.add_url_rule('/create', endpoint='create') | ||
|
||
from . import admin | ||
app.register_blueprint(admin.bp) | ||
app.add_url_rule('/admin/', endpoint='index') | ||
|
||
from . import admin_quizzes | ||
app.register_blueprint(admin_quizzes.bp) | ||
app.add_url_rule('/admin/quiz/', endpoint='index') | ||
|
||
from . import admin_grades | ||
app.register_blueprint(admin_grades.bp) | ||
app.add_url_rule('/admin/grades/', endpoint='grades') | ||
|
||
from . import admin_results | ||
app.register_blueprint(admin_results.bp) | ||
app.add_url_rule('/admin/results/', endpoint='index') | ||
|
||
return app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
import functools, time, logging, sys, pickle | ||
|
||
from flask import ( | ||
Blueprint, flash, g, redirect, render_template, request, session, url_for | ||
) | ||
from werkzeug.security import check_password_hash, generate_password_hash | ||
|
||
from werkzeug.exceptions import abort | ||
|
||
from quiz.auth import login_required, admin_login_required | ||
from quiz.db import get_db | ||
|
||
|
||
# See: https://stackoverflow.com/questions/54444776/how-can-i-break-up-a-flask-blueprint-into-multiple-files-within-a-subfolder answers to breakdown in subfolders | ||
|
||
bp = Blueprint('admin', __name__) | ||
|
||
@bp.route('/admin/') | ||
@admin_login_required | ||
def index(): | ||
db = get_db() | ||
quizzes = db.execute( | ||
"""SELECT tE.*, COUNT(DISTINCT tQ.id) as cnt_questions, COUNT(DISTINCT tT.id) as cnt_takings | ||
FROM quizzes tE LEFT JOIN questions tQ ON tQ.quiz_id=tE.id | ||
LEFT JOIN takings tT ON tT.quiz_id = tE.id | ||
GROUP BY tE.id | ||
ORDER BY tE.name""" | ||
).fetchall() | ||
|
||
return render_template('admin/index.html', quizzes = quizzes) | ||
|
||
# Manage questions | ||
@bp.route('/admin/questions/<int:quiz_id>', methods=('GET', 'POST')) | ||
@admin_login_required | ||
def questions(quiz_id): | ||
db = get_db() | ||
# select quiz | ||
quiz = db.execute( | ||
'SELECT * FROM quizzes WHERE id=?', | ||
(quiz_id,) | ||
).fetchone() | ||
|
||
if quiz is None: | ||
return "No such quiz" | ||
|
||
# delete question | ||
if request.args.get('del'): | ||
question_id = request.args.get('del') | ||
|
||
db.execute( | ||
"""DELETE FROM answers WHERE question_id=?""", | ||
(question_id,) | ||
) | ||
db.commit() | ||
|
||
db.execute( | ||
"""DELETE FROM questions WHERE id=?""", | ||
(question_id,) | ||
) | ||
db.commit() | ||
return redirect(url_for('admin.questions', quiz_id = quiz_id)) | ||
|
||
# select questions | ||
questions = db.execute( | ||
"""SELECT tQ.*, COUNT(tA.id) as cnt_answers | ||
FROM questions tQ LEFT JOIN answers tA ON tA.question_id = tQ.id | ||
WHERE tQ.quiz_id=? GROUP BY tQ.id ORDER BY tQ.sort_order, tQ.id""", | ||
(quiz_id,) | ||
).fetchall() | ||
|
||
return render_template('admin/questions.html', quiz = quiz, questions = questions, cls = '') | ||
# end questions | ||
|
||
# add / edit question | ||
@bp.route('/admin/question/<int:quiz_id>/<int:id>', methods=('GET', 'POST')) | ||
@bp.route('/admin/question/<int:quiz_id>', methods=('GET', 'POST')) | ||
def question(quiz_id, id = 0): | ||
db = get_db() | ||
|
||
quiz = db.execute( | ||
'SELECT * FROM quizzes WHERE id=?', | ||
(quiz_id,) | ||
).fetchone() | ||
|
||
# add/save question | ||
if request.method == 'POST': | ||
# prepare variables | ||
question = request.form['question'] or '' | ||
answer_type = request.form['answer_type'] or 'radio' | ||
is_required = 1 if "is_required" in request.form else 0 | ||
is_inactive = 1 if "is_inacitve" in request.form else 0 | ||
is_survey = 1 if "is_survey" in request.form else 0 | ||
feedback = request.form['feedback'] or '' | ||
num_columns = 1 #temp | ||
|
||
if id: | ||
db.execute( | ||
"""UPDATE questions | ||
SET question=?, answer_type=?, sort_order=?, is_required=?, feedback=?, is_inactive=?, is_survey=?, num_columns=? | ||
WHERE id=?""", | ||
(question, answer_type, 1, is_required, feedback, is_inactive, is_survey, num_columns, id,) | ||
) | ||
db.commit() | ||
|
||
# update or delete existing answers | ||
answers = db.execute( | ||
"""SELECT id FROM answers WHERE question_id=?""", | ||
(id,) | ||
).fetchall() | ||
|
||
for answer in answers: | ||
ans_text = request.form['answer_' + str(answer['id'])] | ||
# print(str(answer['id']) + ": " + ans_text, file=sys.stderr) | ||
if ans_text == '': | ||
#print("delete " + str(answer['id']) + ": " + ans_text, file=sys.stderr) | ||
# delete answer | ||
db.execute( | ||
"""DELETE FROM answers WHERE id=?""", | ||
(answer['id'],) | ||
) | ||
db.commit() | ||
else: | ||
# update | ||
correct = 0 if "correct_"+str(answer['id']) not in request.form else 1 | ||
points = request.form['points_' + str(answer['id'])] | ||
|
||
if ans_text != '': | ||
# print("insert " + ans_text, file=sys.stderr) | ||
db.execute(""" UPDATE answers SET answer=?, correct=?, points=?, sort_order=1 WHERE id=?""", | ||
(ans_text, correct, points, answer['id']) | ||
) | ||
db.commit() | ||
|
||
question_id = id | ||
else: | ||
db.execute( | ||
"""INSERT INTO questions (quiz_id, question, answer_type, sort_order, is_required, feedback, is_inactive, is_survey, num_columns) | ||
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)""", | ||
(quiz_id, question, answer_type, 1, is_required, feedback, is_inactive, is_survey, num_columns) | ||
) | ||
db.commit() | ||
|
||
question_id = db.execute('SELECT last_insert_rowid() as last_id').fetchone() | ||
question_id = question_id['last_id'] | ||
|
||
# add new answer if any | ||
for answer, points, correct in zip(request.form.getlist('answer'), | ||
request.form.getlist('points'), | ||
request.form.getlist('points')): | ||
|
||
if answer != '': | ||
db.execute( | ||
"""INSERT INTO answers (question_id, answer, correct, points, sort_order) | ||
VALUES (?, ?, ?, ?, ?)""", | ||
(question_id, answer, correct, points, 1) | ||
) | ||
db.commit() | ||
|
||
return redirect(url_for('admin.questions', quiz_id = quiz_id)) | ||
if id: | ||
question = db.execute( | ||
'SELECT * FROM questions WHERE id=?', | ||
(id, ) | ||
).fetchone() | ||
|
||
answers = db.execute( | ||
"""SELECT * FROM answers WHERE question_id=?""", | ||
(id,) | ||
).fetchall() | ||
count_answers = len(answers) | ||
else: | ||
question = None | ||
answers = None | ||
count_answers = 0 | ||
|
||
return render_template('admin/question.html', quiz = quiz, question = question, answers=answers, count_answers = count_answers) | ||
|
||
# global settings | ||
@bp.route('/admin/settings', methods=('GET', 'POST')) | ||
@admin_login_required | ||
def settings(): | ||
db = get_db() | ||
if request.form: | ||
# email sender | ||
db.execute("INSERT OR REPLACE INTO options (option_key, option_value) VALUES ('email_sender',?)", (request.form['sender'],)) | ||
db.commit() | ||
|
||
db.execute("INSERT OR REPLACE INTO options (option_key, option_value) VALUES ('email_sender_name',?)", (request.form['sender_name'],)) | ||
db.commit() | ||
|
||
# email config as serialized dict | ||
email_config = { | ||
'server' : request.form['mail_server'], | ||
'port' : request.form['mail_port'], | ||
'username' : request.form['mail_username'], | ||
'pass' : request.form['mail_pass'], | ||
'use_tls' : 1 if 'mail_use_tls' in request.form else 0, | ||
'use_ssl' : 1 if 'mail_use_ssl' in request.form else 0, | ||
} | ||
email_config = pickle.dumps(email_config) | ||
db.execute("INSERT OR REPLACE INTO options (option_key, option_value) VALUES ('email_config',?)", (email_config,)) | ||
db.commit() | ||
|
||
sender = db.execute("SELECT option_value FROM options WHERE option_key = 'email_sender' ").fetchone() | ||
sender_name = db.execute("SELECT option_value FROM options WHERE option_key = 'email_sender_name' ").fetchone() | ||
|
||
email_config = db.execute("SELECT option_value FROM options WHERE option_key = 'email_config' ").fetchone() | ||
email_config = pickle.loads(email_config['option_value']) if email_config else None | ||
return render_template('admin/settings.html', sender = sender, sender_name = sender_name, email_config = email_config) | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import functools, time, logging, sys | ||
|
||
from flask import ( | ||
Blueprint, flash, g, redirect, render_template, request, session, url_for | ||
) | ||
from werkzeug.security import check_password_hash, generate_password_hash | ||
|
||
from werkzeug.exceptions import abort | ||
|
||
from inspect import getmembers | ||
from pprint import pprint | ||
|
||
from quiz.auth import login_required, admin_login_required | ||
from quiz.db import get_db | ||
from quiz.helpers import * | ||
|
||
# See: https://stackoverflow.com/questions/54444776/how-can-i-break-up-a-flask-blueprint-into-multiple-files-within-a-subfolder answers to breakdown in subfolders | ||
|
||
bp = Blueprint('admin_grades', __name__) | ||
|
||
@bp.route('/admin/grades/<int:quiz_id>/', methods=('GET', 'POST')) | ||
@admin_login_required | ||
def grades(quiz_id ): | ||
db = get_db() | ||
|
||
quiz = db.execute( | ||
"""SELECT tE.*, COUNT(tQ.id) as cnt_questions, COUNT(tT.id) as cnt_takings | ||
FROM quizzes tE LEFT JOIN questions tQ ON tE.id = tQ.quiz_id | ||
LEFT JOIN takings tT ON tE.id = tT.quiz_id | ||
WHERE tE.id=?""", | ||
(quiz_id,) | ||
).fetchone() | ||
|
||
#print(quiz, file=sys.stderr) | ||
|
||
if quiz['id'] is None: | ||
return abort(404) | ||
|
||
|
||
if request.method == 'POST': | ||
title = request.form['title'] or '' | ||
description = request.form['description'] or '' | ||
gfrom = to_float(request.form['gfrom']) or 0 | ||
gto = to_float(request.form['gto']) or 0 | ||
redirect_url = request.form['redirect_url'] or '' | ||
|
||
# add grade | ||
if 'add' in request.form and request.form['add']: | ||
db.execute( | ||
"""INSERT INTO grades (quiz_id, title, description, gfrom, gto, redirect_url) | ||
VALUES (?, ?, ?, ?, ?, ?)""", | ||
(quiz_id, title, description, gfrom, gto, redirect_url) | ||
) | ||
db.commit() | ||
return redirect(url_for('admin_grades.grades', quiz_id=quiz_id)) | ||
|
||
# delete grade | ||
if 'del' in request.form and request.form['del'] and request.form['id']: | ||
db.execute('DELETE FROM grades WHERE id=?', (request.form['id'] )) | ||
db.commit() | ||
return redirect(url_for('admin_grades.grades', quiz_id=quiz_id)) | ||
|
||
# edit grade | ||
if 'save' in request.form and request.form['save'] and request.form['id']: | ||
db.execute("""UPDATE grades SET title=?, description=?, gfrom=?, gto=?, redirect_url=? WHERE id=?""", | ||
(title, description, gfrom, gto, redirect_url, request.form['id']) | ||
) | ||
db.commit() | ||
|
||
|
||
# select existing grades | ||
grades = db. execute( | ||
"""SELECT * FROM grades WHERE quiz_id=? ORDER BY id""", | ||
(quiz_id,) | ||
).fetchall() | ||
|
||
return render_template('admin/grades.html', quiz = quiz, grades = grades) |
Oops, something went wrong.