feat(v3): restructure project + add complete frontend
- Restructure: move backend from new_project/ to backend/ - Add full React/TypeScript frontend (37 pages, 17 services, 16 type defs, 11 query hooks) - Add docs/ with SRS specs, user stories, and workflow documentation - Update .gitignore for new directory layout Workflows implemented: WF1 User Signup, WF2 Placement Test, WF3 Exam Configuration, WF4 General English Exam, WF5 Course Generation, WF6 Entity Student Onboarding, AI Course Generation, Adaptive Learning Engine UI, White-Label Branding, Score Release Made-with: Cursor
This commit is contained in:
67
custom_addons/encoach_adaptive/services/alert_service.py
Normal file
67
custom_addons/encoach_adaptive/services/alert_service.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import fields
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AdaptiveAlertService:
|
||||
"""Checks for students with no learning progress and creates teacher alerts."""
|
||||
|
||||
@classmethod
|
||||
def check_no_progress(cls, env):
|
||||
"""Find students with no adaptive events within threshold days and alert teachers.
|
||||
|
||||
Called by scheduled action (ir.cron).
|
||||
"""
|
||||
Settings = env['encoach.adaptive.settings'].sudo()
|
||||
Event = env['encoach.adaptive.event'].sudo()
|
||||
Path = env['encoach.adaptive.path'].sudo()
|
||||
Activity = env['mail.activity'].sudo()
|
||||
|
||||
all_settings = Settings.search([])
|
||||
if not all_settings:
|
||||
all_settings = Settings.new({'no_progress_alert_days': 3})
|
||||
|
||||
for setting in all_settings:
|
||||
days = setting.no_progress_alert_days or 3
|
||||
cutoff = fields.Datetime.now() - timedelta(days=days)
|
||||
teacher = setting.teacher_id
|
||||
|
||||
if not teacher:
|
||||
continue
|
||||
|
||||
# Find students with active paths but no recent events
|
||||
paths = Path.search([])
|
||||
for path in paths:
|
||||
student_id = path.student_id.id
|
||||
recent_events = Event.search_count([
|
||||
('student_id', '=', student_id),
|
||||
('created_at', '>=', cutoff),
|
||||
])
|
||||
|
||||
if recent_events == 0:
|
||||
# Check if alert already exists for this student
|
||||
existing = Activity.search([
|
||||
('res_model', '=', 'res.users'),
|
||||
('res_id', '=', student_id),
|
||||
('user_id', '=', teacher.id),
|
||||
('summary', 'ilike', 'No learning progress'),
|
||||
('date_deadline', '>=', fields.Date.today()),
|
||||
], limit=1)
|
||||
|
||||
if not existing:
|
||||
activity_type = env.ref('mail.mail_activity_data_todo', raise_if_not_found=False)
|
||||
Activity.create({
|
||||
'res_model_id': env['ir.model']._get_id('res.users'),
|
||||
'res_id': student_id,
|
||||
'user_id': teacher.id,
|
||||
'activity_type_id': activity_type.id if activity_type else False,
|
||||
'summary': f'No learning progress for {days}+ days',
|
||||
'note': f'Student {path.student_id.name} has not shown any '
|
||||
f'learning activity in the last {days} days.',
|
||||
'date_deadline': fields.Date.today(),
|
||||
})
|
||||
_logger.info('Created no-progress alert for student %s → teacher %s',
|
||||
path.student_id.name, teacher.name)
|
||||
Reference in New Issue
Block a user