CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-canvasapi

API wrapper for the Canvas LMS

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

quizzes-assessments.mddocs/

Quizzes & Assessments

Quiz creation, question management, submissions, and both Classic Quizzes and New Quizzes support. Comprehensive assessment tools for measuring student learning and progress.

Capabilities

Quiz Management

Create and manage quizzes with comprehensive configuration options.

class Course(CanvasObject):
    def get_quizzes(self, **kwargs) -> PaginatedList[Quiz]:
        """
        List quizzes in the course.
        
        Parameters:
        - search_term: Search term to filter quizzes
        - include: Additional data ('assignment', 'overrides', 'all_dates', 'quiz_reports', 'submission_questions', 'html_url', 'mobile_url')
        
        Returns:
        Paginated list of Quiz objects
        """
    
    def create_quiz(self, quiz: dict, **kwargs) -> Quiz:
        """
        Create a new quiz.
        
        Parameters:
        - quiz: Dictionary with quiz attributes:
            - title: Quiz title (required)
            - description: Quiz description (HTML)
            - quiz_type: Quiz type ('practice_quiz', 'assignment', 'graded_survey', 'survey')
            - assignment_group_id: Assignment group ID
            - time_limit: Time limit in minutes
            - shuffle_answers: Shuffle answer choices
            - hide_results: Hide results ('always', 'until_after_last_attempt', None)
            - show_correct_answers: Show correct answers after submission
            - show_correct_answers_last_attempt: Show correct answers only after last attempt
            - show_correct_answers_at: Date to show correct answers
            - hide_correct_answers_at: Date to hide correct answers
            - allowed_attempts: Number of allowed attempts (-1 for unlimited)
            - scoring_policy: Scoring policy ('keep_highest', 'keep_latest')
            - one_question_at_a_time: Show one question at a time
            - cant_go_back: Prevent going back to previous questions
            - access_code: Access code required to take quiz
            - ip_filter: IP address filter
            - due_at: Due date
            - lock_at: Lock date
            - unlock_at: Unlock date
            - published: Whether quiz is published
            - one_time_results: Show results only once
            - only_visible_to_overrides: Only visible to override recipients
        
        Returns:
        Quiz object
        """
    
    def get_quiz(self, quiz, **kwargs) -> Quiz:
        """
        Get a single quiz by ID.
        
        Parameters:
        - quiz: Quiz object or quiz ID
        - include: Additional data to include
        
        Returns:
        Quiz object
        """

class Quiz(CanvasObject):
    def edit(self, **kwargs) -> Quiz:
        """
        Edit quiz properties.
        
        Parameters:
        - quiz: Dictionary with quiz updates (same structure as create_quiz)
        
        Returns:
        Updated Quiz object
        """
    
    def delete(self, **kwargs) -> Quiz:
        """Delete the quiz."""

Question Management

Create and manage quiz questions with various question types.

def get_questions(self, **kwargs) -> PaginatedList[QuizQuestion]:
    """
    List questions for the quiz.
    
    Parameters:
    - quiz_submission_id: Filter by specific submission
    - quiz_submission_attempt: Filter by submission attempt
    
    Returns:
    Paginated list of QuizQuestion objects
    """

def create_question(self, question: dict, **kwargs) -> QuizQuestion:
    """
    Create a quiz question.
    
    Parameters:
    - question: Dictionary with question attributes:
        - question_name: Question name
        - question_text: Question text (HTML)
        - question_type: Question type ('multiple_choice_question', 'true_false_question', 
                        'short_answer_question', 'fill_in_multiple_blanks_question', 
                        'multiple_answers_question', 'multiple_dropdowns_question',
                        'matching_question', 'numerical_question', 'calculated_question',
                        'essay_question', 'uploaded_data_question', 'file_upload_question',
                        'text_only_question')
        - points_possible: Points for the question
        - correct_comments: Comments for correct answers
        - incorrect_comments: Comments for incorrect answers
        - neutral_comments: General comments
        - text_after_answers: Text to display after answers
        - answers: List of answer choices (structure varies by question type)
        - variables: Variables for calculated questions
        - formulas: Formulas for calculated questions
        - answer_tolerance: Tolerance for numerical questions
        - formula_decimal_places: Decimal places for calculated questions
        - matches: Matches for matching questions
        - matching_answer_incorrect_matches: Incorrect matches for matching questions
    
    Returns:
    QuizQuestion object
    """

