changed database structure

This commit is contained in:
Ville Rantanen
2018-12-05 10:47:58 +02:00
parent 2910cc2186
commit 59c371cd79
5 changed files with 108 additions and 112 deletions

24
abot.py
View File

@@ -6,19 +6,14 @@ from revprox import ReverseProxied
from utils import * from utils import *
import manager import manager
DATABASE = 'abot.sqlite' DATABASE = 'abot.sqlite' # database file
DEBUG = True DEBUG = True
SECRET_KEY = 'otwet6oi539iosf'
QUESTIONS = 'questions' # path to questions QUESTIONS = 'questions' # path to questions
# create our little application :)
app = Flask(__name__) app = Flask(__name__)
app.config.from_object(__name__) app.config.from_object(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app) app.wsgi_app = ReverseProxied(app.wsgi_app)
def connect_db():
return sqlite3.connect(app.config['DATABASE'])
@app.before_request @app.before_request
def before_request(): def before_request():
g.db = connect_db() g.db = connect_db()
@@ -78,15 +73,13 @@ def save_vote():
form = parse_form(key) form = parse_form(key)
if not form: if not form:
return render_template('blank.html', message = "Error creating form") return render_template('blank.html', message = "Error creating form")
create_voter_table(g.db, key)
if is_draft(form): if is_draft(form):
return render_template('blank.html', message = "Not published") return render_template('blank.html', message = "Not published")
if is_expired(form): if is_expired(form):
return render_template('blank.html', message = "Voting has closed") return render_template('blank.html', message = "Voting has closed")
if is_closed_vote(form): #~ if is_closed_vote(form):
if has_voted(key, token): #~ if has_voted(key, token):
return render_template('blank.html', message = "Token already used") #~ return render_template('blank.html', message = "Token already used")
create_result_table(key)
write_vote(key, token, request.form, form) # using request. write_vote(key, token, request.form, form) # using request.
tokens = False tokens = False
@@ -95,16 +88,17 @@ def save_vote():
answers = [] answers = []
if is_show_results(form): if is_show_results(form):
summary = True summary = True
questions, answers = get_summary(g.db, key) questions, answers = sort_summary(*get_summary(g.db, key))
tokens = get_token_counts(g.db, key) tokens = get_token_counts(g.db, key)
print(answers)
return render_template( return render_template(
'thank_you.html', 'thank_you.html',
summary = summary, summary = summary,
tokens = tokens, tokens = tokens,
questions = questions, qa = zip(questions, answers)
answers = answers
) )
create_db(DATABASE)
if __name__ == "__main__": if __name__ == "__main__":
manager.main(DATABASE, QUESTIONS) manager.main(DATABASE, QUESTIONS)

View File

