I have a Python project from an online course whereby I am tasked to build an interactive learning tool that allows users to create, practice, and test their knowledge using multiple-choice and freeform text questions. The program will track user statistics and provide options to manage the questions. So far I managed to write some functions to add questions (either multiple choice or freeform questions) and then save them in a CSV file. The issue is after adding and saving a question (or more) I have yet to be able to view them (I cant spot any bug in my view_statistics function), as I'd always get "No questions available" notification. Please help, would be greatly appreciated!
import csv
import time
import sys
import os
import platform
from colorama import init, Fore, Style
from questions import Question, Quiz, Freeform
questions = []
def loading():
'''
Short loading animation
'''
print("Loading...")
time.sleep(1)
if platform.system() == "Windows":
os.system('cls')
else:
os.system('clear')
def select_quiz_type():
'''
Selects the type of quiz to add questions to
'''
while True:
print("You can add quiz-type or freeform-type question(s)")
print("===============================================")
print("(a) Quiz")
print("(b) Freeform")
print("(c) Back to main menu")
print("===============================================")
question_type = input("Type in a or b (quiz/freeform): ").strip().lower()
if not question_type:
break
elif question_type == "a":
add_quiz_question()
elif question_type == "b":
add_freeform_question()
elif question_type == "c":
print("Loading...")
time.sleep(1)
if platform.system() == "Windows":
os.system('cls')
else:
os.system('clear')
main()
else:
print(Fore.RED + "Invalid value" + Style.RESET_ALL)
if len(questions) > 0:
save_questions(questions)
print(Fore.GREEN + "Questions saved successfully" + Style.RESET_ALL)
else:
print(Fore.RED + "No questions to save" + Style.RESET_ALL)
def add_quiz_question():
'''
Adds quiz questions and multiple possible answers
'''
question = input("Enter the quiz question: ").strip()
if not question:
print(Fore.RED + "Question cannot be empty." + Style.RESET_ALL)
return
choices = []
print("Enter multiple choices for the question: ")
while True:
choice = input("Choice: ").strip().lower()
if not choice:
print(Fore.RED + "No choices entered" + Style.RESET_ALL)
return
elif choice == "done":
if len(choices) < 3:
print(Fore.RED + "Minimum of 3 choices required" + Style.RESET_ALL)
choices = []
continue
break
else:
choices.append(choice)
loading()
try:
answer = int(input("Enter the number of the correct answer: ")) - 1
if answer < 0 or answer >= len(choices):
raise ValueError("Answer must be a valid choice number.")
except ValueError as e:
print(Fore.RED + f"Error: {e}" + Style.RESET_ALL)
return
questions.append(Quiz(question, choices, answer))
print(Fore.GREEN + "Your question has been added\n" + Style.RESET_ALL)
def add_freeform_question():
'''
Adds freeform questions and answers
'''
question = input("Enter the question: ")
answer = input("Enter the answer: ")
if not question or not answer:
print(Fore.RED + "Both question and answer cannot be empty")
return
questions.append(Freeform(question, answer))
print(Fore.GREEN + "Your question has been added\n" + Style.RESET_ALL)
def save_questions(questions, file_name="questions.csv"):
'''
Saves submitted questions to a CSV file
'''
with open(file_name, "w", newline='', encoding='utf-8') as file:
writer = csv.DictWriter(file, fieldnames=["question", "type", "choices", "answer", "enabled", "times_shown", "correct_count"])
writer.writeheader()
for question in questions:
row = question.__dict__.copy()
if isinstance(question, Quiz):
row['type'] = 'quiz'
row['choices'] = ', '.join(question.choices)
elif isinstance(question, Freeform):
row['type'] = 'freeform'
row['choices'] = ''
writer.writerow(row)
def load_questions(file_name="questions.csv"):
'''
Loads questions from a CSV file
'''
if not os.path.exists(file_name):
print("No questions found. The file does not exist.")
return []
questions = []
question_id = 1
with open(file_name, encoding='utf-8') as file:
reader = csv.DictReader(file)
for row in reader:
try:
enabled = row.get('enabled', 'True').lower() == 'true'
times_shown = int(row.get('times_shown', 0))
correct_count = int(row.get('correct_count', 0))
if row['type'] == 'quiz':
question = Quiz(row['question'], row['choices'].split(', '), row['answer'], enabled, times_shown, correct_count)
questions.append(question)
elif row['type'] == 'freeform':
question = Freeform(row['question'], row['answer'], row['enabled'])
question.id = question_id
questions.append(question)
question_id += 1
except ValueError as e:
print(Fore.RED + f"Error reading row: {row} - {e}" + Style.RESET_ALL)
return questions
def view_statistics(questions):
'''
Displays statistics for available questions
'''
if not questions:
print(Fore.RED + "No questions available" + Style.RESET_ALL)
return
print("Question Statistics:")
print("===============================================")
for question in questions:
correct_percentage = (question.correct_count / question.times_shown * 100 if question.times_shown > 0 else 0)
# active_status = (Fore.GREEN + "Enabled" + Style.RESET_ALL if question["active"] else Fore.RED + "Disabled" + Style.RESET_ALL)
print(f"ID: {question.id} | Status: {question.enabled}")
print(f"Type: {question.type}")
print(f"Question: {question.question}")
print(f"Times Shown: {question.times_shown}")
print(f"Correct Percentage: {question.correct_count}%")
print("===============================================")
def mode_validation(user_input):
'''
Validates user input for mode selection
'''
input_value = user_input.lower()
try:
if input_value not in ['a', 'b', 'c', 'd', 'e', 'f']:
raise ValueError("Please enter a valid mode")
except ValueError as e:
print(Fore.RED + f"Error: {e}")
return False
return True
def main():
'''
User can choose any of the available modes to run the program
'''
while True:
print(Fore.GREEN + "Choose a mode to run the program:" + Style.RESET_ALL)
print("=====================================")
print("(a) Adding questions")
print("(b) Statistics viewing")
print("(c) Disable / Enable questions")
print("(d) Practice mode")
print("(e) Test mode")
print("(f) End program")
print("=====================================")
selection = input("You choose: ")
if mode_validation(selection):
loading()
break
if selection == 'a':
select_quiz_type()
elif selection == 'b':
questions = load_questions()
view_statistics(questions)
elif selection == 'c':
...
elif selection == 'd':
...
elif selection == 'e':
...
else:
print("Exiting program...Bye!")
sys.exit(1)
if __name__ == "__main__":
main()
and here is my questions.py file to hold the classes
class Question:
def __init__(self, question, answer, enabled=True, times_shown=0, correct_count=0):
self.question = question
self.answer = answer
self.enabled = enabled
self.times_shown = times_shown
self.correct_count = correct_count
def __str__(self):
return f"Question: {self.question}\nAnswer: {self.answer}\nEnabled: {self.enabled}"
class Quiz(Question):
def __init__(self, question, choices, answer, enabled=True):
super().__init__(question, answer, enabled)
self.choices = choices
def __str__(self):
return super().__str__() + f"\nChoices: {self.choices}"
class Freeform(Question):
def __init__(self, question, answer, enabled=True):
super().__init__(question, answer, enabled)
Without seeing your questions.py file is difficult to test your code, but I tried to guess how the functions (Quiz, Question) inside your script work. For me view_statistics() works as seen in this output:
Choose a mode to run the program:
=====================================
(a) Adding questions
(b) Statistics viewing
(c) Disable / Enable questions
(d) Practice mode
(e) Test mode
(f) End program
=====================================
You choose: b
Loading...
Question Statistics:
===============================================
ID: 1 | Status: True
Type: quiz
Question: test
Times Shown: 0
Correct Percentage: 0%
===============================================
ID: 1 | Status: True
Type: quiz
Question: test
Times Shown: 0
Correct Percentage: 0%
===============================================
So, my guessues at why your code is not working for you are:
You try to save your questions but they are not actually saved, thus building an empty questions.csv file (so when your code tries to read them and display statistics, you get the "No questions available" notification).
The file questions.csv is in the wrong place/is missing, and since your load_questions() function expects questions.csv in the same folder as the script, if your script can’t find it, it will also return an empty list.
Edit: Here I leave you with the Quiz and Question classes I defined, maybe they work for you as well. Also I added "id" in the fieldnames
of csv.DictWriter
.
class Question:
def __init__(self, question, answer, enabled=True, times_shown=0, correct_count=0):
self.question = question
self.answer = answer
self.enabled = enabled
self.times_shown = times_shown
self.correct_count = correct_count
self.id = None
self.type = None
class Quiz(Question):
def __init__(self, question, choices, correct_answer, enabled=True, times_shown=0, correct_count=0):
super().__init__(question, correct_answer, enabled, times_shown, correct_count)
self.choices = choices
self.type = 'quiz'