def get_question(self, question_id, **kwargs) -> QuizQuestion:
    """Get a single quiz question by ID."""

class QuizQuestion(CanvasObject):
    def edit(self, **kwargs) -> QuizQuestion:
        """
        Edit quiz question.
        
        Parameters:
        - question: Dictionary with question updates
        
        Returns:
        Updated QuizQuestion object
        """
    
    def delete(self, **kwargs) -> QuizQuestion:
        """Delete the quiz question."""

Quiz Submissions

Handle student quiz attempts and submissions.

def get_submissions(self, **kwargs) -> PaginatedList[QuizSubmission]:
    """
    Get quiz submissions.
    
    Parameters:
    - include: Additional data ('submission', 'quiz', 'user')
    
    Returns:
    Paginated list of QuizSubmission objects
    """

def get_submission(self, quiz_submission_id, **kwargs) -> QuizSubmission:
    """
    Get a single quiz submission.
    
    Parameters:
    - quiz_submission_id: Quiz submission ID
    - include: Additional data to include
    
    Returns:
    QuizSubmission object
    """

def create_submission(self, **kwargs) -> QuizSubmission:
    """
    Start a quiz attempt.
    
    Parameters:
    - access_code: Access code if required
    - preview: Whether this is a preview
    
    Returns:
    QuizSubmission object
    """

class QuizSubmission(CanvasObject):
    def complete(self, **kwargs) -> QuizSubmission:
        """
        Complete/submit the quiz attempt.
        
        Parameters:
        - attempt: Attempt number
        - validation_token: Validation token
        - access_code: Access code if required
        
        Returns:
        Completed QuizSubmission object
        """
    
    def get_questions(self, **kwargs) -> PaginatedList[QuizSubmissionQuestion]:
        """
        Get questions for this submission.
        
        Parameters:
        - include: Additional data ('quiz_question')
        
        Returns:
        Paginated list of QuizSubmissionQuestion objects
        """
    
    def answer_submission_questions(self, **kwargs) -> QuizSubmissionQuestion:
        """
        Answer quiz questions.
        
        Parameters:
        - attempt: Attempt number
        - validation_token: Validation token
        - access_code: Access code if required
        - quiz_questions: List of question answers:
            - id: Question ID
            - answer: Answer value (format depends on question type)
        
        Returns:
        QuizSubmissionQuestion object
        """
    
    def get_times(self, **kwargs) -> dict:
        """Get timing data for the quiz submission."""
    
    def update_score_and_comments(self, **kwargs) -> QuizSubmission:
        """
        Update submission score and comments (for manually graded questions).
        
        Parameters:
        - quiz_submissions: List of submission updates:
            - attempt: Attempt number
            - fudge_points: Fudge points to add/subtract
            - questions: Dictionary of question updates with comments and scores
        
        Returns:
        Updated QuizSubmission object
        """

Quiz Statistics and Analytics

Access quiz performance data and analytics.

def get_statistics(self, **kwargs) -> QuizStatistic:
    """
    Get quiz statistics.
    
    Parameters:
    - all_versions: Include all quiz versions
    
    Returns:
    QuizStatistic object with performance data
    """

def get_reports(self, **kwargs) -> PaginatedList[QuizReport]:
    """
    Get available quiz reports.
    
    Returns:
    Paginated list of QuizReport objects
    """

