docs: add instructor api v2 ADR/OpenAPI spec for course info and grading (#37743)

This commit is contained in:
wgu-jesse-stewart
2025-12-17 15:19:55 -05:00
committed by GitHub
parent ca78895edf
commit 2c53232c5d
4 changed files with 1281 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
Instructor Course Information API Specification
-----------------------------------------------
Status
======
**Accepted** *2025-10-30*
Context
=======
The instructor dashboard requires comprehensive course metadata, enrollment statistics, user
permissions, and navigation configuration. This information was previously scattered across
multiple endpoints, requiring multiple round-trip requests and complex client-side data
aggregation for MFEs.
Decisions
=========
#. **Consolidated Course Metadata Endpoint**
Create ``GET /api/instructor/v2/courses/{course_id}`` that returns comprehensive course
information in a single request, including course identity, timing, enrollment statistics,
user permissions, dashboard tab configuration, and operational information.
#. **Permission-Based Tab Configuration**
Server-side logic determines which dashboard tabs the current user can access based on
their roles, course features, and system configuration. Tabs are returned with URLs
pointing to the appropriate MFE routes.
#. **Serializer-Based Business Logic**
Use Django REST Framework serializers (``CourseInformationSerializer``) to encapsulate
all business logic for data gathering, permission checks, enrollment queries, and
formatting. Keep views thin.
#. **OpenAPI Specification**
Maintain an OpenAPI specification at ``../references/instructor-v2-course-info-spec.yaml`` to guide implementation. This static specification serves as a reference during development, but ``/api-docs/`` is the source of truth for what is actually deployed. Once implementation is complete and the endpoints are live in ``/api-docs/``, the static spec file will be deleted to avoid maintaining outdated documentation.
Consequences
============
Positive
~~~~~~~~
* Single request replaces multiple round-trips, reducing latency for MFE page loads
* Centralized business logic ensures consistent permission checks and data formatting
* Simplified client code with all course information available in one call
* OpenAPI specification enables type-safe client generation
Negative
~~~~~~~~
* Larger response payload (though offset by eliminating multiple requests)
* Some over-fetching when clients don't need all information
* Permission-based data prevents simple course-level caching
* Enrollment queries and permission checks run on every request
References
==========
* OpenAPI Specification: ``../references/instructor-v2-course-info-spec.yaml``
* Implementation: ``lms/djangoapps/instructor/views/api_v2.py``
* Live API Documentation: ``/api-docs/``

View File

@@ -0,0 +1,77 @@
Instructor Grading API Specification
-------------------------------------
Status
======
**Draft** (=> **Provisional**)
Context
=======
The instructor dashboard is being migrated to a Micro-Frontend (MFE) architecture, which requires RESTful API endpoints. The current implementation provides grading operations (reset attempts, rescore, override scores, delete state) through legacy endpoints.
The MFE migration requires a modern, RESTful API with consistent URL patterns, clear synchronous vs asynchronous behavior, comprehensive task monitoring, and proper documentation. These operations need to support both single-learner (synchronous) and all-learners (asynchronous) execution models.
Decisions
=========
#. **RESTful Resource-Oriented Design**
Use resource-oriented URLs: ``/api/instructor/v2/courses/{course_key}/{problem}/grading/{resource}``
Use appropriate HTTP methods:
* ``GET`` for read operations (learner info, problem metadata, task status)
* ``POST`` for actions (reset attempts, rescore)
* ``PUT`` for replacements (score overrides)
* ``DELETE`` for removals (delete learner state)
#. **Synchronous vs Asynchronous Execution**
* Operations targeting a single learner (with ``learner`` parameter) execute synchronously
and return ``200 OK`` with immediate results (< 5s typical)
* Operations targeting all learners (no ``learner`` parameter) queue a background task
and return ``202 Accepted`` with task tracking information
* Provide task status endpoint: ``GET /api/instructor/v2/courses/{course_key}/tasks/{task_id}``
#. **Clear Operation Semantics**
* **Reset Attempts**: Resets counter to zero, preserves answers/state
* **Delete State**: Permanently removes all learner data (requires ``learner`` parameter)
* **Rescore**: Re-evaluates submissions with current grading logic (supports ``only_if_higher``)
* **Override Score**: Manually sets specific score (requires ``learner`` parameter)
#. **Consistent Response Formats**
* Synchronous operations return ``SyncOperationResult`` with success, learner, problem_location, message
* Asynchronous operations return ``AsyncOperationResult`` with task_id, status_url, scope
* Task status responses include task_id, state, progress, result/error, timestamps
#. **OpenAPI Specification**
Maintain an OpenAPI specification at ``../references/instructor-v2-grading-api-spec.yaml`` to guide implementation. This static specification serves as a reference during development, but ``/api-docs/`` is the source of truth for what is actually deployed. Once implementation is complete and the endpoints are live in ``/api-docs/``, the static spec file will be deleted to avoid maintaining outdated documentation.
Consequences
============
Positive
~~~~~~~~
* Consistent URL patterns and response formats make the API predictable
* Explicit sync/async behavior allows proper UI feedback
* OpenAPI specification enables automated validation, testing, and type-safe client generation
* Resource-oriented design makes it easy to add new operations
Negative
~~~~~~~~
* Existing clients using legacy endpoints need to be updated
* Dual maintenance during transition period
* Developers familiar with legacy endpoints need to learn new patterns
References
==========
* OpenAPI Specification: ``../references/instructor-v2-grading-api-spec.yaml``
* Live API Documentation: ``/api-docs/``

View File

@@ -0,0 +1,484 @@
swagger: '2.0'
info:
title: Instructor Dashboard API v2
version: 2.0.0
description: |
REST API for instructor dashboard operations.
**Design Principles:**
- RESTful resource-oriented URLs
- Query parameters for filtering operations
- Clear separation between read and write operations
- Consistent error handling
host: courses.example.com
basePath: /
schemes:
- https
securityDefinitions:
JWTAuth:
type: apiKey
in: header
name: Authorization
description: JWT token authentication. Header format depends on JWT_AUTH['JWT_AUTH_HEADER_PREFIX'] setting (default is 'JWT <token>').
security:
- JWTAuth: []
tags:
- name: Course
description: Course metadata and configuration
- name: Tasks
description: Background task monitoring
paths:
/api/instructor/v2/courses/{course_id}:
get:
tags:
- Course
summary: Get course metadata
description: |
Retrieve comprehensive course metadata including enrollment counts, dashboard configuration,
permissions, and navigation sections.
operationId: getCourseMetadata
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseId'
responses:
200:
description: Course metadata retrieved successfully
schema:
$ref: '#/definitions/CourseInformation'
400:
$ref: '#/responses/BadRequest'
401:
$ref: '#/responses/Unauthorized'
403:
$ref: '#/responses/Forbidden'
404:
$ref: '#/responses/NotFound'
/api/instructor/v2/courses/{course_id}/instructor_tasks:
get:
tags:
- Tasks
summary: List instructor tasks
description: |
List instructor tasks for a course with optional filtering by problem location and student.
**Task States:**
- `PENDING`: Task is queued but not yet started
- `PROGRESS`: Task is currently executing
- `SUCCESS`: Task finished successfully
- `FAILURE`: Task encountered an error
- `REVOKED`: Task was cancelled
operationId: listInstructorTasks
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseId'
- name: problem_location_str
in: query
description: Filter tasks to a specific problem location (usage key)
required: false
type: string
x-example: "block-v1:edX+DemoX+Demo_Course+type@problem+block@sample_problem"
- name: unique_student_identifier
in: query
description: Filter tasks to specific student (requires problem_location_str). Can be username or email.
required: false
type: string
x-example: "john_harvard"
responses:
200:
description: Task list retrieved successfully
schema:
$ref: '#/definitions/InstructorTaskList'
examples:
application/json:
tasks:
- task_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
task_type: "rescore_problem"
task_state: "PROGRESS"
status: "In progress"
created: "2024-01-15T10:30:00Z"
task_input: '{"problem_url": "block-v1:edX+DemoX+Demo_Course+type@problem+block@hw1"}'
task_output: null
duration_sec: "45"
task_message: "Processing 75 of 150 students"
requester: "staff"
- task_id: "b2c3d4e5-f6a7-8901-bcde-f1234567890a"
task_type: "reset_problem_attempts"
task_state: "SUCCESS"
status: "Complete"
created: "2024-01-15T10:00:00Z"
task_input: '{"problem_url": "block-v1:edX+DemoX+Demo_Course+type@problem+block@hw2"}'
task_output: '{"total": 150, "succeeded": 150, "failed": 0}'
duration_sec: "120"
task_message: "Reset attempts for 150 students"
requester: "instructor"
400:
$ref: '#/responses/BadRequest'
401:
$ref: '#/responses/Unauthorized'
403:
$ref: '#/responses/Forbidden'
404:
$ref: '#/responses/NotFound'
parameters:
CourseId:
name: course_id
in: path
required: true
description: Course identifier in format `course-v1:{org}+{course}+{run}`
type: string
pattern: '^course-v1:[^/+]+(\\+[^/+]+)+(\\+[^/]+)$'
x-example: "course-v1:edX+DemoX+Demo_Course"
responses:
BadRequest:
description: Bad request - Invalid parameters or malformed request
schema:
$ref: '#/definitions/Error'
examples:
application/json:
error: "INVALID_PARAMETER"
message: "Invalid course key format"
Unauthorized:
description: Unauthorized - Authentication required
schema:
$ref: '#/definitions/Error'
examples:
application/json:
error: "AUTHENTICATION_REQUIRED"
message: "You must be authenticated to access this endpoint"
Forbidden:
description: Forbidden - Insufficient permissions
schema:
$ref: '#/definitions/Error'
examples:
application/json:
error: "PERMISSION_DENIED"
message: "You do not have instructor permissions for this course"
NotFound:
description: Not found - Resource does not exist
schema:
$ref: '#/definitions/Error'
examples:
application/json:
error: "RESOURCE_NOT_FOUND"
message: "The specified resource does not exist"
definitions:
CourseInformation:
type: object
description: Comprehensive course information including metadata, enrollment statistics, permissions, and dashboard configuration
required:
- course_id
- display_name
- org
- course_number
- pacing
- has_started
- has_ended
- total_enrollment
- enrollment_counts
- num_sections
- permissions
- tabs
- disable_buttons
properties:
course_id:
type: string
description: Course run key
example: "course-v1:edX+DemoX+Demo_Course"
display_name:
type: string
description: Course display name
example: "Demonstration Course"
org:
type: string
description: Organization identifier
example: "edX"
course_number:
type: string
description: Course number
example: "DemoX"
enrollment_start:
type: string
format: date-time
description: Enrollment start date (ISO 8601 with timezone)
example: "2013-02-05T00:00:00Z"
x-nullable: true
enrollment_end:
type: string
format: date-time
description: Enrollment end date (ISO 8601 with timezone)
example: "2024-12-31T23:59:59Z"
x-nullable: true
start:
type: string
format: date-time
description: Course start date (ISO 8601 with timezone)
example: "2013-02-05T05:00:00Z"
x-nullable: true
end:
type: string
format: date-time
description: Course end date (ISO 8601 with timezone)
example: "2024-12-31T23:59:59Z"
x-nullable: true
pacing:
type: string
enum:
- self
- instructor
description: Course pacing type
example: "instructor"
has_started:
type: boolean
description: Whether the course has started based on current time
example: true
has_ended:
type: boolean
description: Whether the course has ended based on current time
example: false
total_enrollment:
type: integer
minimum: 0
description: Total number of enrollments across all modes
example: 150
enrollment_counts:
type: object
description: Enrollment count breakdown by mode
properties:
total:
type: integer
minimum: 0
audit:
type: integer
minimum: 0
verified:
type: integer
minimum: 0
honor:
type: integer
minimum: 0
example:
total: 150
audit: 100
verified: 40
honor: 10
num_sections:
type: integer
minimum: 0
description: Number of sections/chapters in the course
example: 12
grade_cutoffs:
type: string
description: Formatted string of grade cutoffs
example: "A is 0.9, B is 0.8, C is 0.7, D is 0.6"
course_errors:
type: array
description: List of course validation errors from modulestore
items:
type: array
items:
type: string
example: []
studio_url:
type: string
format: uri
description: URL to view/edit course in Studio
example: "https://studio.example.com/course/course-v1:edX+DemoX+Demo_Course"
permissions:
type: object
description: User permissions for instructor dashboard features
required:
- admin
- instructor
- finance_admin
- sales_admin
- staff
- forum_admin
- data_researcher
properties:
admin:
type: boolean
description: User is a platform administrator
instructor:
type: boolean
description: User has instructor role
finance_admin:
type: boolean
description: User has finance admin role
sales_admin:
type: boolean
description: User has sales admin role
staff:
type: boolean
description: User has staff role
forum_admin:
type: boolean
description: User has forum administrator role
data_researcher:
type: boolean
description: User has data researcher permissions
example:
admin: false
instructor: true
finance_admin: false
sales_admin: false
staff: true
forum_admin: true
data_researcher: false
tabs:
type: array
description: List of course tabs with configuration and display information
items:
type: object
required:
- tab_id
- title
- url
properties:
tab_id:
type: string
description: Unique tab identifier
title:
type: string
description: Display title for the tab
url:
type: string
format: uri
description: URL to access the tab
example:
- tab_id: "course_info"
title: "Course Info"
url: "https://mfe.example.com/instructor/course-v1:edX+DemoX+Demo_Course/course_info"
- tab_id: "enrollments"
title: "Enrollments"
url: "https://mfe.example.com/instructor/course-v1:edX+DemoX+Demo_Course/enrollments"
- tab_id: "grading"
title: "Grading"
url: "https://mfe.example.com/instructor/course-v1:edX+DemoX+Demo_Course/grading"
disable_buttons:
type: boolean
description: Whether to disable certain bulk action buttons due to large course size
example: false
analytics_dashboard_message:
type: string
description: Message about analytics dashboard availability
example: "To gain insights into student enrollment and participation, visit the analytics dashboard."
InstructorTaskList:
type: object
description: List of instructor tasks
required:
- tasks
properties:
tasks:
type: array
items:
$ref: '#/definitions/InstructorTask'
InstructorTask:
type: object
description: Instructor task details
required:
- task_id
- task_type
- task_state
- status
- created
- duration_sec
- task_message
- requester
- task_input
properties:
task_id:
type: string
description: Unique task identifier
example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
task_type:
type: string
description: Type of instructor task
enum:
- rescore_problem
- reset_problem_attempts
- delete_problem_state
- override_problem_score
- grade_course
- calculate_grades_csv
- problem_grade_report
- export_ora2_data
- cohort_students
- send_bulk_email
example: "rescore_problem"
task_state:
type: string
enum:
- PENDING
- PROGRESS
- SUCCESS
- FAILURE
- REVOKED
description: Current state of the task
example: "PROGRESS"
status:
type: string
description: Human-readable status message
example: "In progress"
created:
type: string
format: date-time
description: Task creation timestamp (ISO 8601 with timezone)
example: "2024-01-15T10:30:00Z"
duration_sec:
type: string
description: Task duration in seconds, or "unknown" if not available
example: "45"
task_message:
type: string
description: Detailed task message or progress update
example: "Processing 75 of 150 students"
requester:
type: string
description: Username of the user who initiated the task
example: "instructor"
task_input:
type: string
description: JSON string of task input parameters
example: '{"problem_url": "block-v1:edX+DemoX+Demo_Course+type@problem+block@hw1"}'
task_output:
type: string
description: JSON string of task output/results (null if not complete)
example: '{"total": 150, "succeeded": 150, "failed": 0}'
x-nullable: true
Error:
type: object
description: Error response
required:
- error
- message
properties:
error:
type: string
description: Machine-readable error code
example: "RESOURCE_NOT_FOUND"
message:
type: string
description: Human-readable error message
example: "The specified course does not exist"
field_errors:
type: object
description: Field-specific validation errors (if applicable)
additionalProperties:
type: string

View File

@@ -0,0 +1,654 @@
swagger: '2.0'
info:
title: Instructor Dashboard API
version: 2.0.0
description: |
Modern REST API for instructor dashboard operations.
**Design Principles:**
- RESTful resource-oriented URLs
- Query parameters for filtering operations
- Clear separation between read and write operations
- Consistent error handling
**Execution Model:**
- Operations that affect a single learner execute synchronously (< 5s typical)
- Operations that affect all learners queue a background task
- Use the task status endpoint to monitor background tasks
host: courses.example.com
basePath: /
schemes:
- https
securityDefinitions:
JWTAuth:
type: apiKey
in: header
name: Authorization
description: JWT token authentication. Header format depends on JWT_AUTH['JWT_AUTH_HEADER_PREFIX'] setting (default is 'JWT <token>').
security:
- JWTAuth: []
tags:
- name: Learners
description: Learner information and enrollment data
- name: Problems
description: Problem metadata and structure
- name: Grading
description: Grading operations and score management
- name: Tasks
description: Background task monitoring
paths:
# ==================== LEARNER ENDPOINTS ====================
/api/instructor/v2/courses/{course_key}/learners/{email_or_username}:
get:
tags:
- Learners
summary: Get learner information
description: |
Retrieve comprehensive learner information including profile, enrollment status,
progress URLs, and current grading data.
operationId: getLearner
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseKey'
- $ref: '#/parameters/LearnerIdentifierPath'
responses:
200:
description: Learner information retrieved successfully
schema:
$ref: '#/definitions/Learner'
400:
$ref: '#/responses/BadRequest'
401:
$ref: '#/responses/Unauthorized'
403:
$ref: '#/responses/Forbidden'
404:
$ref: '#/responses/NotFound'
# ==================== PROBLEM ENDPOINTS ====================
/api/instructor/v2/courses/{course_key}/problems/{location}:
get:
tags:
- Problems
summary: Get problem information
description: |
Retrieve problem metadata including display name, location in course hierarchy,
and usage key.
**Note:** Requires exact problem location - no search or partial matching.
operationId: getProblem
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseKey'
- name: location
in: path
description: Problem block usage key
required: true
type: string
x-example: "block-v1:edX+DemoX+Demo_Course+type@problem+block@sample_problem"
responses:
200:
description: Problem information retrieved successfully
schema:
$ref: '#/definitions/Problem'
400:
$ref: '#/responses/BadRequest'
401:
$ref: '#/responses/Unauthorized'
403:
$ref: '#/responses/Forbidden'
404:
$ref: '#/responses/NotFound'
# ==================== GRADING ENDPOINTS ====================
/api/instructor/v2/courses/{course_key}/{problem}/grading/attempts/reset:
post:
tags:
- Grading
summary: Reset problem attempts
description: |
Reset attempt counters to zero, allowing learner(s) to reattempt the problem.
The learner's previous answers and state are preserved, only the attempt counter is reset.
**Scope:**
- With `learner`: Single learner, single problem (synchronous, ~100-500ms)
- Without `learner`: All learners, single problem (async task)
**Note:** To completely delete state instead of just resetting attempts,
use `DELETE /grading/state` instead.
operationId: resetAttempts
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseKey'
- $ref: '#/parameters/ProblemLocationPath'
- $ref: '#/parameters/LearnerIdentifierQuery'
responses:
200:
description: Operation completed successfully (synchronous)
schema:
$ref: '#/definitions/SyncOperationResult'
202:
description: Task queued for background processing (asynchronous)
schema:
$ref: '#/definitions/AsyncOperationResult'
400:
$ref: '#/responses/BadRequest'
401:
$ref: '#/responses/Unauthorized'
403:
$ref: '#/responses/Forbidden'
404:
$ref: '#/responses/NotFound'
/api/instructor/v2/courses/{course_key}/{problem}/grading/state:
delete:
tags:
- Grading
summary: Delete learner problem state
description: |
Permanently delete a specific learner's StudentModule record for a specific problem,
including all answers, submissions, attempts, and scores from the database.
**Warning:** This operation is destructive and cannot be undone. Unlike resetting
attempts (which preserves state), this completely removes all records.
**Requirements:**
- `learner` parameter is required
- Always executes synchronously
operationId: deleteState
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseKey'
- $ref: '#/parameters/ProblemLocationPath'
- name: learner
in: query
description: Learner username or email (required)
required: true
type: string
x-example: "john_harvard"
responses:
200:
description: State deleted successfully
schema:
$ref: '#/definitions/SyncOperationResult'
400:
$ref: '#/responses/BadRequest'
401:
$ref: '#/responses/Unauthorized'
403:
$ref: '#/responses/Forbidden'
404:
$ref: '#/responses/NotFound'
/api/instructor/v2/courses/{course_key}/{problem}/grading/scores/rescore:
post:
tags:
- Grading
summary: Rescore problem submissions
description: |
Re-evaluate learner submissions and update scores based on current grading logic.
**Scope:**
- With `learner`: Single learner, single problem (synchronous)
- Without `learner`: All learners, single problem (async task)
operationId: rescore
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseKey'
- $ref: '#/parameters/ProblemLocationPath'
- $ref: '#/parameters/LearnerIdentifierQuery'
- name: only_if_higher
in: query
description: Only update score if the new score is higher than current score
required: false
type: boolean
default: false
responses:
200:
description: Rescore completed successfully (synchronous)
schema:
$ref: '#/definitions/SyncOperationResult'
202:
description: Rescore task queued for background processing (asynchronous)
schema:
$ref: '#/definitions/AsyncOperationResult'
400:
$ref: '#/responses/BadRequest'
401:
$ref: '#/responses/Unauthorized'
403:
$ref: '#/responses/Forbidden'
404:
$ref: '#/responses/NotFound'
/api/instructor/v2/courses/{course_key}/{problem}/grading/scores:
put:
tags:
- Grading
summary: Override a learner's score
description: |
Manually set a specific score for a learner on a problem, replacing any
automatically calculated score.
**Requirements:**
- `learner` parameter is required
- Always executes synchronously
**Note:** This creates or updates a PersistentSubsectionGradeOverride record.
operationId: overrideScore
consumes:
- application/json
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseKey'
- $ref: '#/parameters/ProblemLocationPath'
- name: learner
in: query
description: Learner username or email (required)
required: true
type: string
x-example: "john_harvard"
- name: body
in: body
required: true
schema:
type: object
required:
- score
properties:
score:
type: number
description: New score value (out of problem's total possible points)
minimum: 0
example: 8.5
responses:
200:
description: Score overridden successfully
schema:
$ref: '#/definitions/SyncOperationResult'
examples:
application/json:
success: true
learner: "john_harvard"
problem_location: "block-v1:edX+DemoX+Demo_Course+type@problem+block@sample_problem"
score: 8.5
previous_score: 5.0
message: "Score overridden successfully"
400:
$ref: '#/responses/BadRequest'
401:
$ref: '#/responses/Unauthorized'
403:
$ref: '#/responses/Forbidden'
404:
$ref: '#/responses/NotFound'
# ==================== TASK ENDPOINTS ====================
/api/instructor/v2/courses/{course_key}/tasks/{task_id}:
get:
tags:
- Tasks
summary: Get task status
description: |
Check the status of a background task.
**Task States:**
- `pending`: Task is queued but not yet started
- `running`: Task is currently executing
- `completed`: Task finished successfully
- `failed`: Task encountered an error
operationId: getTaskStatus
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseKey'
- name: task_id
in: path
description: Task identifier returned from async operation
required: true
type: string
responses:
200:
description: Task status retrieved successfully
schema:
$ref: '#/definitions/TaskStatus'
examples:
application/json:
task_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
state: "completed"
progress:
current: 150
total: 150
result:
success: true
message: "Reset attempts for 150 learners"
created_at: "2024-01-15T10:30:00Z"
updated_at: "2024-01-15T10:35:23Z"
400:
$ref: '#/responses/BadRequest'
401:
$ref: '#/responses/Unauthorized'
403:
$ref: '#/responses/Forbidden'
404:
$ref: '#/responses/NotFound'
# ==================== COMPONENTS ====================
parameters:
CourseKey:
name: course_key
in: path
required: true
description: Course identifier in format `course-v1:{org}+{course}+{run}`
type: string
pattern: '^course-v1:[^/+]+(\\+[^/+]+)+(\\+[^/]+)$'
x-example: "course-v1:edX+DemoX+Demo_Course"
LearnerIdentifierPath:
name: email_or_username
in: path
required: true
description: Learner's username or email address
type: string
minLength: 1
LearnerIdentifierQuery:
name: learner
in: query
required: false
description: |
Learner's username or email address.
If omitted, operation applies to all learners in the course.
type: string
x-example: "john_harvard"
ProblemLocationPath:
name: problem
in: path
required: true
description: Problem block usage key
type: string
x-example: "block-v1:edX+DemoX+Demo_Course+type@problem+block@sample_problem"
responses:
BadRequest:
description: Bad request - Invalid parameters or malformed request
schema:
$ref: '#/definitions/Error'
examples:
application/json:
error: "INVALID_PARAMETER"
message: "Invalid course key format"
status_code: 400
Unauthorized:
description: Unauthorized - Authentication required
schema:
$ref: '#/definitions/Error'
examples:
application/json:
error: "AUTHENTICATION_REQUIRED"
message: "You must be authenticated to access this endpoint"
status_code: 401
Forbidden:
description: Forbidden - Insufficient permissions
schema:
$ref: '#/definitions/Error'
examples:
application/json:
error: "PERMISSION_DENIED"
message: "You do not have instructor permissions for this course"
status_code: 403
NotFound:
description: Not found - Resource does not exist
schema:
$ref: '#/definitions/Error'
examples:
application/json:
error: "RESOURCE_NOT_FOUND"
message: "The specified resource does not exist"
status_code: 404
definitions:
SyncOperationResult:
type: object
description: Result from a synchronous grading operation
required:
- success
properties:
success:
type: boolean
description: Whether the operation succeeded
learner:
type: string
description: Learner identifier (if applicable)
problem_location:
type: string
description: Problem location (if applicable)
score:
type: number
description: Updated score (for override operations)
message:
type: string
description: Human-readable result message
example:
success: true
learner: "john_harvard"
problem_location: "block-v1:edX+DemoX+Demo_Course+type@problem+block@hw1_p1"
message: "Operation completed successfully"
AsyncOperationResult:
type: object
description: Task information for an asynchronous operation
required:
- task_id
- status_url
properties:
task_id:
type: string
description: Unique task identifier
status_url:
type: string
format: uri
description: URL to poll for task status
scope:
type: object
description: Scope of the operation
properties:
learners:
type: string
description: Either "all" or specific learner identifier
problems:
type: string
description: Either "all" or specific problem location
example:
task_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
status_url: "/api/instructor/v2/courses/course-v1:edX+DemoX+Demo_Course/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
scope:
learners: "all"
problem_location: "block-v1:edX+DemoX+Demo_Course+type@problem+block@hw1_p1"
TaskStatus:
type: object
description: Status of a background task
required:
- task_id
- state
- created_at
- updated_at
properties:
task_id:
type: string
description: Task identifier
state:
type: string
enum:
- pending
- running
- completed
- failed
description: Current state of the task
progress:
type: object
description: Progress information (if available)
properties:
current:
type: integer
minimum: 0
total:
type: integer
minimum: 0
required:
- current
- total
result:
type: object
description: Task result (present when state is "completed")
properties:
success:
type: boolean
message:
type: string
error:
type: object
description: Error information (present when state is "failed")
properties:
code:
type: string
message:
type: string
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
Learner:
type: object
description: Comprehensive learner information
required:
- username
- email
- first_name
- last_name
properties:
username:
type: string
example: "john_harvard"
email:
type: string
format: email
example: "john@example.com"
first_name:
type: string
example: "John"
last_name:
type: string
example: "Harvard"
progress_url:
type: string
format: uri
description: URL to learner's progress page
x-nullable: true
gradebook_url:
type: string
format: uri
description: URL to learner's gradebook view
x-nullable: true
current_score:
type: object
x-nullable: true
properties:
score:
type: number
format: float
minimum: 0
total:
type: number
format: float
minimum: 0
attempts:
type: object
x-nullable: true
properties:
current:
type: integer
minimum: 0
total:
type: integer
minimum: 0
Problem:
type: object
description: Problem metadata and location
required:
- id
- name
- breadcrumbs
properties:
id:
type: string
description: Problem usage key
example: "block-v1:edX+DemoX+Demo_Course+type@problem+block@sample_problem"
name:
type: string
description: Problem display name
example: "Sample Problem"
breadcrumbs:
type: array
description: Course hierarchy breadcrumbs
items:
type: object
required:
- display_name
properties:
display_name:
type: string
usage_key:
type: string
description: Block usage key (omitted for course level)
Error:
type: object
description: Error response
required:
- error
- message
- status_code
properties:
error:
type: string
description: Machine-readable error code
example: "RESOURCE_NOT_FOUND"
message:
type: string
description: Human-readable error message
example: "The specified course does not exist"
status_code:
type: integer
description: HTTP status code
example: 404
field_errors:
type: object
description: Field-specific validation errors (if applicable)
additionalProperties:
type: string