I am writing a quiz app on pythonanywhere using flask. This is the first time I have ever used flask or pythonanywhere, so I am very much still learning. The function below is throwing a weird error, where sometimes it generates 11 or 9 dictionary entries instead of 10, even though the Qnum parameter never changes.
I thought the issue might be something to do with aliasing (since the function deletes an entry), so I've tried to make separate lists by walking down the dictionary keys and values. The code worked fine when I wrote it directly in my main app file, but once I abstracted it into a helper function it started playing up.
From Helper Function File:
def create_answer_dict(Dict, Qnum):
import random
Qdict={}
for i in range(Qnum):
#Choose random word to test
Qkeys=[]
for key in Dict.keys():
Qkeys.append(key)
Qword=random.choice(Qkeys)
#Get correct answer from dictionary
correctAnswer = Dict[Qword]
#Generate wrong answer options
wrongAnswers=[]
for value in Dict.values():
wrongAnswers.append(value)
del wrongAnswers[wrongAnswers.index(correctAnswer)]
wrongAnswers = random.sample(wrongAnswers, 3)
answerOptions = wrongAnswers + [correctAnswer]
random.shuffle(answerOptions)
Qdict[Qword]=answerOptions
return Qdict
From main app file:
@app.route("/", methods=["GET","POST"])
def index():
Qdict=create_answer_dict(questions, total)
if request.method == "GET":
return render_template('main.html', q = Qdict, keys=Qdict.keys())
elif request.method == 'POST':
score=0
for i in Qdict.keys():
answered=request.form[i]
if original_questions[i]==answered:
score+=1
return render_template("results.html", score=score, total=total)
From html view:
<form action='/' method='POST'>
<ol>
{% for i in keys %}
<li>What is the French for <u>{{i}}</u> ? </li>
{% for j in q[i] %}
<input type='radio' value='{{j}}' name='{{i}}' style="margin-right: 5"/>{{j}}
<br></br>
{% endfor %}
{% endfor %}
</ol>
<input type="submit" value="submit" />
</form>
How it is supposed to work:
Possible questions and answers are stored in a dictionary object.
In my main app file I call this function from a helper functions file, using my question-answer dictionary and a variable total as parameters. Total is set to 10.
The function chooses Qnum questions, finds the corresponding answer, and chooses 3 random incorrect answers.
It returns these as a dictionary in the following format:
{Question1:[CorrectAnswer, IncorrectAnswer1,IncorrectAnswer2, IncorrectAnswer3],
Question2:[CorrectAnswer, IncorrectAnswer1,IncorrectAnswer2, IncorrectAnswer3],
etc.}
Everything comes back without raising an error, just sometimes the dictionary has one less or one more entry than expected.
You cannot expect to get a dictionary of length n
by randomly picking n
entries from another dictionary, because there is always the possibility of picking duplicate entries (and since dictionary keys are unique, there duplicate entries will be overwritten in the resulting dictionary).
A much better approach for picking a fixed number n
of random keys in a dictionary, is simply create a list from the dictionary keys, shuffle that list, and then slice that list to only keep the first n
elements.
In your code, it would look like this:
def create_answer_dict(Dict, Qnum):
import random
Qdict={}
possibleQuestions = list(Dict.keys())
random.shuffle(possibleQuestions)
possibleQuestions = possibleQuestions[:Qnum]
for Qword in possibleQuestions:
#Get correct answer from dictionary
correctAnswer = Dict[Qword]
#Generate wrong answer options
wrongAnswers = list(Dict.values())
del wrongAnswers[wrongAnswers.index(correctAnswer)]
wrongAnswers = random.sample(wrongAnswers, 3)
answerOptions = wrongAnswers + [correctAnswer]
random.shuffle(answerOptions)
Qdict[Qword] = answerOptions
return Qdict
This will guarantee that there are Qnum
unique questions being generated.
Edit: also, in index()
, to avoid KeyErrors if the user doesn't answer all questions, replace
for i in Qdict.keys():
answered=request.form[i]
...
with
for i in request.form:
answered=request.form[i]
...
Working demo: https://repl.it/@glhr/55701832