304 lines
10 KiB
Markdown
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 |
|