def create_report(self, report_type: str, **kwargs) -> QuizReport:
    """
    Create a quiz report.
    
    Parameters:
    - report_type: Type of report ('student_analysis', 'item_analysis')
    - quiz_report: Dictionary with report settings:
        - includes_all_versions: Include all quiz versions
    
    Returns:
    QuizReport object
    """

class QuizReport(CanvasObject):
    def get_file(self, **kwargs) -> File:
        """Get the report file once generation is complete."""

New Quizzes Integration

Support for Canvas New Quizzes (Quizzes.Next) platform.

def create_new_quiz(self, **kwargs) -> NewQuiz:
    """
    Create a new quiz using New Quizzes platform.
    
    Parameters:
    - title: Quiz title
    - instructions: Quiz instructions
    - quiz_type: Quiz type
    - allowed_attempts: Number of allowed attempts
    - time_limit: Time limit in minutes
    - due_at: Due date
    - points_possible: Total points
    
    Returns:
    NewQuiz object
    """

class NewQuiz(CanvasObject):
    """
    Represents a New Quiz (Quizzes.Next) assessment.
    Limited API functionality compared to Classic Quizzes.
    """
    def edit(self, **kwargs) -> NewQuiz:
        """Edit new quiz properties."""
    
    def delete(self, **kwargs) -> NewQuiz:
        """Delete the new quiz."""

Question Groups and Banks

Organize questions into groups and banks for reuse.

def get_groups(self, **kwargs) -> PaginatedList[QuizGroup]:
    """
    Get question groups for the quiz.
    
    Returns:
    Paginated list of QuizGroup objects
    """

def create_group(self, quiz_groups: list, **kwargs) -> QuizGroup:
    """
    Create a question group.
    
    Parameters:
    - quiz_groups: List of group dictionaries:
        - name: Group name
        - pick_count: Number of questions to pick from group
        - question_points: Points per question
        - assessment_question_bank_id: Question bank to draw from
    
    Returns:
    QuizGroup object
    """

class QuizGroup(CanvasObject):
    def edit(self, **kwargs) -> QuizGroup:
        """Edit question group properties."""
    
    def delete(self, **kwargs) -> QuizGroup:
        """Delete the question group."""

Usage Examples

Creating Comprehensive Quizzes

from canvasapi import Canvas

canvas = Canvas("https://canvas.example.com", "your-token")
course = canvas.get_course(12345)

# Create a quiz with comprehensive settings
quiz = course.create_quiz({
    'title': 'Midterm Examination',
    'description': '<p>This exam covers chapters 1-5. You have 90 minutes to complete it.</p>',
    'quiz_type': 'assignment',
    'assignment_group_id': exam_group.id,
    'time_limit': 90,
    'allowed_attempts': 1,
    'scoring_policy': 'keep_highest',
    'shuffle_answers': True,
    'show_correct_answers': False,  # Don't show answers immediately
    'show_correct_answers_at': '2024-12-01T09:00:00Z',  # Show after exam period
    'one_question_at_a_time': True,
    'cant_go_back': True,  # Prevent students from going back
    'due_at': '2024-11-15T23:59:59Z',
    'unlock_at': '2024-11-15T08:00:00Z',
    'lock_at': '2024-11-15T23:59:59Z',
    'points_possible': 100,
    'published': False  # Keep unpublished until ready
})

print(f"Created quiz: {quiz.title} (ID: {quiz.id})")

Adding Different Question Types

# Multiple choice question
mc_question = quiz.create_question({
    'question_name': 'Python Data Types',
    'question_text': 'Which of the following is a mutable data type in Python?',
    'question_type': 'multiple_choice_question',
    'points_possible': 5,
    'answers': [
        {
            'answer_text': 'String',
            'answer_weight': 0,
            'answer_comments': 'Strings are immutable in Python.'
        },
        {
            'answer_text': 'Tuple', 
            'answer_weight': 0,
            'answer_comments': 'Tuples are immutable in Python.'
        },
        {
            'answer_text': 'List',
            'answer_weight': 100,  # Correct answer
            'answer_comments': 'Correct! Lists are mutable in Python.'
        },
        {
            'answer_text': 'Integer',
            'answer_weight': 0,
            'answer_comments': 'Integers are immutable in Python.'
        }
    ],
    'correct_comments': 'Well done! Lists can be modified after creation.',
    'incorrect_comments': 'Review the differences between mutable and immutable data types.'
})

