Posts: 16
Threads: 6
Joined: May 2020
I have Python 3.10, and want to read a series of multiple choice or Yes-No/True-False test questions into a list (or array or whatever) from an Excel spreadsheet, complete with 5 possible answers for the multiple choice questions and, of course, 2 possible answers for the true/false or yes/no questions. Each question has a preferred answer priority, 1st or best answer, 2nd best answer… worst or 5th best answer, etc.
The program should first show how many questions there are to choose from, and the user chooses how many questions they want to answer. Can someone show me how to randomly choose the questions to ask (no repeats), as well as randomly shuffle the answers (while keeping track of their priority (i.e. 1st or best choice, 2nd best, etc.)? Seems like a nightmare for this novice Python programmer.
Many thanks in advance,
Perplexed in Pittsburgh
Posts: 453
Threads: 16
Joined: Jun 2022
Sep-26-2022, 08:02 PM
(This post was last modified: Sep-27-2022, 11:51 AM by rob101.)
I've not covered the 'Excel' part of this, as I'm not a Excel user, but from what I do know, I can't see that part being a huge challenge: I see the QandA dictionary being built, in part, from your Excel file, with which I'm sure OPs will be able to help, if needs be.
This is one way that one could begin to build the Q&A part:
from random import shuffle, randint
answered = 'answered'
answer = 'answer'
question = 'question'
answers = 'answers'
QandA = {
1:{
answered: False,
answer: '',
question: "This will be the text for question 1.", # place holder
answers: ("Answer 1", "Answer 2", "Answer 3", "Answer 4", "Answer 5") # place holder
},
2:{
answered: False,
answer: '',
question: "This will be the text for question 2.", # place holder
answers: ("Answer 1", "Answer 2", "Answer 3", "Answer 4", "Answer 5") # place holder
},
3:{
answered: False,
answer: '',
question: "This will be the text for question 3.", # place holder
answers: ("Answer 1", "Answer 2", "Answer 3", "Answer 4", "Answer 5") # place holder
},
4:{
answered: False,
answer: '',
question: "This will be the text for question 4.", # place holder
answers: ("Answer 1", "Answer 2", "Answer 3", "Answer 4", "Answer 5") # place holder
},
5:{
answered: False,
answer: '',
question: "This will be the text for question 5.", # place holder
answers: ("True", "False") # place holder
}
}
# the driver
no_of_qs = len(QandA)
print(f"There will be {no_of_qs} questions.\n")
q_asked = [] #holds the index of the question asked
q = 0
done = False
while not done:
q = randint(1,no_of_qs)
if len(q_asked) < no_of_qs:
if q not in q_asked:
q_asked.append(q)
ask = QandA[q][question]
print(ask)
options = [option for option in range(0,len(QandA[q][answers]))]
if len(options) > 2: # no need to shuffle True/False
shuffle(options)
for choose, ans in enumerate(options,1):
print(f"{choose}: {QandA[q][answers][ans]}")
the_answer = False
while not the_answer:
the_answer = input(f"\n1 to {len(options)} or 0 to pass.\n> ")
if len(the_answer) > 1 or the_answer.isalpha() or int(the_answer)not in range(0,len(options)+1):
the_answer = False
print("Invalid answer.")
if the_answer != '0':
QandA[q][answered] = True
QandA[q][answer] = QandA[q][answers][options[int(the_answer)-1]]
print()
else:
done = True
for q in range(1,no_of_qs+1):
if QandA[q][answered]:
print(f"Question {q} was answered: {QandA[q][answer]}.")
else:
print(f"Question {q} was not answered.") Not too sure if this is going to be of any help, or if anyone else has a better way for this to be done. I've also not covered the binary part of this (questions that have a simple yes/no answer), but I don't see why that could not be implemented in some way; I'll have a look at that next, if you're still interested in moving this forward.
Code update: Some minor changes, just to make for a better base solution...
Added a True/False option and also a feedback for the questions that have been answered, from which a score could be calculated, based on whatever system you choose.
Sig:
>>> import this
The UNIX philosophy: "Do one thing, and do it well."
"The danger of computers becoming like humans is not as great as the danger of humans becoming like computers." :~ Konrad Zuse
"Everything should be made as simple as possible, but not simpler." :~ Albert Einstein
Posts: 6,779
Threads: 20
Joined: Feb 2020
Sep-27-2022, 04:23 PM
(This post was last modified: Sep-27-2022, 04:23 PM by deanhystad.)
I'd bet Excel really means csv. After loading the csv file (or maybe the Excel file) the trickiest part will be formatting the question. For True/False the choices should be "T" and "F". for Yes/No the choices should be "Y" and "N". For multiple choice the choices can be anything. Letters, numbers, first unique letter in each choice, etc. I like letters because they are easier for me to type. For kids, numbers is probably a better choice.
For random, either use random.sample() or random.shuffle(). Both are guaranteed to not repeat.
This is a rough example. I have no idea how to handle "2nd best answer" because I have no idea what is meant by "2nd best answer" in a multiple choice test. Do you get partial credit if you don't pick the "worst" answer?
from io import StringIO
import csv
import random
# Standin for CSV file. Question followed by choices.
# Imagine "Question 1" is "What was the capital of Germany in 1956?", and it is followed
# by "Bonn", "Berlin", "Frankfurt", "Flensburg", "Munich". First choice is correct, but
# all choices have been capitals except Munich (the worst choice).
file = StringIO(
"""\
"Question 1","Choice 1","Choice 2","Choice 3","Choice 4","Choice 5"
"Question 2","Choice 1","Choice 2","Choice 3","Choice 4"
"Question 3","True","False"
"Question 4","Choice 1","Choice 2","Choice 3"
"Question 5","False","True"
"Question 6","Yes","No"
"Question 7","No","Yes"
"""
)
# True/False and Yes/No are special cases and should always be presented
# in the same order and use special answer keys. Choices for other questions
# appear in random order.
TRUE_FALSE = {"T": "True", "F": "False"}
YES_NO = {"Y": "Yes", "N": "No"}
def get_answer(question, choices):
"""Get user input. Input must match one of the choice keys"""
print(f"\n{question}")
for key, value in choices.items():
print(f"{key}: {value}")
selection = input("> ").upper()
while True:
if selection in choices:
return choices[selection]
selection = input(f"Please choose from {', '.join(choices)}: ").upper()
def ask_question(question):
"""Format question. Randomize choices. Return True if selection is correct"""
question, *choices = question
if choices[0] in YES_NO.values():
options = YES_NO
elif choices[0] in TRUE_FALSE.values():
options = TRUE_FALSE
else:
# Any multiple choice that is not True/False or Yes/No
# Use 1234567 instead of ABCDEFG if want "1: choice" instead of "A: choice"
options = {
a: b for a, b in zip("ABCDEFG", random.sample(choices, k=len(choices)))
}
return get_answer(question, options) == choices[0] # First choice is correct
# Load and shuffle the questions
questions = list(csv.reader(file))
random.shuffle(questions)
# Ask questions and keep score
score = 0
for question in questions:
if ask_question(question):
score += 1
print(score)
|