feat: SAM integration, deployment scripts, and audio response enhancements
- Add SAM (Sistema de Administración Académica) integration with sync endpoints - Add deployment automation (deploy.sh, remote-setup.sh, setup-nginx-ssl.sh) - Add nginx proxy configuration for SSL with Let's Encrypt - Add audio response support for student lessons (migrations, handlers) - Add audio evaluations admin page - Update CORS to support wildcard subdomains for norteamericano.cl - Add comprehensive deployment documentation (DESPLIEGUE.md, ManualDeConfiguracion.md) - Update docker-compose.yml with nginx-proxy and acme-companion services - Remove outdated documentation files Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -430,6 +430,8 @@ export default function LessonPlayerPage({ params }: { params: { id: string, les
|
||||
keywords={block.keywords}
|
||||
timeLimit={block.timeLimit}
|
||||
isGraded={lesson.is_graded}
|
||||
lessonId={params.lessonId}
|
||||
blockId={block.id}
|
||||
onComplete={(score) => handleBlockComplete(block.id, score)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -10,6 +10,8 @@ interface AudioResponsePlayerProps {
|
||||
keywords?: string[];
|
||||
timeLimit?: number;
|
||||
isGraded?: boolean;
|
||||
lessonId?: string;
|
||||
blockId?: string;
|
||||
onComplete?: (score: number, transcript: string) => void;
|
||||
}
|
||||
|
||||
@@ -19,6 +21,8 @@ export default function AudioResponsePlayer({
|
||||
keywords = [],
|
||||
timeLimit,
|
||||
isGraded = false,
|
||||
lessonId,
|
||||
blockId,
|
||||
onComplete
|
||||
}: AudioResponsePlayerProps) {
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
@@ -195,9 +199,22 @@ export default function AudioResponsePlayer({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lessonId || !blockId) {
|
||||
console.error("Missing lessonId or blockId");
|
||||
alert("Error: Missing lesson or block information");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsTranscribing(true);
|
||||
try {
|
||||
const result = await lmsApi.evaluateAudioFile(audioBlob, prompt, keywords);
|
||||
const result = await lmsApi.evaluateAudioFile(
|
||||
audioBlob,
|
||||
prompt,
|
||||
keywords,
|
||||
lessonId,
|
||||
blockId,
|
||||
recordingTime
|
||||
);
|
||||
setEvaluation({
|
||||
score: result.score,
|
||||
foundKeywords: result.found_keywords,
|
||||
|
||||
@@ -604,17 +604,23 @@ export const lmsApi = {
|
||||
body: JSON.stringify({ transcript, prompt, keywords })
|
||||
});
|
||||
},
|
||||
async evaluateAudioFile(file: Blob, prompt: string, keywords: string[]): Promise<AudioGradingResponse> {
|
||||
async evaluateAudioFile(file: Blob, prompt: string, keywords: string[], lessonId: string, blockId: string, duration?: number): Promise<AudioGradingResponse> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file, 'recorded_audio.webm');
|
||||
formData.append('prompt', prompt);
|
||||
formData.append('keywords', JSON.stringify(keywords));
|
||||
formData.append('lesson_id', lessonId);
|
||||
formData.append('block_id', blockId);
|
||||
if (duration) {
|
||||
formData.append('duration', duration.toString());
|
||||
}
|
||||
|
||||
const token = getToken();
|
||||
return fetch(`${getLmsApiUrl()}/audio/evaluate-file`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
||||
// Don't set Content-Type for FormData - browser sets it with boundary
|
||||
},
|
||||
body: formData
|
||||
}).then(async res => {
|
||||
|
||||
Reference in New Issue
Block a user