# True/False question
tf_question = quiz.create_question({
    'question_name': 'Python Syntax',
    'question_text': 'Python uses curly braces {} to define code blocks.',
    'question_type': 'true_false_question',
    'points_possible': 3,
    'answers': [
        {
            'answer_text': 'True',
            'answer_weight': 0,
            'answer_comments': 'Python uses indentation, not curly braces.'
        },
        {
            'answer_text': 'False',
            'answer_weight': 100,
            'answer_comments': 'Correct! Python uses indentation to define code blocks.'
        }
    ]
})

# Short answer question
sa_question = quiz.create_question({
    'question_name': 'Function Definition',
    'question_text': 'What keyword is used to define a function in Python?',
    'question_type': 'short_answer_question',  
    'points_possible': 2,
    'answers': [
        {
            'answer_text': 'def',
            'answer_weight': 100
        },
        {
            'answer_text': 'define',
            'answer_weight': 0
        }
    ]
})

# Essay question
essay_question = quiz.create_question({
    'question_name': 'Algorithm Analysis',
    'question_text': 'Explain the difference between O(n) and O(log n) time complexity. Provide an example of each.',
    'question_type': 'essay_question',
    'points_possible': 15,
    'correct_comments': 'Review your answer for completeness and accuracy.'
})

# Numerical question
num_question = quiz.create_question({
    'question_name': 'Calculation',
    'question_text': 'What is the result of 2^10?',
    'question_type': 'numerical_question',
    'points_possible': 3,
    'answers': [
        {
            'answer_exact': 1024,
            'answer_error_margin': 0,
            'answer_weight': 100
        }
    ],
    'answer_tolerance': 0
})

Managing Quiz Submissions

# Get all submissions for grading
submissions = quiz.get_submissions(include=['submission', 'user'])

print(f"Found {len(submissions)} submissions")

for submission in submissions:
    user = submission.user
    print(f"Student: {user['name']}")
    print(f"Score: {submission.score}/{quiz.points_possible}")
    print(f"Attempt: {submission.attempt}")
    print(f"Time taken: {submission.time_spent} seconds")
    print(f"Submitted: {submission.submitted_at}")
    
    # Get submission questions for detailed analysis
    questions = submission.get_questions(include=['quiz_question'])
    
    for question in questions:
        print(f"  Q: {question.question_text[:50]}...")
        print(f"  Answer: {question.answer}")
        print(f"  Correct: {question.correct}")
        print(f"  Points: {question.points_awarded}/{question.points_possible}")

# Grade essay questions manually
for submission in submissions:
    if submission.workflow_state == 'pending_review':
        # Update scores for manually graded questions
        submission.update_score_and_comments(
            quiz_submissions=[{
                'attempt': submission.attempt,
                'fudge_points': 2,  # Add 2 bonus points
                'questions': {
                    essay_question.id: {
                        'score': 12,  # Out of 15 points
                        'comment': 'Good explanation of time complexity. Could provide more detailed examples.'
                    }
                }
            }]
        )

Creating Question Groups

# Create a question group that randomly selects from a question bank
question_group = quiz.create_group([{
    'name': 'Random Multiple Choice',
    'pick_count': 5,  # Pick 5 questions randomly
    'question_points': 4,  # Each question worth 4 points
    'assessment_question_bank_id': question_bank.id
}])

# Create a group with specific questions
specific_group = quiz.create_group([{
    'name': 'Required Questions',
    'pick_count': 3,
    'question_points': 5
}])

# Add specific questions to the group
important_questions = [mc_question.id, tf_question.id, sa_question.id]
for question_id in important_questions:
    # Move existing questions to the group
    question = quiz.get_question(question_id)
    question.edit(quiz_group_id=specific_group.id)

Quiz Analytics and Reports

# Get quiz statistics
stats = quiz.get_statistics(all_versions=True)

print(f"Quiz Statistics:")
print(f"Submissions: {stats.submission_statistics['scores']['count']}")
print(f"Average Score: {stats.submission_statistics['scores']['mean']:.2f}")
print(f"High Score: {stats.submission_statistics['scores']['max']}")
print(f"Low Score: {stats.submission_statistics['scores']['min']}")

# Analyze question performance
for question_stat in stats.question_statistics:
    question_id = question_stat['id']
    correct_percent = question_stat['responses'] / question_stat['answered_student_count'] * 100 if question_stat['answered_student_count'] > 0 else 0
    
    print(f"Question {question_id}:")
    print(f"  Answered by: {question_stat['answered_student_count']} students")
    print(f"  Average time: {question_stat['time_spent']} seconds")
    print(f"  Difficulty: {question_stat['difficulty_index']:.2f}")

# Generate detailed reports
student_report = quiz.create_report(
    report_type='student_analysis',
    quiz_report={'includes_all_versions': True}
)

item_report = quiz.create_report(
    report_type='item_analysis',
    quiz_report={'includes_all_versions': True}
)

# Check report status and download when ready
import time
while student_report.workflow_state == 'generatiing':
    time.sleep(10)
    student_report = quiz.get_report(student_report.id)

if student_report.workflow_state == 'complete':
    report_file = student_report.get_file()
    print(f"Student analysis report: {report_file.url}")

Working with New Quizzes

# Create a New Quiz (Quizzes.Next)
new_quiz = course.create_new_quiz(
    title='Interactive Assessment',
    instructions='Complete all sections of this assessment.',
    quiz_type='graded',
    allowed_attempts=2,
    time_limit=60,
    due_at='2024-12-01T23:59:59Z',
    points_possible=50
)

print(f"Created New Quiz: {new_quiz.title}")
print(f"Edit URL: {new_quiz.html_url}")

# Note: New Quizzes have limited API functionality
# Most question creation and detailed management happens through the UI

Advanced Quiz Configuration

# Create a practice quiz with immediate feedback
practice_quiz = course.create_quiz({
    'title': 'Chapter 3 Practice Quiz',
    'description': 'Practice quiz with immediate feedback',
    'quiz_type': 'practice_quiz',
    'time_limit': 30,
    'allowed_attempts': -1,  # Unlimited attempts
    'scoring_policy': 'keep_highest',
    'show_correct_answers': True,
    'show_correct_answers_last_attempt': False,
    'one_question_at_a_time': False,
    'cant_go_back': False,
    'shuffle_answers': True,
    'hide_results': None,  # Show results immediately
    'published': True
})

# Create a secure exam with access restrictions
secure_exam = course.create_quiz({
    'title': 'Final Examination',
    'quiz_type': 'assignment',
    'time_limit': 120,
    'allowed_attempts': 1,
    'access_code': 'EXAM2024',
    'ip_filter': '192.168.1.0/24',  # Restrict to specific IP range
    'one_question_at_a_time': True,
    'cant_go_back': True,
    'show_correct_answers': False,
    'lock_at': '2024-12-15T17:00:00Z',
    'due_at': '2024-12-15T16:00:00Z',
    'published': False
})

# Create quiz with date overrides for different sections
override = secure_exam.create_override({
    'course_section_id': section_a.id,
    'title': 'Section A Extended Time',
    'due_at': '2024-12-15T17:30:00Z',  # 30 minutes extra
    'lock_at': '2024-12-15T17:30:00Z'
})

Install with Tessl CLI

npx tessl i tessl/pypi-canvasapi

docs

account-administration.md

assignments-grading.md

communication.md

content-management.md

course-management.md

index.md

main-client.md

quizzes-assessments.md

user-management.md

tile.json