pythonfunctionclassdebugging

Unable to display statistics viewing for my python project


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)

Solution

  • 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:

    1. 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).

    2. 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'