Generating Random Quiz Files
Tags: python, automation
Published: 2023-12-07 12:51:47

Welcome to the first project in this tutorial series. In this project, you will learn how to read and write to files while creating random quiz files. You will broaden your knowledge of the random module by using it to shuffle questions and answers. By the end of this project, you will have automated the creation of random quiz files.

What you will gain from this project:

- You will learn how to use the os module for file operations.
- You will work with the random module to enhance your Python skills.

## Prerequisites

- To get the most out of this project, you should already be familiar with "[Automate the Boring Stuff](https://drive.google.com/file/d/1xvF0KdMVNhrCb9St4OWs0L8d4nbsM1o-/view?usp=drivesdk): 'Python Programming Basics' and 'Reading and Writing to Files'".
- Willingness to learn and tackle more complex exercises.

### How to follow this project:
For clarity and to avoid confusion, especially with nested loops:

1. Open the project file in a separate tab and always refer back to it.
2. The “...” was used to indicate that the code has been truncated, just as you would do in your code editor when dealing with long lines of code. 
3. Avoid copying and pasting from here; instead, use the project file.
4. It is often better to write small sections of code and test it.

Now, let me walk you through the steps to generate random quiz files. To access the project file, click [here](https://github.com/praise002/Automating-the-boring-stuff-with-Python-blog-project/tree/master/chapter-8/generate-random-quiz-files).

**Step 1**: Write Comments.

```
def generate_random_quiz_files():
    # Create a directory to store quiz and answer key files  
    # Generate 35 quiz files.
    # write out the header for the quiz        
    # Shuffle the order of the states         
    # Loop through all 50 states, making a question for each         
    # Create a list of answer options            
    # Write the question and answer options in the quiz file            
    # Write the correct answer to the answer file
    pass

if __name__ == '__main__':
    generate_random_quiz_files()

```

**Step 2**: Create a file and name it `dict.py`.
This file contains the dictionary that stores the states and capitals. To access the dict file, click [here](https://github.com/praise002/Automating-the-boring-stuff-with-Python-blog-project/blob/master/chapter-8/generate-random-quiz-files/dict.py). Below is a sample of what it looks like in the repo.

```
capitals = {'Alabama': 'Montgomery', 'Alaska': 'Juneau', 'Arizona': 'Phoenix', 'Arkansas': 'Little Rock', 'California': 'Sacramento', 'Colorado': 'Denver',...}
```

**Step 3**: Import the Required Modules.
You will import the random module, the `os` module, and the dictionary.

```
import random, os
from dict import capitals
```

**Step 4**: Create a directory and subdirectories to store quiz and answer files.
To ensure the program functions smoothly, start by checking if the path exists. If it doesn't, proceed to create the 'quizzes' directory. Note that we are using relative paths. If you prefer to use absolute paths, you can call the `os.path.abspath()` function.
After creating the directory, to create a directory tree, you will use the `os.makedirs` function.

```
def generate_random_quiz_files():
    # Create a directory to store quiz and answer files
    if not os.path.exists('quizzes'):
        os.mkdir('quizzes/')
    if not os.path.exists('quizzes/questions):
        os.makedirs('quizzes/questions)
    if not os.path.exists('quizzes/answers'):
        os.makedirs('quizzes/answers')
```

Run the program, and only proceed to the next step if it works correctly. You should achieve a directory structure as shown below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64eiykjxdzua5vvl71g7.png)


**Step 5**: Generate 35 quiz files.
Remember: The range function starts from 0 if no starting point is indicated and loops up to the endpoint, excluding the endpoint. For example, `range(35)` starts from 0 and ends at 34, resulting in 35 files.
Next, you will generate the question_filename. Use the `os.path.join()` function and f-strings to create the filename.
Lastly, test the program to ensure it functions correctly. The quickest way to do this is by using the `print()` statement. If everything works well, you can proceed to the next step; otherwise, troubleshoot.

```
def generate_random_quiz_files():
    # Create a directory to store quiz and answer key files
    #...
       
    # Generate 35 quiz files.
    for quizNum in range(35):
        # Create the quiz and answer key files.
        question_filename = os.path.join('quizzes', 'questions', f'quiz_{quizNum + 1}.txt')
        answer_filename = os.path.join('quizzes', 'answers', f'quiz_{quizNum + 1}_answer.txt')
        print(question_filename)
        print(answer_filename)
```

**Step 6**: Write the header for the quiz.
First, comment out the print statement for clarity.
Next, open the file in write mode. You use write mode to overwrite any previous generation and ensure a fresh start. The `os.path.exists()` check you implemented earlier helps handle pre-existing folders. You also add an encoding to prevent encoding error issues, then proceed to write to the file. Ensure that you run the program to confirm it works as expected.
Here's the updated code:

```
def generate_random_quiz_files():
    # Create a directory to store quiz and answer key files
    #...
    # Generate 35 quiz files.
    #...
       
        with open(question_filename, 'w', encoding='utf-8') as question_file, \
            open(answer_filename, 'w', encoding='utf-8') as answer_file:
                # write out the header for the quiz
                question_file.write('Name:\n\nDate:\n\nPeriod:\n\n')
                question_file.write(f'{" " * 20}State Capitals Quiz - Student {quizNum + 1}\n\n')
                answer_file.write(f'US State Capitals Answers - Student {quizNum + 1}\n\n')
               

```
**Step 7**:  Shuffle the order of the states
First, retrieve the keys, which represent the states. Store them in a variable called states, and convert this into a list because we will need a list in the next step.
Next, shuffle the list. It is important to note that the shuffle method modifies the original list instead of creating a new one. Test it to ensure it works as expected. Try storing the shuffled list in a variable and observe the results. This will clarify the behavior of the shuffle operation.

```
def generate_random_quiz_files():
    # Create a directory to store quiz and answer key files
    #...
    # Generate 35 quiz files.
    #...
       
        with open(question_filename, 'w', encoding='utf-8') as question_file, \
            open(answer_filename, 'w', encoding='utf-8') as answer_file:
                # write out the header for the quiz
                #...
               
                # Shuffle the order of the states
                states = list(capitals.keys())
                # print(states)
                random.shuffle(states)  # it does not return a new list instead it changes d original list
                print(states)
```

**Step 8**: Loop through all 50 states, making a question for each.
Run your code to see the results. Comment out the print statement before moving on to the next step.

```
def generate_random_quiz_files():
    # Create a directory to store quiz and answer key files
    #...
    # Generate 35 quiz files.
    #...
       
        with open(question_filename, 'w', encoding='utf-8') as question_file, \
            open(answer_filename, 'w', encoding='utf-8') as answer_file:
                # write out the header for the quiz
                #...
               
                # Shuffle the order of the states
                #...


                # Loop through all 50 states, making a question for each
                for question_num in range(50):  # it generates 1750 questions
                    state = states[question_num]  # get questions 1-50 from the list
                    correct_answer = capitals[state]  # get the capital of the state from the capitals dictionary
                    print(state)
                    print(correct_answer)
```
                   

**Step 9**: Create a list of answer options.

```
def generate_random_quiz_files():
    # Create a directory to store quiz and answer key files
    #...
    # Generate 35 quiz files.
    #...
       
        with open(question_filename, 'w', encoding='utf-8') as question_file, \
            open(answer_filename, 'w', encoding='utf-8') as answer_file:
                # write out the header for the quiz
                #...
               
                # Shuffle the order of the states
                #...


                # Loop through all 50 states, making a question for each
                for question_num in range(50):  # it generates 1750 questions
                    #...
                   
                    # Create a list of answer options
                    answer_options = list(capitals.values())
                    del answer_options[answer_options.index(correct_answer)]  # delete the correct answer from d anaswer options
                    wrong_answers = random.sample(answer_options, 3)
                    answer_options = wrong_answers + [correct_answer]  # conactenate list with list
                    print(answer_options)
                    random.shuffle(answer_options)
                    print(answer_options)
```
                   

**Step 10**: Write the question and answer options.
In this step, you will write the questions and answer options to the quiz file.

**Step 11**: Write the correct answer to the answer key file.
This is the last step and you can refer back to the code for clarity. Here is the completed code:

```
import random, os
from dict import capitals


def generate_random_quiz_files():
    # Create a directory to store quiz and answer key files
    if not os.path.exists('quizzes'):
        os.mkdir('quizzes/')
    if not os.path.exists('quizzes/questions'):
        os.makedirs('quizzes/questions')
    if not os.path.exists('quizzes/answers'):
        os.makedirs('quizzes/answers')
       
    # Generate 35 quiz files.
    for quizNum in range(35):
        # Create the quiz and answer key files.
        question_filename = os.path.join('quizzes', 'questions', f'quiz_{quizNum + 1}.txt')
        answer_filename = os.path.join('quizzes', 'answers', f'quiz_{quizNum + 1}_answer.txt')
        # print(question_file)
        # print(answer_file)
       
        with open(question_filename, 'w', encoding='utf-8') as question_file, \
            open(answer_filename, 'w', encoding='utf-8') as answer_file:
                # write out the header for the quiz
                question_file.write('Name:\n\nDate:\n\nPeriod:\n\n')
                question_file.write(f'{" " * 20}State Capitals Quiz - Student {quizNum + 1}\n\n')
                answer_file.write(f'US State Capitals Answers - Student {quizNum + 1}\n\n')
               
                # Shuffle the order of the states
                states = list(capitals.keys())
                # print(states)
                random.shuffle(states)  # it does not return a new list instead it changes d original list
                # print(states)


                # Loop through all 50 states, making a question for each
                for question_num in range(50):  # it generates 1750 questions
                    state = states[question_num]  # get questions 1-50 from the list
                    correct_answer = capitals[state]  # get the capital of the state from the capitals dictionary
                    # print(state)
                    # print(correct_answer)
                   
                    # Create a list of answer options
                    answer_options = list(capitals.values())
                    del answer_options[answer_options.index(correct_answer)]  # delete the correct answer from d anaswer options
                    wrong_answers = random.sample(answer_options, 3)
                    answer_options = wrong_answers + [correct_answer]  # conactenate list with list
                    # print(answer_options)
                    random.shuffle(answer_options)
                    # print(answer_options)
                   
                    # Write the question and answer options to the quiz file
                    question_file.write(f'{question_num + 1}. What is the capital of {state}?\n')
                    for index, option in enumerate(answer_options):
                        question_file.write(f'{"ABCD"[index]}. {option}\n')
                    question_file.write('\n')
                   
                    # Write the correct answer to the answer key file
                    answer_file.write(f'{question_num + 1}. {"ABCD"[answer_options.index(correct_answer)]}\n')


if __name__ == '__main__':
    generate_random_quiz_files()

```
It's been a great journey of learning and practicing. Don’t worry if you are confused, everyone does. Now it is time for some practice.

### Exercise
1. Your task is to write a program that converts the dictionary to JSON format. After that, you will modify the code to use the JSON file instead.
2. You will notice that there is a lot of nesting in the code which makes it less readable. Your task is to create smaller functions to get rid of the nesting.

## Conclusion
By now, you should have completed the exercises and gained practical experience. Remember, getting stuck is a normal part of the learning process. In this project, you learned how to generate random quiz files using the OS and random modules. You also implemented complex loops, used range functions, and mastered writing data to a file.

This project has equipped you with essential skills that you can apply in various Python programming tasks. Continue to explore, practice, and refine your coding abilities.

If you have any questions, want to connect, or just fancy a chat, feel free to reach out to me on [LinkedIn](https://www.linkedin.com/in/praise-ifeoluwa-idowu-back-end-developer-django?utm_source=share&utm_campaign=share_via&utm_content=profile&utm_medium=android_app) and [Twitter](https://twitter.com/PraiseID3?s=09).