Full backend implementation with custom Odoo modules: - encoach_api: Core API, user management, JWT auth - encoach_exam: Exam generation (reading, writing, listening, speaking) - encoach_evaluate: AI-powered evaluation (writing, speaking) - encoach_training: Training tips and walkthrough - encoach_storage: File storage management - encoach_payment: Stripe, PayPal, Paymob integration - encoach_mail: Email notifications Made-with: Cursor
138 lines
5.2 KiB
Python
138 lines
5.2 KiB
Python
import json
|
|
import logging
|
|
|
|
from odoo import http
|
|
from odoo.http import request
|
|
|
|
from .base import EncoachMixin
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class StatsController(EncoachMixin, http.Controller):
|
|
|
|
# -------------------------------------------------------------- save
|
|
@http.route('/api/stats', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
|
|
def save_stats(self, **kwargs):
|
|
try:
|
|
user = self._authenticate()
|
|
if not user:
|
|
return self._error_response('Unauthorized', 401)
|
|
|
|
body = self._get_json_body()
|
|
stat_id = body.get('_id') or body.get('id')
|
|
Stat = request.env['encoach.stat'].sudo()
|
|
|
|
vals = {}
|
|
if body.get('sessionId'):
|
|
vals['session_id'] = int(body['sessionId'])
|
|
if body.get('module'):
|
|
vals['module'] = body['module']
|
|
if 'score' in body:
|
|
vals['score'] = body['score']
|
|
if body.get('result') is not None:
|
|
vals['result'] = json.dumps(body['result']) if isinstance(body['result'], dict) else body['result']
|
|
if body.get('examId'):
|
|
vals['exam_id'] = int(body['examId'])
|
|
if body.get('assignmentId'):
|
|
vals['assignment_id'] = int(body['assignmentId'])
|
|
|
|
if stat_id:
|
|
record = Stat.browse(int(stat_id))
|
|
if record.exists():
|
|
record.write(vals)
|
|
return self._json_response(self._serialize_stat(record))
|
|
|
|
vals['user_id'] = user.id
|
|
record = Stat.create(vals)
|
|
return self._json_response(self._serialize_stat(record), status=201)
|
|
except Exception:
|
|
_logger.exception("Save stats error")
|
|
return self._error_response('Internal server error', 500)
|
|
|
|
# --------------------------------------------------------------- get
|
|
@http.route(
|
|
'/api/stats/<int:stat_id>', type='http', auth='public',
|
|
methods=['GET', 'OPTIONS'], csrf=False, cors='*',
|
|
)
|
|
def get_stat(self, stat_id, **kwargs):
|
|
try:
|
|
user = self._authenticate()
|
|
if not user:
|
|
return self._error_response('Unauthorized', 401)
|
|
|
|
stat = request.env['encoach.stat'].sudo().browse(stat_id)
|
|
if not stat.exists():
|
|
return self._error_response('Stat not found', 404)
|
|
|
|
return self._json_response(self._serialize_stat(stat))
|
|
except Exception:
|
|
_logger.exception("Get stat error")
|
|
return self._error_response('Internal server error', 500)
|
|
|
|
# --------------------------------------------------------- statistical
|
|
@http.route('/api/statistical', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
|
|
def statistical_query(self, **kwargs):
|
|
"""Run statistical queries against exam stats."""
|
|
try:
|
|
user = self._authenticate()
|
|
if not user:
|
|
return self._error_response('Unauthorized', 401)
|
|
|
|
body = self._get_json_body()
|
|
Stat = request.env['encoach.stat'].sudo()
|
|
|
|
domain = []
|
|
user_id = body.get('userId')
|
|
if user_id:
|
|
domain.append(('user_id', '=', int(user_id)))
|
|
|
|
module = body.get('module')
|
|
if module:
|
|
domain.append(('module', '=', module))
|
|
|
|
group_id = body.get('groupId')
|
|
if group_id:
|
|
domain.append(('assignment_id.group_id', '=', int(group_id)))
|
|
|
|
assignment_id = body.get('assignmentId')
|
|
if assignment_id:
|
|
domain.append(('assignment_id', '=', int(assignment_id)))
|
|
|
|
date_from = body.get('dateFrom')
|
|
if date_from:
|
|
domain.append(('create_date', '>=', date_from))
|
|
|
|
date_to = body.get('dateTo')
|
|
if date_to:
|
|
domain.append(('create_date', '<=', date_to))
|
|
|
|
stats = Stat.search(domain, order='create_date desc')
|
|
|
|
scores = [s.score for s in stats if s.score]
|
|
avg_score = sum(scores) / len(scores) if scores else 0
|
|
total = len(stats)
|
|
|
|
module_breakdown = {}
|
|
for s in stats:
|
|
mod = getattr(s, 'module', 'unknown')
|
|
if mod not in module_breakdown:
|
|
module_breakdown[mod] = {'count': 0, 'scores': []}
|
|
module_breakdown[mod]['count'] += 1
|
|
if s.score:
|
|
module_breakdown[mod]['scores'].append(s.score)
|
|
|
|
for mod, data in module_breakdown.items():
|
|
data['average'] = sum(data['scores']) / len(data['scores']) if data['scores'] else 0
|
|
del data['scores']
|
|
|
|
return self._json_response({
|
|
'total': total,
|
|
'averageScore': round(avg_score, 2),
|
|
'moduleBreakdown': module_breakdown,
|
|
'stats': [self._serialize_stat(s) for s in stats[:100]],
|
|
})
|
|
except Exception:
|
|
_logger.exception("Statistical query error")
|
|
return self._error_response('Internal server error', 500)
|