feat: QA fixes, new APIs (assignments, rubrics, custom exams), Generation page enhancements

- Fix ELAI video generation (correct render endpoint, script splitting for 60s limit)
- Fix speaking script generation error handling and empty response display
- Add custom exam list API (GET /api/exam/custom/list)
- Add assignments REST API (list, create, get)
- Add rubrics REST API (list, create)
- Enhance Generation page: dynamic exam structures, auto-module selection, preview dialog, audio player
- Improve submit feedback with exam ID and status in toast notifications
- Fix ExamsListPage to show both custom exams and exam sessions
- Connect RubricsPage to backend API with fallback data
- Add Dockerfile, docker-compose.yml, requirements.txt for deployment
- Fix placement, grading, scoring, and auth controllers
- Add ErrorBoundary component for frontend resilience
- Add QA report and credentials documentation

Made-with: Cursor
This commit is contained in:
Yamen Ahmad
2026-04-12 14:26:39 +04:00
parent 6a62a43d61
commit ca91544acd
20 changed files with 682 additions and 75 deletions

View File

@@ -303,18 +303,49 @@ class EncoachPlacementController(http.Controller):
@jwt_required
def speaking_status(self, **kw):
try:
session_id = kw.get('session_id')
upload_id = kw.get('upload_id')
if not upload_id:
return _error_response('upload_id is required', 400)
attachment = request.env['ir.attachment'].sudo().browse(int(upload_id))
if not attachment.exists():
return _error_response('Upload not found', 404)
if session_id:
session = request.env['encoach.cat.session'].sudo().browse(int(session_id))
if not session.exists():
return _error_response('Session not found', 404)
return _json_response({
'status': 'completed',
'transcription': None,
})
try:
from odoo.addons.encoach_ai.services.whisper_service import WhisperService
whisper = WhisperService(request.env)
attachments = request.env['ir.attachment'].sudo().search([
('res_model', '=', 'encoach.cat.session'),
('create_uid', '=', request.env.uid),
], limit=1, order='create_date desc')
if attachments and attachments.datas:
audio_data = base64.b64decode(attachments.datas)
transcript = whisper.transcribe(audio_data, use_api=True)
return _json_response({
'status': 'scored',
'transcription': transcript.get('text', ''),
})
except Exception as ai_err:
_logger.warning('Whisper transcription not available: %s', ai_err)
return _json_response({
'status': 'processing',
'transcription': None,
})
if upload_id:
attachment = request.env['ir.attachment'].sudo().browse(int(upload_id))
if not attachment.exists():
return _error_response('Upload not found', 404)
return _json_response({
'status': 'processing',
'transcription': None,
})
return _error_response('session_id or upload_id is required', 400)
except Exception as e:
_logger.exception('speaking status failed')