- 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
81 lines
2.8 KiB
Python
81 lines
2.8 KiB
Python
import json
|
|
import logging
|
|
|
|
from odoo import http
|
|
from odoo.http import request
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _json_response(data, status=200):
|
|
return request.make_json_response(data, status=status)
|
|
|
|
|
|
def _rubric_to_dict(rec):
|
|
criteria_text = rec.criteria or ''
|
|
criteria_count = 0
|
|
if criteria_text:
|
|
try:
|
|
parsed = json.loads(criteria_text)
|
|
if isinstance(parsed, list):
|
|
criteria_count = len(parsed)
|
|
elif isinstance(parsed, dict):
|
|
criteria_count = len(parsed)
|
|
else:
|
|
criteria_count = len([l for l in criteria_text.split('\n') if l.strip()])
|
|
except (json.JSONDecodeError, ValueError):
|
|
criteria_count = len([l for l in criteria_text.split('\n') if l.strip()])
|
|
|
|
return {
|
|
'id': rec.id,
|
|
'name': rec.name,
|
|
'skill': rec.skill or '',
|
|
'exam_type': rec.exam_type or '',
|
|
'criteria': criteria_count or 1,
|
|
'criteria_text': criteria_text,
|
|
'levels': ['A1', 'A2', 'B1', 'B2', 'C1', 'C2'],
|
|
'created': rec.create_date.strftime('%Y-%m-%d') if rec.create_date else '',
|
|
}
|
|
|
|
|
|
class EncoachRubricController(http.Controller):
|
|
|
|
@http.route('/api/rubrics', type='http', auth='user',
|
|
methods=['GET'], csrf=False)
|
|
def list_rubrics(self, **kw):
|
|
try:
|
|
Rubric = request.env['encoach.rubric'].sudo()
|
|
limit = int(kw.get('limit', 50))
|
|
offset = int(kw.get('offset', 0))
|
|
records = Rubric.search([], limit=limit, offset=offset,
|
|
order='create_date desc')
|
|
total = Rubric.search_count([])
|
|
return _json_response({
|
|
'items': [_rubric_to_dict(r) for r in records],
|
|
'total': total,
|
|
})
|
|
except Exception as e:
|
|
_logger.exception('rubrics list failed')
|
|
return _json_response({'error': str(e)}, 500)
|
|
|
|
@http.route('/api/rubrics', type='http', auth='user',
|
|
methods=['POST'], csrf=False)
|
|
def create_rubric(self, **kw):
|
|
try:
|
|
body = json.loads(request.httprequest.data or '{}')
|
|
name = body.get('name', '').strip()
|
|
if not name:
|
|
return _json_response({'error': 'name is required'}, 400)
|
|
|
|
vals = {
|
|
'name': name,
|
|
'skill': body.get('skill', 'writing'),
|
|
'criteria': body.get('criteria', ''),
|
|
'exam_type': body.get('exam_type', 'academic'),
|
|
}
|
|
rec = Rubric = request.env['encoach.rubric'].sudo().create(vals)
|
|
return _json_response(_rubric_to_dict(rec), 201)
|
|
except Exception as e:
|
|
_logger.exception('rubric create failed')
|
|
return _json_response({'error': str(e)}, 500)
|