API and Python Script to list Quizzes that have Shuffle turned on and "All of the above" etc answers
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
My use case is to search a list courses that contain Classic Quizzes and to list any Quizzes that have "shuffle_answers":true and that contain one or more questions that have "All of the above" as a answer or distractor.
Unfortunately we have thousands of quizzes and most probably 1-2 % would fall into this category. 😞
Being able to filter out a list of all quizzes with "shuffle_answers":true would be a start but I can't work out a smart way to then search the questions in the quizzes for "All of the above" or "Both A & D" or "None of the above" type distractors.
I am hoping that I don't have to write a AHK script to do this.
I would like a Python Script to do this if at all possible.
The answer to this question may hold some clues.
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Working with Chat GPT I have a working python script as follows:
# Fetches classic quizzes and associated questions from a list of Canvas courses.
# Not tested with new quizzes
# Retrieves quiz details including: Course Name Quiz Name, Number of Questions, Shuffle Answers, Published, Contains Search Strings
# Generates a CSV report
# Code Generated by Chat GPT 14/04/2024
# With prompts by Rupert Russell
import os
import requests
import csv
import json
# Define your Canvas API URL and access token
API_URL = "https://....instructure.com/api/v1/"
ACCESS_TOKEN = '...' # generate your own token
# Function to fetch quizzes for a course from Canvas
def get_quizzes(course_id):
url = f"{API_URL}/courses/{course_id}/quizzes"
headers = {"Authorization": f"Bearer {ACCESS_TOKEN}"}
quizzes = []
page = 1
while True:
params = {"page": page}
try:
response = requests.get(url, headers=headers, params=params)
response.raise_for_status() # Raise an exception for 4xx/5xx status codes
quizzes_page = json.loads(response.text) # Convert response text to dictionary using json.loads()
quizzes.extend(quizzes_page)
if "next" not in response.links:
break # No more pages to fetch
page += 1
except requests.exceptions.RequestException as e:
print(f"Error fetching quizzes for course {course_id}: {e}")
return [] # Return an empty list if there's an error
return quizzes
# Function to fetch questions for a quiz from Canvas
def get_quiz_questions(course_id, quiz_id):
url = f"{API_URL}/courses/{course_id}/quizzes/{quiz_id}/questions"
headers = {"Authorization": f"Bearer {ACCESS_TOKEN}"}
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # Raise an exception for 4xx/5xx status codes
return json.loads(response.text) # Convert response text to dictionary using json.loads()
except requests.exceptions.RequestException as e:
print(f"Error fetching questions for quiz {quiz_id} in course {course_id}: {e}")
return [] # Return an empty list if there's an error
# Function to get quiz details from Canvas
def get_quiz_details(course_id, quiz_id):
url = f"{API_URL}/courses/{course_id}/quizzes/{quiz_id}"
headers = {"Authorization": f"Bearer {ACCESS_TOKEN}"}
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # Raise an exception for 4xx/5xx status codes
return json.loads(response.text) # Convert response text to dictionary using json.loads()
except requests.exceptions.RequestException as e:
print(f"Error fetching details for quiz {quiz_id} in course {course_id}: {e}")
return {} # Return an empty dictionary if there's an error
# Function to fetch question bank details from Canvas
def get_question_bank_details(course_id, question_bank_id):
url = f"{API_URL}/courses/{course_id}/question_groups/{question_bank_id}"
headers = {"Authorization": f"Bearer {ACCESS_TOKEN}"}
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # Raise an exception for 4xx/5xx status codes
return json.loads(response.text) # Convert response text to dictionary using json.loads()
except requests.exceptions.RequestException as e:
print(f"Error fetching details for question bank {question_bank_id} in course {course_id}: {e}")
return {} # Return an empty dictionary if there's an error
# Main function to fetch courses with quizzes and save as CSV
def main(course_numbers, search_strings):
total_courses = len(course_numbers)
csv_version = 1
while True:
csv_filename = f'courses_with_quizzes_v{csv_version:03d}.csv'
if not os.path.exists(csv_filename):
break
csv_version += 1
with open(csv_filename, 'w', newline='') as csvfile:
fieldnames = ['Course Name', 'Quiz Name', 'Number of Questions', 'Shuffle Answers', 'Published', 'Contains Search Strings']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for i, course_number in enumerate(course_numbers, start=1):
print(f"Checking course {i}/{total_courses}")
quizzes = get_quizzes(course_number)
for quiz in quizzes:
shuffle_answers = 'True' if quiz.get('shuffle_answers', False) else 'False'
quiz_questions = get_quiz_questions(course_number, quiz['id'])
num_questions = len(quiz_questions)
contains_search_strings = any(
any(search_string.lower() in choice['text'].lower() for choice in question.get('answers', []))
for question in quiz_questions
for search_string in search_strings
)
# Check questions in associated question banks
question_bank_ids = [group['id'] for group in quiz.get('question_groups', [])]
for question_bank_id in question_bank_ids:
question_bank_details = get_question_bank_details(course_number, question_bank_id)
if not question_bank_details:
continue # Skip if unable to fetch question bank details
questions_in_bank = question_bank_details.get('questions', [])
contains_search_strings |= any(
any(search_string.lower() in choice['text'].lower() for choice in question.get('answers', []))
for question in questions_in_bank
for search_string in search_strings
)
quiz_details = get_quiz_details(course_number, quiz['id'])
is_published = quiz_details.get('published', False)
writer.writerow({'Course Name': course_number, 'Quiz Name': quiz['title'], 'Number of Questions': num_questions, 'Shuffle Answers': shuffle_answers, 'Published': is_published, 'Contains Search Strings': contains_search_strings})
print(f"CSV file '{csv_filename}' has been created.")
if __name__ == "__main__":
# Example usage:
courses = [522, 20451]
search_strings = ["All of the above", "None of the above", "Some other string"]
main(courses, search_strings)