@@ -11,17 +11,18 @@ import sys
def insert_token(db, name, token): def insert_token(db, name, token):
table_name = get_voter_table_name(name)
cur = db.cursor() cur = db.cursor()
cur.execute(""" cur.execute("""
INSERT INTO `%s` VALUES ( INSERT INTO tokens (token, question_set, answered) VALUES (
?,
?, ?,
'false' 'false'
); );
"""%( table_name, ), """,
( (
token, token,
name
) )
) )
db.commit() db.commit()
@@ -31,7 +32,6 @@ def add_token(options):
if not is_key(options.name, options): if not is_key(options.name, options):
raise Exception("%s does not exist, or is not a valid question set name"%( options.name, )) raise Exception("%s does not exist, or is not a valid question set name"%( options.name, ))
db = open_db(options.db) db = open_db(options.db)
create_voter_table(db, options.name)
for i in range(options.number): for i in range(options.number):
N = 32 N = 32
token = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N)) token = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))
@@ -42,6 +42,7 @@ def add_token(options):
token token
)) ))
def open_db(db): def open_db(db):
return sqlite3.connect(db) return sqlite3.connect(db)
@@ -98,22 +99,29 @@ def parse_options(database, questions):
help = "Name of the question set" help = "Name of the question set"
) )
## clear ## clear
parser_clear = subparsers.add_parser('clear', help = "Delete results") parser_clear_votes = subparsers.add_parser('clear_votes', help = "Delete results")
parser_clear.add_argument( parser_clear_votes.add_argument(
'--really', '--really',
action="store_true", action="store_true",
dest="really", dest="really",
default = False, default = False,
help = "Really delete results for the vote" help = "Really delete results for the vote"
) )
parser_clear.add_argument( parser_clear_votes.add_argument(
'--tokens', dest = "name",
action="store_true", help = "Name of the question set"
dest="tokens",
default = False,
help = "Delete tokens too"
) )
parser_clear.add_argument(
## clear tokens
parser_clear_tokens = subparsers.add_parser('clear_tokens', help = "Delete tokens")
parser_clear_tokens.add_argument(
'--really',
action="store_true",
dest="really",
default = False,
help = "Really delete tokens for the vote"
)
parser_clear_tokens.add_argument(
dest = "name", dest = "name",
help = "Name of the question set" help = "Name of the question set"
) )
@@ -141,7 +149,7 @@ def summary(options):
questions, answers = get_summary(db, options.name) questions, answers = get_summary(db, options.name)
tokens = get_token_counts(db, options.name) tokens = get_token_counts(db, options.name)
if options.tsv: if hasattr(options, 'tsv') and options.tsv:
out = summary_tsv(questions, answers, tokens) out = summary_tsv(questions, answers, tokens)
else: else:
out = summary_list(questions, answers, tokens) out = summary_list(questions, answers, tokens)
@@ -203,29 +211,35 @@ def summary_tsv(questions, answers, tokens):
def clear_votes(options): def clear_votes(options):
if not is_key(options.name, options): try:
raise Exception("%s does not exist, or is not a valid question set name"%( options.name, )) summary(options)
summary(options) except Exception as err:
print("\nQuestions no longer available")
if not options.really: if not options.really:
print("\nNot really deleting results") print("\nNot really deleting results. use --really")
sys.exit(0) sys.exit(0)
db = open_db(options.db) db = open_db(options.db)
cur = db.cursor() cur = db.cursor()
cur.execute( cur.execute(
"DROP TABLE IF EXISTS `%s`"%( options.name, ) "DELETE FROM answers WHERE question_set = ?",
( options.name, )
) )
db.commit() db.commit()
print("\nDeleted votes for %s"%( options.name, )) print("\nDeleted votes for %s"%( options.name, ))
def clear_tokens(options): def clear_tokens(options):
if not is_key(options.name, options): db = open_db(options.db)
raise Exception("%s does not exist, or is not a valid question set name"%( options.name, )) print(get_token_counts(db, options.name))
if not options.really:
print("\nNot really deleting tokens. use --really")
sys.exit(0)
db = open_db(options.db) db = open_db(options.db)
cur = db.cursor() cur = db.cursor()
cur.execute( cur.execute(
"DROP TABLE IF EXISTS `%s`"%( get_voter_table_name(options.name,) ) "DELETE FROM tokens WHERE question_set = ?",
( options.name, )
) )
db.commit() db.commit()
print("\nDeleted tokens for %s"%( options.name, )) print("\nDeleted tokens for %s"%( options.name, ))
@@ -238,8 +252,8 @@ def main(database, questions):
add_token(options) add_token(options)
if options.subparser_name == "summary": if options.subparser_name == "summary":
summary(options) summary(options)
if options.subparser_name == "clear": if options.subparser_name == "clear_votes":
clear_votes(options) clear_votes(options)
if options.tokens: if options.subparser_name == "clear_tokens":
clear_tokens(options) clear_tokens(options)

View File

@@ -9,7 +9,7 @@ draft: false
# Create tokens with `./manager token multi_question` # Create tokens with `./manager token multi_question`
vote_style: closed vote_style: closed
# By default voters can not see the results # By default voters can not see the results
show_results: false show_results: true
# HTML can be added inline. The line needs to begin with "<" # HTML can be added inline. The line needs to begin with "<"

View File

@@ -8,20 +8,19 @@ Thank you for the vote!
<h3>Current report</h3> <h3>Current report</h3>
<div id="questions"> <div id="questions">
{% for question in questions %} {% for qa_item in qa %}
<div class = "question autoformat"> <div class = "question autoformat">
<h2>{{ question|safe }}</h2> <h2>{{ qa_item[0]|safe }}</h2>
<ul> <ul>
{% for choice in answers[question].answers %} {% for answer in qa_item[1] %}
{% if answers[question].answer_type == "single" %} {% if answer.answer_type == "single" %}
<li class = "summary_single">{{ choice }}: {{ answers[question].answers[choice] }} <li class = "summary_single">{{ answer.answer }}: {{ answer.count }} ({{ answer.percent }}%)
{% endif %} {% endif %}
{% if answers[question].answer_type == "multiple" %} {% if answer.answer_type == "multiple" %}
<li class = "summary_multiple">{{ choice }}: {{ answers[question].answers[choice] }} <li class = "summary_multiple">{{ answer.answer }}: {{ answer.count }} ({{ answer.percent }}%)
{% endif %} {% endif %}
{% if answers[question].answer_type == "open" %} {% if answer.answer_type == "open" %}
<li class = "summary_open">"{{ choice }}" <li class = "summary_open">"{{ answer.answer }}"
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</ul> </ul>

111
utils.py
View File

