From 9afc9a43922a37d7958333845f1523445ca0ffc1 Mon Sep 17 00:00:00 2001 From: Yared Yemane Date: Fri, 15 May 2026 02:16:26 -0700 Subject: [PATCH] date filter for dashborad analytics --- docs/PRACTICE_CREATION_API_GUIDE.md | 540 ++++++++++++++++++++++++++++ 1 file changed, 540 insertions(+) create mode 100644 docs/PRACTICE_CREATION_API_GUIDE.md diff --git a/docs/PRACTICE_CREATION_API_GUIDE.md b/docs/PRACTICE_CREATION_API_GUIDE.md new file mode 100644 index 0000000..17e97b2 --- /dev/null +++ b/docs/PRACTICE_CREATION_API_GUIDE.md @@ -0,0 +1,540 @@ +# Practice Creation API Guide (Lesson Scope) + +This guide provides the full step-by-step API process to create a lesson practice when using: + +- system-defined question types (`MCQ`, `TRUE_FALSE`, `SHORT_ANSWER`, `AUDIO`) +- dynamic question types (`DYNAMIC` with `question_type_definition_id` + `dynamic_payload`) + +All endpoints below are relative to `/api/v1` and require bearer authentication. + +--- + +## Standard Response Envelope + +Most successful responses follow: + +```json +{ + "message": "Human-readable message", + "data": {}, + "success": true, + "status_code": 200, + "metadata": null +} +``` + +Most errors follow: + +```json +{ + "message": "Error summary", + "error": "Detailed reason" +} +``` + +--- + +## Required Permissions + +At minimum, your role should have: + +- `questions.create` +- `question_sets.create` +- `question_set_items.add` +- `practices.create` + +If you create/update dynamic definitions: + +- `questions.update` +- `questions.delete` (if you also delete definitions) + +--- + +## End-to-End Flow + +1. (Optional) Upload media assets +2. Create question(s): + - system-defined path, or + - dynamic path (definition + question) +3. Create `PRACTICE` question set +4. Add question(s) to the set +5. Create lesson practice linked to that set +6. Verify under lesson + +--- + +## Step 0 (Optional): Upload Media + +Use this when question content references audio/image URLs. + +### Endpoint + +`POST /files/upload` (multipart form-data) + +### Form fields + +- `file`: binary +- `media_type`: `image` or `audio` or `video` + +### Example success response (shape) + +```json +{ + "message": "Media uploaded successfully", + "data": { + "url": "https://your-host/static/uploads/audio/abc.mp3", + "object_key": "audio/abc.mp3" + }, + "success": true, + "status_code": 201 +} +``` + +### Common errors + +- `400` invalid media type/content type +- `500` upload/storage failure + +Capture and reuse: + +- `data.url` (or equivalent resolved file URL) + +--- + +## Step 1A: Create System-Defined Question(s) + +### Endpoint + +`POST /questions` + +### Request example (MCQ) + +```json +{ + "question_text": "Choose the correct sentence.", + "question_type": "MCQ", + "difficulty_level": "EASY", + "points": 1, + "status": "PUBLISHED", + "options": [ + { "option_text": "He go to school.", "is_correct": false }, + { "option_text": "He goes to school.", "is_correct": true } + ] +} +``` + +### Request example (SHORT_ANSWER) + +```json +{ + "question_text": "Write one sentence using the word 'improve'.", + "question_type": "SHORT_ANSWER", + "difficulty_level": "MEDIUM", + "points": 2, + "status": "PUBLISHED", + "short_answers": [ + { "acceptable_answer": "I want to improve my English.", "match_type": "CASE_INSENSITIVE" } + ] +} +``` + +### Example success response (shape) + +```json +{ + "message": "Question created successfully", + "data": { + "id": 456, + "question_text": "Choose the correct sentence.", + "question_type": "MCQ", + "status": "PUBLISHED" + }, + "success": true, + "status_code": 201 +} +``` + +### Common errors + +- `400` validation/body errors +- `500` create failure + +Capture: + +- `data.id` as `question_id` + +--- + +## Step 1B: Dynamic Question Path + +If you use dynamic questions, follow these sub-steps. + +### 1B.1 Validate component-kind selection (optional but recommended) + +#### Endpoint + +`POST /questions/validate-question-type-definition` + +#### Request + +```json +{ + "stimulus_component_kinds": ["QUESTION_TEXT", "AUDIO_PROMPT", "IMAGE"], + "response_component_kinds": ["AUDIO_RESPONSE", "TEXT_INPUT"] +} +``` + +#### Success response + +```json +{ + "message": "Question type definition is valid", + "data": { "valid": true } +} +``` + +#### Error response example + +```json +{ + "message": "Invalid question type definition", + "error": "response: unknown component kind \"AUDIO_PROMPT\"" +} +``` + +--- + +### 1B.2 Create or reuse a dynamic type definition + +#### Endpoint + +`POST /questions/type-definitions` + +#### Request + +```json +{ + "key": "dialogue_audio_avatar_v1", + "display_name": "Dialogue Audio + Avatar", + "description": "Question text + prompt audio + two avatar images, with audio/text answer", + "stimulus_component_kinds": ["QUESTION_TEXT", "AUDIO_PROMPT", "IMAGE"], + "response_component_kinds": ["AUDIO_RESPONSE", "TEXT_INPUT"], + "stimulus_schema": [ + { "id": "question_text", "kind": "QUESTION_TEXT", "required": true }, + { "id": "prompt_audio", "kind": "AUDIO_PROMPT", "required": true }, + { "id": "speaker_a_avatar", "kind": "IMAGE", "required": true }, + { "id": "speaker_b_avatar", "kind": "IMAGE", "required": true } + ], + "response_schema": [ + { "id": "answer_audio", "kind": "AUDIO_RESPONSE", "required": true }, + { "id": "answer_text", "kind": "TEXT_INPUT", "required": true } + ], + "status": "ACTIVE" +} +``` + +#### Success response example + +```json +{ + "message": "Question type definition created", + "data": { + "id": 123, + "key": "dialogue_audio_avatar_v1", + "status": "ACTIVE" + } +} +``` + +#### Common errors + +- `400` invalid schema/kinds/mapping +- `500` unexpected persistence errors + +Capture: + +- `data.id` as `question_type_definition_id` + +--- + +### 1B.3 Create dynamic question + +#### Endpoint + +`POST /questions` + +#### Request + +```json +{ + "question_text": "Listen and respond as Speaker B.", + "question_type": "DYNAMIC", + "question_type_definition_id": 123, + "difficulty_level": "MEDIUM", + "points": 2, + "status": "PUBLISHED", + "dynamic_payload": { + "stimulus": [ + { "id": "question_text", "kind": "QUESTION_TEXT", "value": "Respond to the conversation." }, + { "id": "prompt_audio", "kind": "AUDIO_PROMPT", "value": "https://cdn.example.com/audio/prompt-1.mp3" }, + { "id": "speaker_a_avatar", "kind": "IMAGE", "value": "https://cdn.example.com/images/a.webp" }, + { "id": "speaker_b_avatar", "kind": "IMAGE", "value": "https://cdn.example.com/images/b.webp" } + ], + "response": [ + { "id": "answer_audio", "kind": "AUDIO_RESPONSE", "value": { "instructions": "Record your answer" } }, + { "id": "answer_text", "kind": "TEXT_INPUT", "value": { "placeholder": "Type your answer" } } + ] + } +} +``` + +#### Success response example + +```json +{ + "message": "Question created successfully", + "data": { + "id": 789, + "question_type": "DYNAMIC", + "question_type_definition_id": 123, + "status": "PUBLISHED" + }, + "success": true, + "status_code": 201 +} +``` + +#### Common errors + +- `400` missing/invalid `dynamic_payload` +- `400` missing `question_type_definition_id` +- `500` persistence failure + +Capture: + +- `data.id` as `question_id` + +--- + +## Step 2: Create PRACTICE Question Set + +### Endpoint + +`POST /question-sets` + +### Request + +```json +{ + "title": "Lesson 12 - Practice Set", + "description": "Question set for lesson-level practice", + "set_type": "PRACTICE", + "owner_type": "LESSON", + "owner_id": 12, + "status": "PUBLISHED", + "shuffle_questions": false +} +``` + +### Success response example + +```json +{ + "message": "Question set created successfully", + "data": { + "id": 55, + "title": "Lesson 12 - Practice Set", + "set_type": "PRACTICE", + "owner_type": "LESSON", + "owner_id": 12, + "status": "PUBLISHED" + }, + "success": true, + "status_code": 201 +} +``` + +### Common errors + +- `400` invalid input +- `500` create failure + +Capture: + +- `data.id` as `set_id` + +--- + +## Step 3: Add Question(s) to Set + +Run this once per `question_id`. + +### Endpoint + +`POST /question-sets/:setId/questions` + +### Request + +```json +{ + "question_id": 456, + "display_order": 1 +} +``` + +### Success response example + +```json +{ + "message": "Question added to set successfully", + "data": { + "id": 901, + "set_id": 55, + "question_id": 456, + "display_order": 1 + }, + "success": true, + "status_code": 201 +} +``` + +### Common errors + +- `400` invalid `setId` or body +- `500` link/create failure + +--- + +## Step 4: Create Lesson Practice + +This creates the practice record scoped to lesson. + +### Endpoint + +`POST /practices` + +### Request + +```json +{ + "parent_kind": "LESSON", + "parent_id": 12, + "title": "Lesson 12 Conversation Drill", + "story_description": "A short two-speaker scenario.", + "story_image": "https://cdn.example.com/images/story.webp", + "question_set_id": 55, + "quick_tips": "Listen carefully before answering." +} +``` + +### Success response example + +```json +{ + "message": "Practice created successfully", + "data": { + "id": 37, + "parent_kind": "LESSON", + "parent_id": 12, + "title": "Lesson 12 Conversation Drill", + "question_set_id": 55 + }, + "success": true, + "status_code": 201 +} +``` + +### Common errors + +- `400` validation failed / invalid parent kind +- `404` lesson not found +- `404` question set not found +- `500` create failure + +Capture: + +- `data.id` as `practice_id` + +--- + +## Step 5: Verify Practice Under Lesson + +### Endpoint + +`GET /lessons/:id/practices` + +Example: + +`GET /lessons/12/practices` + +### Success response example + +```json +{ + "message": "Practices retrieved successfully", + "data": { + "practices": [ + { + "id": 37, + "parent_kind": "LESSON", + "parent_id": 12, + "title": "Lesson 12 Conversation Drill", + "question_set_id": 55 + } + ], + "total_count": 1, + "limit": 20, + "offset": 0 + }, + "success": true, + "status_code": 200 +} +``` + +--- + +## Optional Learner Completion Step + +### Endpoint + +`POST /progress/practices/:id/complete` + +Use `practice_id` as `:id` for current behavior. + +### Success response example + +```json +{ + "message": "Practice completed", + "success": true, + "status_code": 200 +} +``` + +### Common errors + +- `403` sequence gating violation +- `404` practice not found +- `500` completion/persistence failure + +--- + +## Quick Checklist (IDs to Carry Forward) + +- From question create: `question_id` +- From dynamic definition create (if used): `question_type_definition_id` +- From question set create: `set_id` +- From practice create: `practice_id` + +--- + +## Notes and Pitfalls + +- For dynamic questions, `question_type` must be `DYNAMIC`. +- For dynamic questions, both `question_type_definition_id` and `dynamic_payload` are required. +- `AUDIO_PROMPT` is stimulus-side; response-side audio uses `AUDIO_RESPONSE`. +- `question_set_id` in `POST /practices` must reference an existing set. +- For lesson practice always use: + - `parent_kind = "LESSON"` + - `parent_id = ` +- Publish questions and question set (`status = "PUBLISHED"`) if learners must complete immediately.