// ---------- Pronunciation Assessment ----------
async function assessPronunciation(expectedText, whisperResult) {
try {
const r = await fetch('/pronounce/assess', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
expected: expectedText,
whisper: whisperResult,
lang: 'it'
})
});
if (!r.ok) { console.log('[PRONOUNCE] HTTP', r.status); return null; }
const data = await r.json();
console.log('[PRONOUNCE] score=' + data.overall_score + ' ' + data.quality);
return data;
} catch(e) {
console.log('[PRONOUNCE] ERR', e);
return null;
}
}
function renderPronunciation(assess) {
if (!assess) return '';
const s = assess.overall_score;
const emoji = s >= 90 ? '🟢' : s >= 70 ? '🟡' : s >= 50 ? '🟠' : '🔴';
let html = '
';
// Score bar
const barColor = s >= 90 ? '#4caf50' : s >= 70 ? '#ff9800' : s >= 50 ? '#f44336' : '#9e9e9e';
html += '
';
html += '
' + emoji + '';
html += '
';
html += '
Произношение: ' + s + '%
';
html += '
' + (assess.quality || '') + '
';
// Bar
html += '
';
html += '
';
// Details grid
html += '
';
// Phonemes
if (assess.phoneme_comparison) {
const pc = assess.phoneme_comparison;
html += '
🔤 Фонемы: ' + pc.accuracy + '%
';
html += '
✓ Совпадений: ' + (pc.matches || 0) + '/' + (pc.total_phonemes || 0) + '
';
if (pc.errors && pc.errors.length > 0) {
const subs = pc.errors.filter(e => e.type === 'sub');
const dels = pc.errors.filter(e => e.type === 'del');
const inss = pc.errors.filter(e => e.type === 'ins');
html += '
';
if (subs.length) html += 'Замен: ' + subs.map(e => e.expected + '→' + e.actual).join(', ') + ' ';
if (dels.length) html += 'Пропущено: ' + dels.map(e => e.expected).join(', ') + ' ';
if (inss.length) html += 'Лишних: ' + inss.map(e => e.actual).join(', ') + '';
html += '
';
}
}
// Timing
if (assess.timing) {
const tm = assess.timing;
html += '
⏱ Ритм: ' + (tm.rhythm_score || 0) + '%
';
html += '
📏 Длит-ть: ' + (tm.total_duration || 0).toFixed(1) + 'с
';
if (tm.timing_quality) {
const tq = tm.timing_quality;
html += '
';
html += tq === 'good' ? '✅ Ритм ровный' : tq === 'ok' ? '⚠️ Ритм неровный' : '❌ Ритм сбит';
html += '
';
}
}
html += '
';
// Feedback
if (assess.feedback) {
html += '
';
html += '💬 ' + assess.feedback;
html += '
';
}
// Phoneme detail
if (assess.expected && assess.expected.phonemes) {
html += '
';
html += '🎯 Эталон: /' + assess.expected.phonemes.join(' ') + '/';
if (assess.actual && assess.actual.phonemes) {
html += ' | 🗣 Вы: /' + assess.actual.phonemes.join(' ') + '/';
}
html += '
';
}
html += '
';
return html;
}