EnCoach Odoo 19 custom modules
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
This commit is contained in:
137
encoach_api/controllers/stats.py
Normal file
137
encoach_api/controllers/stats.py
Normal file
@@ -0,0 +1,137 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user