Files
encoach_be_odoo19/encoach_api/controllers/stats.py
Talal Sharabi fa538a1f12 Fix remaining controller serialization and model mismatches
- Replace all _serialize_group/exam/assignment/stat/session calls with _serialize
- Fix sessions controller to use exam_data JSON field instead of nonexistent columns
- Fix stats controller to use correct model fields (score_correct/total/missing, solutions)

Made-with: Cursor
2026-03-15 01:12:17 +04:00

150 lines
5.7 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 body.get('solutions') is not None:
vals['solutions'] = body['solutions'] if isinstance(body['solutions'], (dict, list)) else json.loads(body['solutions'])
if body.get('exercise'):
vals['exercise'] = body['exercise']
if 'scoreCorrect' in body:
vals['score_correct'] = int(body['scoreCorrect'])
if 'scoreTotal' in body:
vals['score_total'] = int(body['scoreTotal'])
if 'scoreMissing' in body:
vals['score_missing'] = int(body['scoreMissing'])
if body.get('examId'):
vals['exam_id'] = int(body['examId'])
if body.get('assignmentId'):
vals['assignment_id'] = int(body['assignmentId'])
if body.get('type'):
vals['stat_type'] = body['type']
if 'timeSpent' in body:
vals['time_spent'] = int(body['timeSpent'])
if 'isPractice' in body:
vals['is_practice'] = bool(body['isPractice'])
if stat_id:
record = Stat.browse(int(stat_id))
if record.exists():
record.write(vals)
return self._json_response(self._serialize(record))
vals['user_id'] = user.id
record = Stat.create(vals)
return self._json_response(self._serialize(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))
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(s) for s in stats[:100]],
})
except Exception:
_logger.exception("Statistical query error")
return self._error_response('Internal server error', 500)