Files
full_encoach_platform/odoo/addons/mail/tests/common_tracking.py
Yamen Ahmad 3e83d8d7d5 feat: add complete EnCoach backend — Odoo 19 + all addons
Includes:
- Odoo 19 framework (odoo/)
- 27 custom EnCoach addons (new_project/custom_addons/)
  - encoach_core, encoach_api, encoach_lms_api, encoach_adaptive_api
  - encoach_exam, encoach_taxonomy, encoach_adaptive, encoach_assignment
  - encoach_ai, encoach_ai_grading, encoach_ai_generation, encoach_ai_media
  - encoach_courseware, encoach_communication, encoach_subscription
  - encoach_notification, encoach_approval, encoach_branding
  - encoach_classroom, encoach_registration, encoach_stats
  - encoach_faq, encoach_ticket, encoach_training, encoach_resources
  - encoach_adaptive_ai, encoach_sis
- 21 OpenEduCat Enterprise modules (new_project/enterprise-19/)
- 14 OpenEduCat Community modules (new_project/openeducat_erp-19.0/)
- Configuration: odoo.conf, requirements.txt, scripts
- 200+ REST API endpoints with JWT authentication
- SRS and test documentation

Made-with: Cursor
2026-04-01 17:10:04 +04:00

197 lines
8.9 KiB
Python

