
Open edX 讲师评分 API v2 规范Instructor Grading API 的 RESTful 化设计与实现【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform本文围绕 Open edX 平台中讲师控制台Instructor Dashboard迁移到微前端MFE架构时制定的评分 API 设计规范系统讲解/api/instructor/v2/评分类端点的资源导向 URL 设计、同步/异步双执行模型、统一响应格式与 OpenAPI 规范落地方式。读完本文你将掌握 Open edX 讲师评分 API v2 的完整端点语义、请求/响应结构、权限模型并能结合源码与测试用例理解其底层实现。设计背景讲师控制台的 MFE 迁移Open edX 的讲师控制台长期以来依赖一套遗留legacy端点提供评分相关操作重置答题次数reset attempts、重新评分rescore、分数覆盖override scores、删除学习状态delete state等。这些端点由 Studio/LMS 服务端模板渲染驱动URL 与响应格式不统一同步/异步行为隐式混在业务流程中任务监控与文档化能力薄弱。随着讲师控制台被迁移到Micro-FrontendMFE架构前端需要通过纯 RESTful API 与后端交互。为此Open edX 在 决策记录 0002Instructor Grading API SpecificationADR-0002中明确了新一代评分 API 的设计规范核心诉求包括一致的 URL 模式resource-oriented清晰的同步synchronous与异步asynchronous行为区分完整的后台任务监控能力规范的接口文档OpenAPI /api-docs/。该决策与同期制定的 课程信息 API 规范、ORA API 规范 和 选课 API 规范 共同构成讲师 API v2 系列的基础。核心设计决策一RESTful 资源导向设计规范要求采用资源导向的 URL统一形态为/api/instructor/v2/courses/{course_key}/{problem}/grading/{resource}并使用符合语义的 HTTP 方法HTTP 方法语义典型用途GET读取操作获取学习者信息、问题元数据、任务状态POST动作触发重置答题次数reset attempts、重新评分rescorePUT整体替换分数覆盖score overridesDELETE移除资源删除学习者状态delete learner state在 URL 路由配置 中v2 端点通过api/instructor/v2/前缀挂载命名空间为instructor_api_v2path( api/instructor/v2/, include((api_urls.v2_api_urls, lms.djangoapps.instructor), namespaceinstructor_api_v2), ),评分相关端点定义在 api_urls.py 中注意{problem}使用.贪婪匹配以容纳完整的 usage key含与字符re_path( rf^courses/{COURSE_ID_PATTERN}/(?Pproblem.)/grading/attempts/reset$, api_v2.ResetAttemptsView.as_view(), namereset_attempts ), re_path( rf^courses/{COURSE_ID_PATTERN}/(?Pproblem.)/grading/state$, api_v2.DeleteStateView.as_view(), namedelete_state ), re_path( rf^courses/{COURSE_ID_PATTERN}/(?Pproblem.)/grading/scores/rescore$, api_v2.RescoreView.as_view(), namerescore ), re_path( rf^courses/{COURSE_ID_PATTERN}/(?Pproblem.)/grading/scores$, api_v2.ScoreOverrideView.as_view(), namescore_override ),核心设计决策二同步与异步双执行模型规范对执行模型做出明确界定这是整套 API 可用性的关键单学习者操作携带learner参数同步执行返回200 OK与即时结果典型耗时 5s重置尝试约 100–500ms全课程学习者操作不携带learner参数投递后台任务返回202 Accepted与任务追踪信息任务状态查询GET /api/instructor/v2/courses/{course_key}/tasks/{task_id}。值得注意的实现差异重置尝试在单学习者场景走同步路径直接调用enrollment.reset_student_attempts而重新评分与分数覆盖在源码 RescoreView 与 ScoreOverrideView 中即使是单学习者也会投递后台任务如submit_rescore_problem_for_student因此返回体同样为异步结构。这与 ADR 中单学习者同步的原则存在差异可推断是评分任务本身较重需重算成绩且only_if_higher、AlreadyRunningError409等防护逻辑更依赖任务框架。异步响应的构造统一由 _build_async_response 完成它会用reverse(instructor_api_v2:task_status, ...)生成可直接轮询的status_url。任务状态端点与状态映射任务查询端点由 TaskStatusView 实现。它从InstructorTask模型读取任务记录并将 Celery 原生状态映射为规范定义的四种对外状态对外状态state映射的 Celery 状态pendingPENDING、QUEUING、SCHEDULED、RECEIVEDrunningSTARTED、PROGRESS、RETRYcompletedSUCCESSfailedFAILURE、REVOKED进度与结果信息解析自task.task_output的 JSON 内容current/total进度对、SUCCESS时的message任务失败时补充error.code TASK_FAILED与错误消息。核心设计决策三清晰的评分操作语义规范为四类评分操作定义了精确的语义边界操作端点语义说明约束Reset AttemptsPOST .../grading/attempts/reset将尝试计数器归零保留答案与状态可携带learner同步或不带异步Delete StateDELETE .../grading/state永久删除学习者的所有答题数据StudentModule 记录必须携带learner始终同步不可撤销RescorePOST .../grading/scores/rescore用当前评分逻辑重新评估提交并更新分数支持only_if_higher参数Override ScorePUT .../grading/scores手动为学习者设置指定分数替换自动计算分数必须携带learner写入 PersistentSubsectionGradeOverrideReset Attempts 的实现细节ResetAttemptsView 单学习者路径调用enrollment.reset_student_attempts( course_key, student, usage_key, requesting_userrequest.user, delete_moduleFalse, # 仅重置计数器保留状态 )若该学习者对问题不存在StudentModule记录则返回 404No state found for this learner and problem。全课程路径要求调用者具有instructor 角色staff 不够并调用task_api.submit_reset_problem_attempts_for_all_students若已有同类任务在运行则返回 409A reset task is already running for this problem。Delete State 的实现细节DeleteStateView 与 reset 的差别仅在于delete_moduleTrue即物理删除对应StudentModule记录。learner参数缺失时直接返回 400The learner parameter is required。测试用例 test_delete_state 验证了删除后StudentModule记录确实不存在。Rescore 与 only_if_higherRescoreView 通过 _get_only_if_higher 解析only_if_higher——该参数可来自查询参数或请求体兼容 MFE 表单编码 POST支持字符串true大小写不敏感与 JSON 布尔值且查询参数优先于请求体。对应的测试覆盖了 query、form-encoded body、JSON body、显式 false、query 覆盖 body 等多种组合见 test_api_v2.py。Override Score 的兼容设计ScoreOverrideView 要求 JSON 请求体携带score字段FloatFieldmin_value0。前端历史字段名new_score也得到兼容ScoreOverrideRequestSerializer.to_internal_value 会在字段校验前将new_score映射为score。覆盖前会额外校验调用者对问题 block 的staff访问权限并调用task_api.submit_override_score落库为覆盖记录。核心设计决策四一致的响应格式规范统一了三种响应模型定义于 serializers_v2.pySyncOperationResult同步操作结果{ success: true, learner: john_harvard, problem_location: block-v1:edXDemoXDemo_Coursetypeproblemblockhw1_p1, message: Operation completed successfully }score、previous_score为可选字段仅在覆盖操作中出现。AsyncOperationResult异步操作结果{ task_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890, status_url: /api/instructor/v2/courses/course-v1:edXDemoXDemo_Course/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890, scope: { learners: all, problem_location: block-v1:edXDemoXDemo_Coursetypeproblemblockhw1_p1 } }TaskStatus任务状态{ 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 }失败时result由error对象替代{code: TASK_FAILED, message: ...}。配套的读取类端点评分操作依赖以下读取端点同样定义于 v2 规范 YAMLLearnerGET /api/instructor/v2/courses/{course_key}/learners/{email_or_username}返回Learner对象username、email、full_name、progress_url 等由 LearnerView 实现要求VIEW_DASHBOARD权限ProblemGET /api/instructor/v2/courses/{course_key}/problems/{location}返回Problem对象usage key、display name、课程层级 breadcrumbs携带learner查询参数时附带该学习者当前得分current_score与尝试数据attemptstotal为 null 表示不限次数。注意该端点要求精确的 location不做搜索或模糊匹配GradingConfigGET /api/instructor/v2/courses/{course_key}/grading-config返回课程的评分策略各作业类型的min_count、drop_count、weight0.0–1.0及字母等级分段grade_cutoffs如A: 0.9。认证与权限模型API 使用 JWT 认证OpenAPI 中定义为apiKeyheaderAuthorization格式由JWT_AUTH[JWT_AUTH_HEADER_PREFIX]决定默认JWT token。所有视图类统一使用permission_classes (IsAuthenticated, permissions.InstructorPermission)并按操作细分权限名端点权限常量说明Learner / Problem / GradingConfigpermissions.VIEW_DASHBOARD查看讲师仪表盘Reset Attempts / Delete Statepermissions.GIVE_STUDENT_EXTENSION学生扩展/干预Rescore / Override Scorepermissions.OVERRIDE_GRADES覆盖成绩Task Statuspermissions.SHOW_TASKS查看后台任务批量全课程操作额外要求调用者具备instructor角色源码中先以 staff 权限加载课程再通过has_access(request.user, instructor, course)校验不满足则返回 403Instructor access required for bulk operations。参数解析与错误处理三个关键辅助函数承载了参数解析逻辑_parse_course_and_problem用CourseKey.from_string与UsageKey.from_string(...).map_into_course(course_key)解析并校验路径参数非法时返回 400_resolve_learner按用户名或邮箱解析用户用户不存在返回 404标识符命中多个用户返回 400_get_learner_identifier从查询参数或请求体读取learner。错误响应统一为结构化对象{ error: RESOURCE_NOT_FOUND, message: The specified resource does not exist, status_code: 404, field_errors: { score: This field is required. } }主要错误码与场景INVALID_PARAMETER400参数格式错误、AUTHENTICATION_REQUIRED401、PERMISSION_DENIED403、RESOURCE_NOT_FOUND404学习者/任务/问题不存在、409同类后台任务已在运行、500提交层SubmissionError。OpenAPI 规范与文档治理规范要求在 instructor-v2-grading-api-spec.yaml 维护静态 OpenAPI 2.0 规范作为开发期参考覆盖学习者、问题、课程配置、评分、任务五类标签tags并完整定义参数CourseKey的正则^course-v1:[^/](\[^/])(\[^/])$、ProblemLocationPath、LearnerIdentifierQuery等、响应BadRequest/Unauthorized/Forbidden/NotFound与数据结构SyncOperationResult、AsyncOperationResult、TaskStatus、Learner、GradingConfig、Problem、Error。文档治理遵循单一事实源原则部署后以/api-docs/自动生成的在线文档为准源码视图中的apidocs.schema(...)装饰器即为其生成输入实现完成、端点上线后静态 YAML 将被删除以避免维护两份逐渐失真的文档。设计影响与迁移代价正面影响统一 URL 与响应格式使 API 可预测前端可据此编写类型安全客户端显式的同步/异步契约让 UI 能给出正确的交互反馈如立即刷新 vs 展示进度条OpenAPI 规范支撑自动化校验、测试与客户端代码生成资源导向设计便于后续低成本扩展新操作。负面影响迁移成本依赖遗留端点的既有客户端需要同步升级过渡期内新旧端点并存存在双维护负担熟悉遗留模式的开发者需要学习新的 URL 与响应约定。从源码结构看v2 端点当前仍集中在api_urls.v2_api_urls路由文件中的注释明确计划将其迁往独立的lms.djangoapps.instructor.api.v2.urls模块以便独立维护这印证了该 API 家族仍在演进中。总结ADR-0002 为 Open edX 讲师评分功能定义了清晰的 RESTful API 契约资源导向 URL、同步/异步双执行模型、四种评分操作的精确语义、统一响应结构与 OpenAPI 文档治理策略。结合 api_v2.py 的实现、api_urls.py 的路由、serializers_v2.py 的序列化层以及 test_api_v2.py 的测试覆盖开发者可以快速接入这套 API或以此为模板为讲师控制台的其他功能设计同风格的 v2 接口。【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考