open voting, without tokens

This commit is contained in:
Ville Rantanen
2018-12-03 21:31:09 +02:00
parent 083915e69a
commit 1a8cc70914
6 changed files with 108 additions and 39 deletions

12
abot.py
View File

@@ -7,7 +7,7 @@ from utils import *
import manager
DATABASE = 'abot.sqlite'
DEBUG = False
DEBUG = True
SECRET_KEY = 'otwet6oi539iosf'
QUESTIONS = 'questions' # path to questions
@@ -51,7 +51,8 @@ def preview(key):
)
@app.route('/vote/<key>/<token>')
def vote(key, token):
@app.route('/vote/<key>')
def vote(key, token = None):
if not is_key(key):
return render_template('blank.html', message = "Unknown key")
form = parse_form(key)
@@ -61,6 +62,7 @@ def vote(key, token):
return render_template('blank.html', message = "Not published")
if is_expired(form):
return render_template('blank.html', message = "Voting has closed")
if is_closed_vote(form):
if has_voted(key, token):
return render_template('blank.html', message = "Token already used")
valid_for = time_to_expiry(form)
@@ -81,13 +83,17 @@ def save_vote():
return render_template('blank.html', message = "Not published")
if is_expired(form):
return render_template('blank.html', message = "Voting has closed")
if is_closed_vote(form):
if has_voted(key, token):
return render_template('blank.html', message = "Token already used")
create_result_table(key)
write_vote(key, token, request.form, form) # using request.
summary = ''
if is_show_results(form):
summary = get_html_summary(key)
return render_template('thank_you.html')
return render_template('thank_you.html', summary = summary)
if __name__ == "__main__":
manager.main(DATABASE, QUESTIONS)

View File