from collections import defaultdict
from datetime import datetime, timedelta
from unittest.mock import patch
from odoo.addons.mail.tests.common import MailCommon
class MailTrackingDurationMixinCase(MailCommon):
@classmethod
def setUpClass(cls, tested_model, model_fields=None):
super().setUpClass()
if model_fields:
for field in model_fields:
if model_fields[field] == 'create':
model_fields[field] = cls.env[tested_model][field].create({'name': 'test'}).id
stage_1_values = {'name': 'Stage 1'}
stage_2_values = {'name': 'Stage 2'}
stage_3_values = {'name': 'Stage 3'}
stage_4_values = {'name': 'Stage 4'}
cls.track_duration_field = cls.env[tested_model]._track_duration_field
stage_model = cls.env[tested_model][cls.track_duration_field]
cls.stage_1 = stage_model.create(stage_1_values)
cls.stage_2 = stage_model.create(stage_2_values)
cls.stage_3 = stage_model.create(stage_3_values)
cls.stage_4 = stage_model.create(stage_4_values)
record_values = {'name': 'test record', cls.track_duration_field: cls.stage_1.id}
if model_fields:
record_values.update(model_fields)
cls.mock_start_time = datetime(2023, 2, 15, 12, 0, 0)
with patch.object(cls.env.cr, 'now', return_value=cls.mock_start_time):
cls.rec_1, cls.rec_2, cls.rec_3, cls.rec_4 = cls.env[tested_model].create(
[record_values for i in range(4)])
cls.flush_tracking(cls)
def _update_duration_tracking(self, record_to_tracking_dic, minutes, new_stage=False):
"""
Updates the mock duration_tracking field for multiple records based on the provided minutes.
If new_stage is defined, the stage of the records is updated as well.
Args:
record_to_tracking_dic (list): A list of tuples mapping records to their respective tracking dictionaries.
minutes (int): The number of minutes to be added to the duration tracking, which will be converted to seconds.
new_stage (int, optional): Indicated the new stage to be set for the records. Defaults to False.
"""
for record, tracking_dic in record_to_tracking_dic:
tracking_dic[str(record[self.track_duration_field].id)] += minutes * 60
if new_stage:
tracking_dic[str(new_stage.id)] += 0
record[self.track_duration_field] = new_stage
self.flush_tracking()
def assertTrackingDuration(self, records, record_to_tracking_dic):
"""
Asserts whether for multiple records their duration_tracking is equal to a dictionary
Args:
records (recordset): all the records that need to be asserted
record_to_tracking_dic (list): A list of tuples mapping records to their respective tracking dictionaries.
"""
records._compute_duration_tracking()
for record, tracking_dic in record_to_tracking_dic:
self.assertDictEqual(dict(tracking_dic), record.duration_tracking)
def _test_record_duration_tracking(self):
"""
Moves a record's many2one field through several values and asserts the duration spent in that value each time
"""
with patch.object(self.env.cr, 'now', return_value=self.mock_start_time) as now:
track_duration_tracking = defaultdict(lambda: 0)
record = self.rec_1
minutes = 5
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking([(record, track_duration_tracking)], minutes, self.stage_2)
self.assertTrackingDuration(record, [(record, track_duration_tracking)])
minutes = 100
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking([(record, track_duration_tracking)], minutes)
self.assertTrackingDuration(record, [(record, track_duration_tracking)])
minutes = 5000
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking([(record, track_duration_tracking)], minutes, self.stage_3)
self.assertTrackingDuration(record, [(record, track_duration_tracking)])
minutes = 5000
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking([(record, track_duration_tracking)], minutes, self.stage_4)
self.assertTrackingDuration(record, [(record, track_duration_tracking)])
minutes = 20
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking([(record, track_duration_tracking)], minutes, self.stage_2)
self.assertTrackingDuration(record, [(record, track_duration_tracking)])
minutes = 55
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking([(record, track_duration_tracking)], minutes, self.stage_4)
self.assertTrackingDuration(record, [(record, track_duration_tracking)])
minutes = 200
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking([(record, track_duration_tracking)], minutes)
self.assertTrackingDuration(record, [(record, track_duration_tracking)])
minutes = 300
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking([(record, track_duration_tracking)], minutes, self.stage_3)
self.assertTrackingDuration(record, [(record, track_duration_tracking)])
def _test_record_duration_tracking_batch(self):
"""
Moves for a batch of records many2one field through several values and asserts the duration
spent in that value each time.
"""
with patch.object(self.env.cr, 'now', return_value=self.mock_start_time) as now:
track_duration_tracking1 = defaultdict(lambda: 0)
track_duration_tracking2 = defaultdict(lambda: 0)
track_duration_tracking3 = defaultdict(lambda: 0)
batch = self.rec_1 | self.rec_2 | self.rec_3
record_to_tracking_dic = [
(self.rec_1, track_duration_tracking1),
(self.rec_2, track_duration_tracking2),
(self.rec_3, track_duration_tracking3)
]
minutes = 5
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking(record_to_tracking_dic, minutes, self.stage_2)
self.assertTrackingDuration(batch, record_to_tracking_dic)
minutes = 100
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking(record_to_tracking_dic, minutes)
self.assertTrackingDuration(batch, record_to_tracking_dic)
minutes = 5000
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking(record_to_tracking_dic, minutes, self.stage_3)
self.assertTrackingDuration(batch, record_to_tracking_dic)
minutes = 5000
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking(record_to_tracking_dic, minutes, self.stage_4)
self.assertTrackingDuration(batch, record_to_tracking_dic)
minutes = 20
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking(record_to_tracking_dic, minutes, self.stage_2)
self.assertTrackingDuration(batch, record_to_tracking_dic)
minutes = 55
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking(record_to_tracking_dic, minutes, self.stage_4)
self.assertTrackingDuration(batch, record_to_tracking_dic)
minutes = 200
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking(record_to_tracking_dic, minutes)
self.assertTrackingDuration(batch, record_to_tracking_dic)
minutes = 300
now.return_value += timedelta(minutes=minutes)
self._update_duration_tracking(record_to_tracking_dic, minutes, self.stage_3)
self.assertTrackingDuration(batch, record_to_tracking_dic)
def _test_queries_batch_duration_tracking(self):
"""
The MailTrackingDuration mixin is only supposed to add 3 queries
"""
batch = self.rec_1 | self.rec_2 | self.rec_3 | self.rec_4
batch[self.track_duration_field] = self.stage_2.id
self.flush_tracking()
batch[self.track_duration_field] = self.stage_4.id
self.flush_tracking()
batch[self.track_duration_field] = self.stage_1.id
self.flush_tracking()
batch[self.track_duration_field] = self.stage_3.id
self.flush_tracking()
batch[self.track_duration_field] = self.stage_2.id
with self.assertQueryCount(2):
batch._compute_duration_tracking()