inline HTML, result viewer
This commit is contained in:
17
abot.py
17
abot.py
@@ -89,11 +89,22 @@ def save_vote():
|
||||
create_result_table(key)
|
||||
|
||||
write_vote(key, token, request.form, form) # using request.
|
||||
summary = ''
|
||||
tokens = False
|
||||
summary = False
|
||||
questions = []
|
||||
answers = []
|
||||
if is_show_results(form):
|
||||
summary = get_html_summary(key)
|
||||
summary = True
|
||||
questions, answers = get_summary(g.db, key)
|
||||
tokens = get_token_counts(g.db, key)
|
||||
|
||||
return render_template('thank_you.html', summary = summary)
|
||||
return render_template(
|
||||
'thank_you.html',
|
||||
summary = summary,
|
||||
tokens = tokens,
|
||||
questions = questions,
|
||||
answers = answers
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
manager.main(DATABASE, QUESTIONS)
|
||||
|
||||
70
manager.py
70
manager.py
@@ -135,77 +135,17 @@ def list_question_sets(options):
|
||||
|
||||
|
||||
def summary(options):
|
||||
print(get_summary(options))
|
||||
|
||||
|
||||
def get_summary(options):
|
||||
if not is_key(options.name, options):
|
||||
raise Exception("%s does not exist, or is not a valid question set name"%( options.name, ))
|
||||
db = open_db(options.db)
|
||||
cur = db.cursor()
|
||||
questions, answers = get_summary(db, options.name)
|
||||
tokens = get_token_counts(db, options.name)
|
||||
|
||||
cur.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name=?;",
|
||||
( options.name, )
|
||||
)
|
||||
matching_tables = cur.fetchall()
|
||||
if len(matching_tables) == 0:
|
||||
return "No votes yet"
|
||||
|
||||
token_table = get_voter_table_name(options.name)
|
||||
cur.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name=?;",
|
||||
( token_table, )
|
||||
)
|
||||
matching_tables = cur.fetchall()
|
||||
if len(matching_tables) == 0:
|
||||
used_tokens = 0
|
||||
unused_tokens = 0
|
||||
else:
|
||||
cur.execute(
|
||||
"SELECT count(*) FROM `%s` WHERE answered = 'true'"%(
|
||||
token_table,
|
||||
)
|
||||
)
|
||||
used_tokens = cur.fetchall()[0][0]
|
||||
cur.execute(
|
||||
"SELECT count(*) FROM `%s` WHERE answered = 'false'"%(
|
||||
token_table,
|
||||
)
|
||||
)
|
||||
unused_tokens = cur.fetchall()[0][0]
|
||||
tokens = {
|
||||
'unused': unused_tokens,
|
||||
'used': used_tokens,
|
||||
'total': used_tokens + unused_tokens
|
||||
}
|
||||
|
||||
questions = []
|
||||
answers = {}
|
||||
cur.execute(
|
||||
"SELECT question, answer, answer_type FROM `%s`"%(
|
||||
options.name,
|
||||
)
|
||||
)
|
||||
for row in cur:
|
||||
if row[0] not in answers.keys():
|
||||
questions.append(row[0])
|
||||
answers[row[0]] = {
|
||||
'answers': {},
|
||||
'answer_type': row[2]
|
||||
}
|
||||
|
||||
if row[1] not in answers[row[0]]['answers'].keys():
|
||||
answers[row[0]]['answers'][row[1]] = 0
|
||||
answers[row[0]]['answers'][row[1]] += 1
|
||||
|
||||
try:
|
||||
if options.tsv:
|
||||
return summary_tsv(questions, answers, tokens)
|
||||
out = summary_tsv(questions, answers, tokens)
|
||||
else:
|
||||
return summary_list(questions, answers, tokens)
|
||||
except AttributeError:
|
||||
return summary_list(questions, answers, tokens)
|
||||
out = summary_list(questions, answers, tokens)
|
||||
print(out)
|
||||
|
||||
def summary_list(questions, answers, tokens):
|
||||
s = """# Tokens for this question set:
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
|
||||
# expiry format: YYYY-MM-DD HH:MM +z
|
||||
# z is the difference to UTC in HHMM, +0000, -0700, etc..
|
||||
|
||||
expires: 2028-12-12 21:20 +0200
|
||||
# if "draft: true" voting is not possible. you can preview the form with address /preview/example
|
||||
|
||||
# if "draft: true" voting is not possible. you can preview the form with address "/preview/example"
|
||||
draft: false
|
||||
# "open" vote style is open for anyone without tokens. "closed" requires tokens to be generated
|
||||
# Create tokens with `./manager token multi_question`
|
||||
vote_style: closed
|
||||
# By default voters can not see the results
|
||||
show_results: false
|
||||
|
||||
|
||||
# HTML can be added inline. The line needs to begin with "<"
|
||||
# HTML added like this is technically a question without answer choices
|
||||
<H1>Title of vote page</H1>
|
||||
<img src="https://upload.wikimedia.org/wikipedia/en/thumb/7/7d/Lenna_%28test_image%29.png/220px-Lenna_%28test_image%29.png"/>
|
||||
|
||||
# - character is single choice question
|
||||
It works?
|
||||
- yes
|
||||
- no
|
||||
|
||||
# If a question is in HTML tags, it's not formatted
|
||||
<p>Fruits</p>
|
||||
# + character is multi choice question
|
||||
Fruits
|
||||
+ banana
|
||||
+ orange
|
||||
+ tomato
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
|
||||
# if "true" voting is not possible. you can preview the form in address /preview/example
|
||||
draft: false
|
||||
# "open" vote style is open for anyone without tokens. "closed" requires tokens to be generated
|
||||
vote_style: open
|
||||
# By default voters can not see the results
|
||||
show_results: true
|
||||
|
||||
# Questions are any line that doesnt match configuration commands
|
||||
It works?
|
||||
# single choice answers are denoted with dashes
|
||||
- yes
|
||||
- no
|
||||
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
body { font-family: sans-serif; background: #eee; }
|
||||
a, h1, h2 { color: #377ba8; }
|
||||
h1, h2 { font-family: 'Georgia', serif; margin: 0; }
|
||||
h1, h2 { font-family: 'Georgia', serif; margin: 0; margin-top: 0.2em; }
|
||||
h1 { border-bottom: 2px solid #eee; }
|
||||
h2 { font-size: 1.2em; }
|
||||
input { margin-top: 0.5em; border: 1px solid gray;}
|
||||
|
||||
.page { margin: 2em auto; width: 90%; border: 5px solid #ccc;
|
||||
padding: 0.8em; background: white; }
|
||||
.entries { list-style: none; margin: 0; padding: 0; }
|
||||
.entries li { margin: 0.8em 1.2em; }
|
||||
.entries li h2 { margin-left: -1em; }
|
||||
table.entriesall { border-collapse: collapse; }
|
||||
.entriesall td, .entriesall th { border: 1px solid black;
|
||||
padding: 0.5em; }
|
||||
.index {
|
||||
margin-top: 1em;
|
||||
}
|
||||
@@ -21,16 +15,27 @@ table.entriesall { border-collapse: collapse; }
|
||||
margin-top: 1em;
|
||||
}
|
||||
.question {
|
||||
border-bottom: 1px solid gray;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
.autoformat {
|
||||
border-bottom: 1px solid gray;
|
||||
}
|
||||
.warning {
|
||||
font-size: small;
|
||||
color: red;
|
||||
}
|
||||
.message {
|
||||
font-size: large;
|
||||
color: red;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 90%;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.header {
|
||||
@@ -38,7 +43,23 @@ textarea {
|
||||
font-size: small;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 2em;
|
||||
text-align: left;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.summary_single {
|
||||
list-style-type: disk;
|
||||
}
|
||||
.summary_multiple {
|
||||
list-style-type: square;
|
||||
}
|
||||
.summary_open {
|
||||
list-style-type: circle;
|
||||
font-style: italic;
|
||||
}
|
||||
.thankyou {
|
||||
font-size: large;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
{{ message }}
|
||||
<div class=message>{{ message }}</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
|
||||
<div class=page>
|
||||
<div class=header>
|
||||
Read more about me at the <a href="#bottom">bottom!</a>
|
||||
<a href="#bottom">About</a>
|
||||
</div>
|
||||
{% block body %}{% endblock %}
|
||||
<hr>
|
||||
</div>
|
||||
<div class=page>
|
||||
<div class=footer>
|
||||
About aBot
|
||||
<ul>
|
||||
<li>This voting machine does not store information about you.</li>
|
||||
<li>The token given to you is stored separately to the answers you give.</li>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<div id="questions">
|
||||
{% for question in form.questions %}
|
||||
<div class = "question">
|
||||
<h2>{{ question.name|safe }}</h2>
|
||||
|
||||
{% if question.autoformat %}
|
||||
<div class = "question autoformat">
|
||||
<h2>{{ question.name|safe }}</h2>
|
||||
{% else %}
|
||||
<div class = "question">
|
||||
{{ question.name|safe }}
|
||||
{% endif %}
|
||||
{% for choice in question.choices %}
|
||||
<div>
|
||||
<input type="radio" name="QC{{ question.index }}" value="{{ choice|safe }}" />
|
||||
|
||||
@@ -1,9 +1,35 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<h1>aBot!</h1>
|
||||
|
||||
<div class=thankyou>
|
||||
Thank you for the vote!
|
||||
</div>
|
||||
{% if summary %}
|
||||
<h3>Current report</h3>
|
||||
|
||||
{{ summary|safe }}
|
||||
<div id="questions">
|
||||
{% for question in questions %}
|
||||
|
||||
<div class = "question autoformat">
|
||||
<h2>{{ question|safe }}</h2>
|
||||
<ul>
|
||||
{% for choice in answers[question].answers %}
|
||||
{% if answers[question].answer_type == "single" %}
|
||||
<li class = "summary_single">{{ choice }}: {{ answers[question].answers[choice] }}
|
||||
{% endif %}
|
||||
{% if answers[question].answer_type == "multiple" %}
|
||||
<li class = "summary_multiple">{{ choice }}: {{ answers[question].answers[choice] }}
|
||||
{% endif %}
|
||||
{% if answers[question].answer_type == "open" %}
|
||||
<li class = "summary_open">"{{ choice }}"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
72
utils.py
72
utils.py
@@ -38,10 +38,69 @@ def get_voter_table_name(key):
|
||||
def get_result_table_name(key):
|
||||
return key
|
||||
|
||||
def get_token_counts(db, key):
|
||||
cur = db.cursor()
|
||||
token_table = get_voter_table_name(key)
|
||||
cur.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name=?;",
|
||||
( token_table, )
|
||||
)
|
||||
matching_tables = cur.fetchall()
|
||||
if len(matching_tables) == 0:
|
||||
used_tokens = 0
|
||||
unused_tokens = 0
|
||||
else:
|
||||
cur.execute(
|
||||
"SELECT count(*) FROM `%s` WHERE answered = 'true'"%(
|
||||
token_table,
|
||||
)
|
||||
)
|
||||
used_tokens = cur.fetchall()[0][0]
|
||||
cur.execute(
|
||||
"SELECT count(*) FROM `%s` WHERE answered = 'false'"%(
|
||||
token_table,
|
||||
)
|
||||
)
|
||||
unused_tokens = cur.fetchall()[0][0]
|
||||
tokens = {
|
||||
'unused': unused_tokens,
|
||||
'used': used_tokens,
|
||||
'total': used_tokens + unused_tokens
|
||||
}
|
||||
return tokens
|
||||
|
||||
def get_html_summary(key):
|
||||
# TODO
|
||||
return ''
|
||||
def get_summary(db, key):
|
||||
""" returns summary for a vote event """
|
||||
questions = []
|
||||
answers = {}
|
||||
cur = db.cursor()
|
||||
|
||||
cur.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name=?;",
|
||||
( key, )
|
||||
)
|
||||
matching_tables = cur.fetchall()
|
||||
if len(matching_tables) == 0:
|
||||
return questions, answers
|
||||
|
||||
cur.execute(
|
||||
"SELECT question, answer, answer_type FROM `%s`"%(
|
||||
key,
|
||||
)
|
||||
)
|
||||
for row in cur:
|
||||
if row[0] not in answers.keys():
|
||||
questions.append(row[0])
|
||||
answers[row[0]] = {
|
||||
'answers': {},
|
||||
'answer_type': row[2]
|
||||
}
|
||||
|
||||
if row[1] not in answers[row[0]]['answers'].keys():
|
||||
answers[row[0]]['answers'][row[1]] = 0
|
||||
answers[row[0]]['answers'][row[1]] += 1
|
||||
|
||||
return questions, answers
|
||||
|
||||
|
||||
def has_voted(key, token):
|
||||
@@ -104,7 +163,7 @@ def parse_form(key):
|
||||
}
|
||||
key = secure_filename(key)
|
||||
try:
|
||||
current_question = None
|
||||
current_question = 0
|
||||
with open(os.path.join(app.config['QUESTIONS'], key + ".txt"), "rt") as fp:
|
||||
for row in fp:
|
||||
if row.strip() == "":
|
||||
@@ -145,9 +204,10 @@ def parse_form(key):
|
||||
form['questions'].append({
|
||||
'choices': [],
|
||||
'multichoices': [],
|
||||
'index': len(form['questions']) + 1,
|
||||
'index': current_question + 1,
|
||||
'name': row.strip().rstrip("_:").rstrip(),
|
||||
'open_question': row.strip().endswith("___")
|
||||
'open_question': row.strip().endswith("___"),
|
||||
'autoformat': not rowsl.startswith("<")
|
||||
})
|
||||
return form
|
||||
except Exception as err:
|
||||
|
||||
Reference in New Issue
Block a user