@@ -135,6 +135,10 @@ 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)
@@ -146,8 +150,8 @@ def summary(options):
)
matching_tables = cur.fetchall()
if len(matching_tables) == 0:
print("No votes yet")
return
return "No votes yet"
token_table = get_voter_table_name(options.name)
cur.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name=?;",
@@ -197,21 +201,20 @@ def summary(options):
try:
if options.tsv:
summary_tsv(questions, answers, tokens)
return summary_tsv(questions, answers, tokens)
else:
summary_list(questions, answers, tokens)
return summary_list(questions, answers, tokens)
except AttributeError:
summary_list(questions, answers, tokens)
return summary_list(questions, answers, tokens)
def summary_list(questions, answers, tokens):
print(
"""# Tokens for this question set:
# used: {used}, unused: {unused}, total: {total}""".format_map(tokens)
)
s = """# Tokens for this question set:
# used: {used}, unused: {unused}, total: {total}
""".format_map(tokens)
for q,a in zip(questions, answers):
sum_answers = sum([answers[q]['answers'][x] for x in answers[q]['answers']])
print("\n%s\n# Answers total: %d"%( q, sum_answers, ))
s += "\n%s\n# Answers total: %d\n"%( q, sum_answers, )
sorted_answers = sorted(
[(x, answers[q]['answers'][x]) for x in answers[q]['answers']],
key = lambda i: -i[1]
@@ -229,33 +232,34 @@ def summary_list(questions, answers, tokens):
prefix = "----\n> "
postfix = ""
print("%s%s%s"%(
s += "%s%s%s\n"%(
prefix,
answer[0],
postfix
))
)
return s
def summary_tsv(questions, answers, tokens):
print(
'''Tokens\tUsed\tUnused\tTotal
""\t"{used}"\t"{unused}"\t"{total}"'''.format_map(tokens)
)
print('"Question"\t"Question type"\t"Answer"\t"Count"')
s = '''Tokens\tUsed\tUnused\tTotal
""\t"{used}"\t"{unused}"\t"{total}"
'''.format_map(tokens)
s += '"Question"\t"Question type"\t"Answer"\t"Count"\n'
good_characters = dict.fromkeys(range(32))
for q,a in zip(questions, answers):
sum_answers = sum([answers[q]['answers'][x] for x in answers[q]['answers']])
print('"%s"\t"%s"\t""\t"%d"'%( q, answers[q]['answer_type'], sum_answers, ))
s += '"%s"\t"%s"\t""\t"%d"\n'%( q, answers[q]['answer_type'], sum_answers, )
sorted_answers = sorted(
[(x, answers[q]['answers'][x]) for x in answers[q]['answers']],
key = lambda i: -i[1]
)
for answer in sorted_answers:
print('""\t""\t""%s\t"%d"'%(
s += '""\t""\t""%s\t"%d"\n'%(
answer[0].translate(good_characters),
answer[1],
))
)
return s
def clear_votes(options):

View File

@@ -32,3 +32,13 @@ table.entriesall { border-collapse: collapse; }
textarea {
width: 90%;
}
.header {
text-align: right;
font-size: small;
}
.footer {
margin-top: 2em;
text-align: left;
font-size: small;
}

View File

@@ -6,7 +6,22 @@
<script src="{{ url_for('static', filename='script.js') }}" type="text/javascript"></script>
</head>
<body>
<div class=page>
<div class=header>
Read more about me at the <a href="#bottom">bottom!</a>
</div>
{% block body %}{% endblock %}
<hr>
<div class=footer>
<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>
<li>If you were given the token via email or any other such means, this voting machine does not know the connection between your contact information and the vote token.</li>
<li>Source code at <a href="https://bitbucket.org/MoonQ/aBot/">Bitbucket</a></li>
</ul>
<a id="bottom"></a>
</div>
</div>
</body>

View File

@@ -4,5 +4,6 @@
Thank you for the vote!
{{ summary|safe }}
{% endblock %}

View File

@@ -16,6 +16,7 @@ def create_result_table(key):
)
g.db.commit()
def create_voter_table(db, name):
table_name = get_voter_table_name(name)
cur = db.cursor()
@@ -29,6 +30,7 @@ def create_voter_table(db, name):
)
db.commit()
def get_voter_table_name(key):
return key + "__voters"
@@ -37,7 +39,14 @@ def get_result_table_name(key):
return key
def get_html_summary(key):
# TODO
return ''
def has_voted(key, token):
if token == None:
return True
cur = g.db.cursor()
cur.execute(
"SELECT token FROM %s WHERE token = ? AND answered = 'true'"%(
@@ -50,15 +59,21 @@ def has_voted(key, token):
return len(cur.fetchall()) > 0
def is_closed_vote(form):
return form['vote_style'] == 'closed'
def is_draft(form):
return form['draft']
def is_expired(form):
if form['expires'] == None:
return False
return datetime.now(timezone.utc) > form['expires']
def is_key(key, cli_opts = False):
key = secure_filename(key)
@@ -74,10 +89,17 @@ def is_key(key, cli_opts = False):
)
)
def is_show_results(form):
return form['show_results']
def parse_form(key):
form = {
'expires': None,
'draft': False,
'vote_style': "closed",
'show_results': False,
'questions': []
}
key = secure_filename(key)
@@ -87,15 +109,24 @@ def parse_form(key):
for row in fp:
if row.strip() == "":
continue
rowsl = row.lower().rstrip()
if row.startswith("#"):
continue
if row.lower().startswith("expires: "):
if rowsl.startswith("expires: "):
form['expires'] = parse_row_date(row)
continue
if row.lower().startswith("draft: "):
if row.lower().rstrip() == "draft: true":
if rowsl.startswith("draft: "):
if rowsl == "draft: true":
form['draft'] = True
continue
if rowsl.startswith("vote_style: "):
if rowsl == "vote_style: open":
form['vote_style'] = "open"
continue
if rowsl.startswith("show_results: "):
if rowsl == "show_results: true":
form['show_results'] = True
continue
if row.startswith("- "):
if current_question == None:
continue
@@ -157,9 +188,11 @@ def write_vote(key, token, answers, form):
answer_type = "open"
if answer == None:
continue
if answer_type == None:
continue
for single in answer:
cur.execute(
"INSERT INTO %s VALUES (?, ?, ?)"%(
"INSERT INTO `%s` VALUES (?, ?, ?)"%(
key,
),
(
@@ -168,7 +201,7 @@ def write_vote(key, token, answers, form):
answer_type
)
)
if is_closed_vote(form):
cur.execute(
"UPDATE %s SET answered = 'true' WHERE token = ?"%(
get_voter_table_name(key),