diff --git a/abot.py b/abot.py index c2eacb3..2c08a28 100644 --- a/abot.py +++ b/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) diff --git a/manager.py b/manager.py index 3905e18..e0f3e63 100644 --- a/manager.py +++ b/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 + if options.tsv: + out = summary_tsv(questions, answers, tokens) 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) - 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: diff --git a/questions/multi_question.txt b/questions/multi_question.txt index d2f1481..e19d7fe 100644 --- a/questions/multi_question.txt +++ b/questions/multi_question.txt @@ -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 +

Title of vote page

+ # - character is single choice question It works? - yes - no +# If a question is in HTML tags, it's not formatted +

Fruits

# + character is multi choice question -Fruits + banana + orange + tomato diff --git a/questions/simple_example.txt b/questions/simple_example.txt index 0ea3dcd..96019cb 100644 --- a/questions/simple_example.txt +++ b/questions/simple_example.txt @@ -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 diff --git a/static/style.css b/static/style.css index 0542068..9456c73 100644 --- a/static/style.css +++ b/static/style.css @@ -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; +} + diff --git a/templates/blank.html b/templates/blank.html index abf7879..ba7eed5 100644 --- a/templates/blank.html +++ b/templates/blank.html @@ -1,4 +1,4 @@ {% extends "layout.html" %} {% block body %} - {{ message }} +
{{ message }}
{% endblock %} diff --git a/templates/layout.html b/templates/layout.html index be87c62..490dcdd 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -9,17 +9,19 @@
-
-Read more about me at the bottom! -
+
+ About +
{% block body %}{% endblock %} -
+
+
diff --git a/templates/questions.html b/templates/questions.html index 8fad537..9ef272b 100644 --- a/templates/questions.html +++ b/templates/questions.html @@ -1,8 +1,13 @@
{% for question in form.questions %} -
-

{{ question.name|safe }}

+ {% if question.autoformat %} +
+

{{ question.name|safe }}

+ {% else %} +
+ {{ question.name|safe }} + {% endif %} {% for choice in question.choices %}
diff --git a/templates/thank_you.html b/templates/thank_you.html index 54a6671..18ceebc 100644 --- a/templates/thank_you.html +++ b/templates/thank_you.html @@ -1,9 +1,35 @@ {% extends "layout.html" %} {% block body %}

aBot!

- +
Thank you for the vote! +
+{% if summary %} +

Current report

-{{ summary|safe }} +
+{% for question in questions %} + +
+

{{ question|safe }}

+
    + {% for choice in answers[question].answers %} + {% if answers[question].answer_type == "single" %} +
  • {{ choice }}: {{ answers[question].answers[choice] }} + {% endif %} + {% if answers[question].answer_type == "multiple" %} +
  • {{ choice }}: {{ answers[question].answers[choice] }} + {% endif %} + {% if answers[question].answer_type == "open" %} +
  • "{{ choice }}" + {% endif %} + {% endfor %} +
+
+ +{% endfor %} +
+ +{% endif %} {% endblock %} diff --git a/utils.py b/utils.py index c67aaa7..b2091ea 100644 --- a/utils.py +++ b/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: