gruppenbildungsspiel/gruppenbildungsspiel.py
2019-04-18 17:09:51 +02:00

350 lines
11 KiB
Python

"""A script for the Sortierhut Game
Based on the Flask and WTForms web frameworks.
"""
__version__ = '0.0'
__author__ = 'aamma, asauer'
import uuid
import csv
import os
from flask import Flask, render_template, request, redirect, url_for
from wtforms import Form, StringField, SelectField, RadioField, TextAreaField, BooleanField, validators
app = Flask(__name__)
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
datafile = 'data.csv'
solutionfile = 'solutions.csv'
def create_used_id_list():
"""Read datafile and create a list of the id in each row.
Useful to compare if a given id is already used.
"""
data = list()
if not os.path.exists(datafile):
open(datafile, 'a').close()
with open(datafile, 'r') as f:
reader = csv.reader(f)
for line in reader:
# line[0] is the row's id
data.append(line[0])
return data
def convert_csv_to_list_of_lists(csv_file):
"""Convert a csv file to a list whose items are lists.
csv_file -- a valid comma-separated file.
Useful because a list of lists can be easily iterated.
"""
list_of_lists = list()
if not os.path.exists(csv_file):
open(csv_file, 'a').close()
with open(csv_file, 'r', newline='') as f:
reader = csv.reader(f)
for line in reader:
list_of_lists.append(line)
return list_of_lists
def check_input(form, field):
"""Check if a SelectField from the questions() is still in default mode.
form -- the form on which this function is called (instance of QuestionForm).
field -- the form field whose input is checked.
"""
if field.data == 'default':
raise validators.ValidationError('Bitte wähle eine Option aus')
def check_id(form, field):
"""Check if the datafile contains a given id.
form -- the form on which this function is called (instance of AnswerForm).
field -- the id input field whose input is checked.
"""
used_ids = create_used_id_list()
if field.data not in used_ids:
raise validators.ValidationError('Leider keine gültige ID')
class QuestionForm(Form):
"""A form of humorous questions."""
computers = [
('default', 'Bitte auswählen'),
('hal', 'HAL 9000 (2001: Odyssee im Weltraum)'),
('deepthought', 'Deep Thought (Hitchhiker\'s Guide)'),
('neuromancer', 'Neuromancer (Neuromancer)'),
('samantha', 'Samatha (Her)'),
('tars', 'TARS und CASE (Interstellar)')
]
statuses = [
('default', 'Bitte auswählen'),
('200', '200 OK'),
('300', '300 Multiple Choices'),
('301', '301 Moved Permanently'),
('402', '402 Payment Required'),
('404', '404 (Sleep) Not Found'),
('408', '408 Request Timeout'),
('418', '418 I am a teapot'),
('450', '450 Blocked by Windows Parental Controls'),
('451', '451 Unavailable For Legal Reasons'),
('502', '502 Bad Gateway (Internetkurort)')
]
vegetables = [
('default', 'Bitte auswählen'),
('karotte', '🥕 Karotte'),
('broccoli','🥦 Brokkoli'),
('aubergine', '🍆 Aubergine'),
('kartoffel', '🥔 Kartoffel'),
('bretzel', '🥨 Bretzel'),
('tomate', '🍅 Tomate'),
('chili', '🌶️ Chili')
]
spirit_animals = [
('default', 'Bitte auswählen'),
('feuer', 'Feuerfuchs'),
('wasser', 'Wasserhahn'),
('erde', 'Erdferkel'),
('luft', 'Luftschlange')
]
operating_systems = [
('default', 'Bitte auswählen'),
('windows', 'Windows'),
('mac', 'Mac'),
('linux', 'Linux'),
('kaffee', 'Kaffee'),
('glados', 'GLaDOS')
]
computer = SelectField(u'Fiktionaler Lieblingscomputer', choices=computers,
validators=[validators.InputRequired(), check_input])
status = SelectField(u'Welcher HTTP-Statuscode trifft am häufigsten auf dich zu?',
choices=statuses,
validators=[validators.InputRequired(), check_input])
vegetable = SelectField(u'Lieblingsgemüse', choices=vegetables,
validators=[validators.InputRequired(), check_input])
spirit_animal = SelectField(u'Spirit Animal', choices=spirit_animals,
validators=[validators.InputRequired(), check_input])
operating_system = SelectField(u'Bevorzugtes Betriebssystem', choices=operating_systems,
validators=[validators.InputRequired(), check_input])
check = BooleanField('Bitte nicht ankreuzen')
class AnswerForm(Form):
"""A form for a user_id as a sort of password."""
user_id = StringField('Id', [validators.InputRequired(), check_id])
class SolutionForm(Form):
"""A form for a solution idea for the Sortierhut riddle."""
choices = [
('computer', 'Fiktionaler Lieblingscomputer'),
('status', 'HTTP-Statuscode'),
('vegetable', 'Lieblingsgemüse'),
('spirit_animal', 'Spirit Animal'),
('operating system', 'Betriebssystem'),
('check', 'Bitte nicht ankreuzen')
]
user_id = StringField('id', [validators.InputRequired(),])
question = RadioField(choices=choices, validators=[validators.InputRequired(),])
solution_text = TextAreaField(u'Textfeld', [validators.InputRequired(),])
@app.route('/')
def index():
"""Display a home page with an explanation and many navigation links."""
return render_template('index.html', title='Sortierhut')
@app.route('/questions', methods=['GET', 'POST'])
def questions():
"""Display a form with some questions.
Raise ValidationError and stay on questions() page
if any of the dropdown fields are still in the default position on
"Bitte auswählen".
Else redirect to result().
"""
def generate_id():
"""Generate a new unique user id."""
data = create_used_id_list()
# best loop ever
while True:
new_id = str(uuid.uuid1()).split('-')[0]
if new_id in data:
break
else:
return new_id
form = QuestionForm(request.form)
# Assign a number to each house
houses = dict()
houses[0] = 'knuth'
houses[1] = 'lovelace'
houses[2] = 'turing'
houses[4] = 'hopper'
houses[8] = 'lamarr'
if request.method == 'POST' and form.validate():
with open(datafile, 'a', newline='') as f:
new_row = list()
new_id = generate_id()
new_row.append(new_id)
for item in request.form:
new_row.append(request.form[item])
# If the BooleanField is not checked,
# the request.form dict does not contain an entry for 'check'
if len(new_row) == 6:
new_row.append('n')
# This is the riddle's kern:
# Compute the mod 10 of the selected http status code
# and this then determines the sorting
magical_house_number = int(new_row[2]) % 10
new_row.append(houses[magical_house_number])
writer = csv.writer(f)
writer.writerow(new_row)
return redirect(url_for('result', user_id=new_id))
return render_template('questions.html', form=form, title='Sortierhut')
@app.route('/result<user_id>')
def result(user_id):
"""Display the user's id and a comment on what to do next.
user_id -- argument passed by redirect call in the questions() function.
Only called in questions().
"""
return render_template('result.html', title='Sortierhut', id=user_id)
@app.route('/answers', methods=['GET', 'POST'])
def access_answers():
"""Display a form for an id that is needed to access the corresponded answers.
Raise ValidationError and stay on access_answers() page
if no id is submitted or the id is not valid.
Else redirect to view_answers() page.
"""
form = AnswerForm(request.form)
if request.method == 'POST' and form.validate():
ident = request.form['user_id']
return redirect(url_for('view_answers', user_id=ident))
return render_template('access_answers.html', form=form, title='Sortierhut')
@app.route('/view_answers<user_id>')
def view_answers(user_id):
"""Display the user's answers.
user_id -- argument passed by the redirect call in the access_answers() function.
Only called in access_answers().
"""
# Create list object to be filled with the datafile row matching the user_id
user_row = list()
with open(datafile, 'r', newline='') as f:
reader = csv.reader(f)
for line in reader:
# line[0] is the row's id
if line[0] == user_id:
user_row = line
break
return render_template('view_answers.html', title='Sortierhut',
user_id=user_id, user_row=user_row)
@app.route('/submit_solution', methods=['GET', 'POST'])
def submit_solution():
"""Display a form for solution ideas.
Raise ValidationError and stay on submit_solution() page
if the form input is not valid.
Else redirect to thanks() page.
"""
form = SolutionForm(request.form)
if request.method == 'POST' and form.validate():
with open(solutionfile, 'a', newline='') as f:
solution_list = list()
# Transfer input data from request.form dict to solution_list list
for item in request.form:
solution_list.append(request.form[item])
writer = csv.writer(f)
writer.writerow(solution_list)
return redirect(url_for('thanks'))
return render_template('submit_solution.html', form=form, title='Sortierhut')
@app.route('/thanks')
def thanks():
"""Display how the right solution will be announced.
Only called in submit_solution().
"""
return render_template('thanks.html', title='Sortierhut')
@app.route('/houses')
def houses():
"""Display a description of the 5 groups (=houses)."""
return render_template('houses.html', title='Sortierhut')
@app.route('/admint_form_fooboar02.104')
def admin_form():
"""Display a table of the data from submitted question form."""
file_data = convert_csv_to_list_of_lists(datafile)
return render_template('admin_form.html', title='Sortierhut',
text='Hallo, Admin Fooboar', file_data=file_data)
@app.route('/admint_solution_fooboar02.104')
def admin_solution():
"""Display a table of the submitted solutions."""
file_data = convert_csv_to_list_of_lists(solutionfile)
return render_template('admin_solution.html', title='Sortierhut', file_data=file_data)
@app.route('/reveal_solution')
def reveal_solution():
"""Display the correct solution."""
return render_template('reveal_solution.html', title='Sortierhut')