@@ -3,65 +3,54 @@ from flask import current_app as app
from flask import g from flask import g
import os import os
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
import html
import sqlite3
def create_result_table(key): def connect_db():
cur = g.db.cursor() return sqlite3.connect(app.config['DATABASE'])
cur.execute("""
CREATE TABLE IF NOT EXISTS `%s` (
question TEXT,
answer TEXT,
answer_type TEXT
);
"""%(key, )
)
g.db.commit()
def create_voter_table(db, name): def create_db(db_file):
table_name = get_voter_table_name(name) db = sqlite3.connect(db_file)
cur = db.cursor() cur = db.cursor()
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS `%s` ( CREATE TABLE IF NOT EXISTS answers (
token TEXT PRIMARY KEY, question_set TEXT,
answered BOOLEAN question TEXT,
answer TEXT,
answer_type TEXT
); );
"""%(table_name, ) """)
cur.execute("""
CREATE TABLE IF NOT EXISTS tokens (
token TEXT PRIMARY KEY,
question_set TEXT,
answered BOOLEAN
);
"""
) )
db.commit() db.commit()
def get_voter_table_name(key):
return key + "__voters"
def get_result_table_name(key):
return key
def get_token_counts(db, key): def get_token_counts(db, key):
cur = db.cursor() cur = db.cursor()
token_table = get_voter_table_name(key)
cur.execute( cur.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name=?;", "SELECT count(*) FROM tokens WHERE answered = 'true' and question_set = ?",
( token_table, ) (
key,
)
) )
matching_tables = cur.fetchall() used_tokens = cur.fetchall()[0][0]
if len(matching_tables) == 0: cur.execute(
used_tokens = 0 "SELECT count(*) FROM tokens WHERE answered = 'false' and question_set = ?",
unused_tokens = 0 (
else: key,
cur.execute(
"SELECT count(*) FROM `%s` WHERE answered = 'true'"%(
token_table,
)
) )
used_tokens = cur.fetchall()[0][0] )
cur.execute( unused_tokens = cur.fetchall()[0][0]
"SELECT count(*) FROM `%s` WHERE answered = 'false'"%(
token_table,
)
)
unused_tokens = cur.fetchall()[0][0]
tokens = { tokens = {
'unused': unused_tokens, 'unused': unused_tokens,
'used': used_tokens, 'used': used_tokens,
@@ -76,15 +65,8 @@ def get_summary(db, key):
cur = db.cursor() cur = db.cursor()
cur.execute( cur.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name=?;", "SELECT question, answer, answer_type FROM answers WHERE question_set = ?",
( key, ) (
)
matching_tables = cur.fetchall()
if len(matching_tables) == 0:
return questions, answers
cur.execute(
"SELECT question, answer, answer_type FROM `%s`"%(
key, key,
) )
) )
@@ -108,11 +90,10 @@ def has_voted(key, token):
return True return True
cur = g.db.cursor() cur = g.db.cursor()
cur.execute( cur.execute(
"SELECT token FROM %s WHERE token = ? AND answered = 'true'"%( "SELECT token FROM tokens WHERE token = ? AND answered = 'true' AND question_set = ?",
get_voter_table_name(key),
),
( (
token, token,
key
) )
) )
return len(cur.fetchall()) > 0 return len(cur.fetchall()) > 0
@@ -252,26 +233,34 @@ def write_vote(key, token, answers, form):
continue continue
for single in answer: for single in answer:
cur.execute( cur.execute(
"INSERT INTO `%s` VALUES (?, ?, ?)"%( "INSERT INTO answers (question_set, question, answer, answer_type) VALUES (?, ?, ?, ?)",
key,
),
( (
key,
question['name'], question['name'],
single.strip(), html.escape(single).strip(),
answer_type answer_type
) )
) )
if is_closed_vote(form): if is_closed_vote(form):
cur.execute( cur.execute(
"UPDATE %s SET answered = 'true' WHERE token = ?"%( "UPDATE tokens SET answered = 'true' WHERE token = ? AND question_set = ?",
get_voter_table_name(key),
),
( (
token, token,
key
) )
) )
g.db.commit() g.db.commit()
def sort_summary(questions, answers):
sorted_answer_list = []
for q,a in zip(questions, answers):
sum_answers = sum([answers[q]['answers'][x] for x in answers[q]['answers']])
sorted_answers = sorted(
[{"answer_type": answers[q]['answer_type'] , "answer": x, "count": answers[q]['answers'][x], "percent": int(100 * float(answers[q]['answers'][x]) / sum_answers)} for x in answers[q]['answers']],
key = lambda i: -i['count']
)
sorted_answer_list.append(sorted_answers)
return questions, sorted_answer_list
def time_to_expiry(form): def time_to_expiry(form):
if form['expires'] == None: if form['expires'] == None: