Yimaru-BackEnd/docs/audio-practice-integration.md

304 lines
10 KiB
Markdown

# Audio Practice (Speaking Practice) — Admin Panel Integration Guide
## Overview
The Yimaru backend **fully supports** audio/speaking practices. This guide covers the steps needed to integrate the feature into the **admin panel frontend**.
---
## Backend Status (✅ Already Complete)
| Layer | File | What It Does |
|-------|------|--------------|
| Domain | `internal/domain/questions.go` | `QuestionTypeAudio = "AUDIO"` constant, `VoicePrompt`, `SampleAnswerVoicePrompt`, `AudioCorrectAnswerText` fields |
| Handler | `internal/web_server/handlers/questions.go` | `CreateQuestion` accepts `question_type: "AUDIO"` with audio-specific fields |
| Handler | `internal/web_server/handlers/file_handler.go` | `UploadAudio` (general audio upload) and `SubmitAudioAnswer` (learner recording) |
| Migration | `db/migrations/000022_audio_questions.up.sql` | `AUDIO` type constraint + `question_audio_answers` table |
| Migration | `db/migrations/000028_user_audio_responses.up.sql` | `user_audio_responses` table for learner recordings |
| SQL | `db/query/question_audio_answers.sql` | CRUD for correct answer text per audio question |
| SQL | `db/query/user_audio_responses.sql` | CRUD for learner audio submissions |
| Routes | `internal/web_server/routes.go` | `POST /files/audio`, `POST /questions/audio-answer` |
---
## Admin Panel Frontend Steps
### Step 1: Add "AUDIO" Option to the Question Type Selector
When creating/editing a question, the admin should be able to select `AUDIO` as a question type alongside `MCQ`, `TRUE_FALSE`, and `SHORT_ANSWER`.
---
### Step 2: Build the AUDIO Question Form
When `question_type = "AUDIO"` is selected, render these fields:
| Field | API Field | Type | Required | Description |
|-------|-----------|------|----------|-------------|
| Question Text | `question_text` | `string` | ✅ | The prompt/instruction text shown to the learner |
| Voice Prompt | `voice_prompt` | `string` (URL) | Optional | Audio file URL — the question read aloud or a listening prompt |
| Sample Answer Voice | `sample_answer_voice_prompt` | `string` (URL) | Optional | Audio URL of a model/reference answer |
| Correct Answer Text | `audio_correct_answer_text` | `string` | Optional | Expected textual answer (used for grading/comparison) |
| Image | `image_url` | `string` (URL) | Optional | Supporting image for the question |
| Explanation | `explanation` | `string` | Optional | Shown to the learner after answering |
| Tips | `tips` | `string` | Optional | Hints shown before/during the question |
| Difficulty Level | `difficulty_level` | `string` | Optional | `EASY`, `MEDIUM`, or `HARD` |
| Points | `points` | `int` | Optional | Score value (defaults to 1) |
> **Note:** Hide the `options` and `short_answers` fields when AUDIO is selected — they are not used for this type.
---
### Step 3: Build an Audio Uploader Component
Create a reusable audio uploader used for both `voice_prompt` and `sample_answer_voice_prompt`.
**API Call:**
```
POST /files/audio
Content-Type: multipart/form-data
Form field: "file" — the audio file
```
**Constraints:**
- Max file size: **50 MB**
- Allowed formats: `mp3`, `wav`, `ogg`, `m4a`, `aac`, `webm`, `flac`
**Response:**
```json
{
"message": "Audio file uploaded successfully",
"data": {
"object_key": "audio/1710000000_recording.mp3",
"url": "https://...",
"content_type": "audio/mpeg"
},
"success": true
}
```
**Usage:**
1. Admin clicks "Upload Voice Prompt" → file picker opens
2. File is uploaded via `POST /files/audio`
3. Store the returned `object_key` as the value for `voice_prompt` or `sample_answer_voice_prompt`
4. Display the audio player preview (see Step 4)
---
### Step 4: Build an Audio Player / Preview Component
After uploading, let admins preview the audio.
**To get a playable URL from an object key:**
```
GET /files/url?key=<object_key>
```
**Frontend implementation:**
```html
<audio controls src="<resolved_url>"></audio>
```
This component should be shown:
- Next to the voice prompt upload field (after upload)
- Next to the sample answer voice prompt upload field (after upload)
- When viewing/editing an existing AUDIO question
---
### Step 5: Create a Practice with Audio Questions
The full admin workflow:
#### 5.1 Create the Practice Set (Question Set)
```
POST /question-sets
{
"title": "Speaking Practice - Lesson 1",
"description": "Practice your pronunciation",
"set_type": "PRACTICE",
"owner_type": "SUB_COURSE",
"owner_id": 42,
"status": "DRAFT"
}
```
#### 5.2 Upload Audio Files
```
POST /files/audio → voice_prompt object_key
POST /files/audio → sample_answer_voice_prompt object_key
```
#### 5.3 Create AUDIO Questions
```
POST /questions
{
"question_text": "Listen and repeat the following phrase",
"question_type": "AUDIO",
"voice_prompt": "minio://audio/1710000000_prompt.mp3",
"sample_answer_voice_prompt": "minio://audio/1710000000_sample.mp3",
"audio_correct_answer_text": "Hello, how are you?",
"difficulty_level": "EASY",
"points": 10
}
```
#### 5.4 Link Questions to the Practice Set
```
POST /question-sets/:setId/questions
{
"question_id": 123,
"display_order": 1
}
```
#### 5.5 (Optional) Add Personas
```
POST /question-sets/:setId/personas
{
"user_id": 5,
"display_order": 1
}
```
#### 5.6 (Optional) Reorder Practices in a Sub-course
```
PUT /course-management/practices/reorder
{
"sub_course_id": 42,
"ordered_ids": [10, 11, 12]
}
```
---
### Step 6: Display Audio Questions in the Question List
When listing questions (`GET /questions?question_type=AUDIO`), show:
- An 🔊 audio icon or "AUDIO" badge for the question type
- A mini audio player if `voice_prompt` is set
- The `audio_correct_answer_text` value (if present)
When viewing a single question (`GET /questions/:id`), the response includes:
```json
{
"question_type": "AUDIO",
"voice_prompt": "minio://audio/...",
"sample_answer_voice_prompt": "minio://audio/...",
"audio_correct_answer_text": "Hello, how are you?"
}
```
---
### Step 7: (Optional) Admin Review of Learner Audio Submissions
> **⚠️ Not yet built** — requires a new backend endpoint if needed.
Currently, learner audio submissions are stored in `user_audio_responses` but there's no admin-facing endpoint to list/review them.
**If this is needed, add:**
1. **New SQL query** in `db/query/user_audio_responses.sql`:
```sql
-- name: ListAudioResponsesByQuestionSet :many
SELECT uar.*, u.first_name, u.last_name
FROM user_audio_responses uar
JOIN users u ON u.id = uar.user_id
WHERE uar.question_set_id = $1
ORDER BY uar.created_at DESC
LIMIT $2 OFFSET $3;
```
2. **New endpoint** in `routes.go`:
```
GET /admin/question-sets/:setId/audio-responses
```
3. **Admin UI**: A table showing learner name, audio player for their recording, timestamp, and the correct answer text for comparison.
---
## API Flow Summary
```
┌─────────────────────────────────────────────────────────┐
│ ADMIN CREATES PRACTICE │
├─────────────────────────────────────────────────────────┤
│ │
│ POST /question-sets │
│ → Creates practice shell (set_type: "PRACTICE") │
│ │
│ POST /files/audio │
│ → Uploads voice_prompt audio file │
│ │
│ POST /files/audio │
│ → Uploads sample_answer_voice_prompt audio file │
│ │
│ POST /questions │
│ → Creates AUDIO question with voice prompt URLs │
│ and correct_answer_text │
│ │
│ POST /question-sets/:id/questions │
│ → Links question to practice set │
│ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ LEARNER COMPLETES PRACTICE │
├─────────────────────────────────────────────────────────┤
│ │
│ GET /files/url?key=... │
│ → Streams voice prompt audio │
│ │
│ POST /questions/audio-answer │
│ → Submits learner's audio recording │
│ │
│ POST /progress/practices/:id/complete │
│ → Marks practice as completed │
│ │
└─────────────────────────────────────────────────────────┘
```
---
## Required RBAC Permissions
Ensure the admin role has these permissions:
| Permission | Used By |
|------------|---------|
| `questions.create` | Creating AUDIO questions |
| `questions.update` | Editing AUDIO questions |
| `questions.list` | Listing questions (filter by AUDIO) |
| `questions.get` | Viewing a single question |
| `questions.delete` | Deleting questions |
| `question_sets.create` | Creating practice sets |
| `question_sets.update` | Updating practice sets |
| `question_set_items.add` | Adding questions to a set |
| `question_set_items.list` | Listing questions in a set |
| `question_set_items.remove` | Removing questions from a set |
| `question_set_items.update_order` | Reordering questions |
| `question_set_personas.add` | Adding personas |
| `practices.reorder` | Reordering practices in a sub-course |