[IMP] mail_activity_reminder: black, isort, prettier

pull/1348/head
Denis Roussel 2023-04-21 18:23:02 +02:00 committed by Ruchir Shukla
parent 651f1fc36a
commit f2284c8b01
6 changed files with 158 additions and 174 deletions

View File

@ -3,22 +3,15 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{ {
'name': 'Mail Activity Reminder', "name": "Mail Activity Reminder",
'version': '12.0.1.0.1', "version": "12.0.1.0.1",
'category': 'Discuss', "category": "Discuss",
'website': 'https://github.com/OCA/social', "website": "https://github.com/OCA/social",
'author': "author": "CorporateHub, " "Odoo Community Association (OCA)",
'CorporateHub, ' "license": "AGPL-3",
'Odoo Community Association (OCA)', "installable": True,
'license': 'AGPL-3', "application": False,
'installable': True, "summary": "Reminder notifications about planned activities",
'application': False, "depends": ["mail",],
'summary': 'Reminder notifications about planned activities', "data": ["data/mail_activity_reminder_cron.xml", "views/mail_activity_type.xml",],
'depends': [
'mail',
],
'data': [
'data/mail_activity_reminder_cron.xml',
'views/mail_activity_type.xml',
],
} }

View File

@ -2,43 +2,38 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import datetime, time from datetime import datetime, time
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from pytz import timezone, UTC from pytz import UTC, timezone
from odoo import _, api, fields, models from odoo import _, api, fields, models
class MailActivity(models.Model): class MailActivity(models.Model):
_inherit = 'mail.activity' _inherit = "mail.activity"
next_reminder = fields.Datetime( next_reminder = fields.Datetime(
string='Next reminder', string="Next reminder",
compute='_compute_next_reminder', compute="_compute_next_reminder",
compute_sudo=True, compute_sudo=True,
store=True, store=True,
) )
last_reminder_local = fields.Datetime( last_reminder_local = fields.Datetime(string="Last reminder (local)",)
string='Last reminder (local)',
)
deadline = fields.Datetime( deadline = fields.Datetime(
string='Deadline', string="Deadline", compute="_compute_deadline", compute_sudo=True, store=True,
compute='_compute_deadline',
compute_sudo=True,
store=True,
) )
@api.model @api.model
def _get_activities_to_remind_domain(self): def _get_activities_to_remind_domain(self):
"""Hook for extensions""" """Hook for extensions"""
return [ return [
('next_reminder', '<=', fields.Datetime.now()), ("next_reminder", "<=", fields.Datetime.now()),
('deadline', '>=', fields.Datetime.now()), ("deadline", ">=", fields.Datetime.now()),
] ]
@api.model @api.model
def _get_activities_to_remind(self): def _get_activities_to_remind(self):
return self \ return self.search(self._get_activities_to_remind_domain())
.search(self._get_activities_to_remind_domain())
@api.model @api.model
def _process_reminders(self): def _process_reminders(self):
@ -48,10 +43,7 @@ class MailActivity(models.Model):
@api.multi @api.multi
@api.depends( @api.depends(
'user_id.tz', "user_id.tz", "activity_type_id.reminders", "deadline", "last_reminder_local",
'activity_type_id.reminders',
'deadline',
'last_reminder_local',
) )
def _compute_next_reminder(self): def _compute_next_reminder(self):
now = fields.Datetime.now() now = fields.Datetime.now()
@ -64,37 +56,39 @@ class MailActivity(models.Model):
activity.next_reminder = None activity.next_reminder = None
continue continue
reminders.sort(reverse=True) reminders.sort(reverse=True)
tz = timezone(activity.user_id.sudo().tz or 'UTC') tz = timezone(activity.user_id.sudo().tz or "UTC")
last_reminder_local = tz.localize( last_reminder_local = (
activity.last_reminder_local tz.localize(activity.last_reminder_local)
) if activity.last_reminder_local else None if activity.last_reminder_local
local_deadline = tz.localize(datetime.combine( else None
activity.date_deadline,
time.min # Schedule reminder based of beginning of day
))
for reminder in reminders:
next_reminder_local = local_deadline - relativedelta(
days=reminder,
) )
if not last_reminder_local \ local_deadline = tz.localize(
or next_reminder_local > last_reminder_local: datetime.combine(
activity.date_deadline,
time.min, # Schedule reminder based of beginning of day
)
)
for reminder in reminders:
next_reminder_local = local_deadline - relativedelta(days=reminder,)
if not last_reminder_local or next_reminder_local > last_reminder_local:
break break
if last_reminder_local \ if last_reminder_local and next_reminder_local <= last_reminder_local:
and next_reminder_local <= last_reminder_local:
activity.next_reminder = None activity.next_reminder = None
continue continue
activity.next_reminder = next_reminder_local \ activity.next_reminder = next_reminder_local.astimezone(UTC).replace(
.astimezone(UTC) \ tzinfo=None
.replace(tzinfo=None) )
@api.multi @api.multi
@api.depends('user_id.tz', 'date_deadline') @api.depends("user_id.tz", "date_deadline")
def _compute_deadline(self): def _compute_deadline(self):
for activity in self: for activity in self:
tz = timezone(activity.user_id.sudo().tz or 'UTC') tz = timezone(activity.user_id.sudo().tz or "UTC")
activity.deadline = tz.localize( activity.deadline = (
datetime.combine(activity.date_deadline, time.max) tz.localize(datetime.combine(activity.date_deadline, time.max))
).astimezone(UTC).replace(tzinfo=None) .astimezone(UTC)
.replace(tzinfo=None)
)
@api.multi @api.multi
def action_notify(self): def action_notify(self):
@ -103,31 +97,27 @@ class MailActivity(models.Model):
for activity in self: for activity in self:
if activity.last_reminder_local: if activity.last_reminder_local:
continue continue
tz = timezone(activity.user_id.sudo().tz or 'UTC') tz = timezone(activity.user_id.sudo().tz or "UTC")
activity.last_reminder_local = utc_now \ activity.last_reminder_local = utc_now.astimezone(tz).replace(tzinfo=None)
.astimezone(tz) \
.replace(tzinfo=None)
@api.multi @api.multi
def action_remind(self): def action_remind(self):
IrModel = self.env['ir.model'] IrModel = self.env["ir.model"]
MailThread = self.env['mail.thread'] MailThread = self.env["mail.thread"]
message_activity_assigned = self.env.ref( message_activity_assigned = self.env.ref("mail.message_activity_assigned")
'mail.message_activity_assigned'
)
utc_now = fields.Datetime.now().replace(tzinfo=UTC) utc_now = fields.Datetime.now().replace(tzinfo=UTC)
for activity in self: for activity in self:
tz = timezone(activity.user_id.sudo().tz or 'UTC') tz = timezone(activity.user_id.sudo().tz or "UTC")
local_now = utc_now.astimezone(tz) local_now = utc_now.astimezone(tz)
model_description = IrModel._get(activity.res_model).display_name model_description = IrModel._get(activity.res_model).display_name
subject = _('%s: %s assigned to you, %d day(s) remaining') % ( subject = _("%s: %s assigned to you, %d day(s) remaining") % (
activity.res_name, activity.res_name,
activity.summary or activity.activity_type_id.name, activity.summary or activity.activity_type_id.name,
(activity.date_deadline - local_now.date()).days (activity.date_deadline - local_now.date()).days,
) )
body = message_activity_assigned.render( body = message_activity_assigned.render(
dict(activity=activity, model_description=model_description), dict(activity=activity, model_description=model_description),
engine='ir.qweb', engine="ir.qweb",
minimal_qcontext=True, minimal_qcontext=True,
) )
MailThread.message_notify( MailThread.message_notify(
@ -136,6 +126,6 @@ class MailActivity(models.Model):
subject=subject, subject=subject,
record_name=activity.res_name, record_name=activity.res_name,
model_description=model_description, model_description=model_description,
notif_layout='mail.mail_notification_light', notif_layout="mail.mail_notification_light",
) )
activity.last_reminder_local = local_now.replace(tzinfo=None) activity.last_reminder_local = local_now.replace(tzinfo=None)

View File

@ -7,12 +7,12 @@ from odoo import api, fields, models
class MailActivityType(models.Model): class MailActivityType(models.Model):
_inherit = 'mail.activity.type' _inherit = "mail.activity.type"
reminders = fields.Char( reminders = fields.Char(
string='Reminders', string="Reminders",
help=( help=(
'A non-digit-separated list of offsets (in days) when reminders' "A non-digit-separated list of offsets (in days) when reminders"
' should be fired: e.g. 0 means "on the deadline day" while' ' should be fired: e.g. 0 means "on the deadline day" while'
' 5 means "5 calendar days before the deadline".' ' 5 means "5 calendar days before the deadline".'
), ),
@ -24,4 +24,4 @@ class MailActivityType(models.Model):
self.ensure_one() self.ensure_one()
if not self.reminders: if not self.reminders:
return [] return []
return [int(x) for x in split(r'\D+', self.reminders) if x] return [int(x) for x in split(r"\D+", self.reminders) if x]

View File

@ -2,6 +2,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from datetime import datetime from datetime import datetime
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from freezegun import freeze_time from freezegun import freeze_time
@ -9,88 +10,85 @@ from odoo.tests import common
class TestMailActivityReminder(common.SavepointCase): class TestMailActivityReminder(common.SavepointCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()
cls.env = cls.env(context=dict( cls.env = cls.env(
cls.env.context, context=dict(
tracking_disable=True, cls.env.context, tracking_disable=True, no_reset_password=True,
no_reset_password=True, )
)) )
cls.ResUsers = cls.env['res.users'] cls.ResUsers = cls.env["res.users"]
cls.Company = cls.env['res.company'] cls.Company = cls.env["res.company"]
cls.MailActivityType = cls.env['mail.activity.type'] cls.MailActivityType = cls.env["mail.activity.type"]
cls.MailActivity = cls.env['mail.activity'] cls.MailActivity = cls.env["mail.activity"]
cls.company_id = cls.Company._company_default_get() cls.company_id = cls.Company._company_default_get()
cls.now = datetime(2020, 4, 19, 15, 00) cls.now = datetime(2020, 4, 19, 15, 00)
cls.today = cls.now.date() cls.today = cls.now.date()
cls.model_res_partner = cls.env['ir.model'].search( cls.model_res_partner = cls.env["ir.model"].search(
[('model', '=', 'res.partner')], limit=1 [("model", "=", "res.partner")], limit=1
) )
cls.partner_DecoAddict = cls.env['res.partner'].search( cls.partner_DecoAddict = cls.env["res.partner"].search(
[('name', 'ilike', 'Deco Addict')], limit=1 [("name", "ilike", "Deco Addict")], limit=1
) )
def test_none_reminders(self): def test_none_reminders(self):
activity_type = self.MailActivityType.create({ activity_type = self.MailActivityType.create({"name": "Activity Type",})
'name': 'Activity Type',
})
self.assertEqual(activity_type._get_reminder_offsets(), []) self.assertEqual(activity_type._get_reminder_offsets(), [])
def test_empty_reminders(self): def test_empty_reminders(self):
activity_type = self.MailActivityType.create({ activity_type = self.MailActivityType.create(
'name': 'Activity Type', {"name": "Activity Type", "reminders": " -./",}
'reminders': ' -./', )
})
self.assertEqual(activity_type._get_reminder_offsets(), []) self.assertEqual(activity_type._get_reminder_offsets(), [])
def test_delimiters(self): def test_delimiters(self):
activity_type = self.MailActivityType.create({ activity_type = self.MailActivityType.create(
'name': 'Activity Type', {"name": "Activity Type", "reminders": "0 1_2/3.4t5",}
'reminders': '0 1_2/3.4t5', )
}) self.assertEqual(activity_type._get_reminder_offsets(), [0, 1, 2, 3, 4, 5])
self.assertEqual(activity_type._get_reminder_offsets(), [
0, 1, 2, 3, 4, 5
])
def test_first_notice_is_reminder(self): def test_first_notice_is_reminder(self):
activity_type = self.MailActivityType.create({ activity_type = self.MailActivityType.create(
'name': 'Activity Type', {"name": "Activity Type", "reminders": "0",}
'reminders': '0', )
}) user = self.ResUsers.sudo().create(
user = self.ResUsers.sudo().create({ {
'name': 'User', "name": "User",
'login': 'user', "login": "user",
'email': 'user@example.com', "email": "user@example.com",
'company_id': self.company_id.id, "company_id": self.company_id.id,
}) }
activity = self.MailActivity.create({ )
'summary': 'Activity', activity = self.MailActivity.create(
'activity_type_id': activity_type.id, {
'res_model_id': self.model_res_partner.id, "summary": "Activity",
'res_id': self.partner_DecoAddict.id, "activity_type_id": activity_type.id,
'date_deadline': self.today, "res_model_id": self.model_res_partner.id,
'user_id': user.id, "res_id": self.partner_DecoAddict.id,
}) "date_deadline": self.today,
"user_id": user.id,
}
)
self.assertTrue(activity.last_reminder_local) self.assertTrue(activity.last_reminder_local)
def test_reminder_behaviour(self): def test_reminder_behaviour(self):
activity_type = self.MailActivityType.create({ activity_type = self.MailActivityType.create(
'name': 'Activity Type', {"name": "Activity Type", "reminders": "0/2",}
'reminders': '0/2', )
})
with freeze_time(self.now): with freeze_time(self.now):
activity = self.MailActivity.create({ activity = self.MailActivity.create(
'summary': 'Activity', {
'activity_type_id': activity_type.id, "summary": "Activity",
'res_model_id': self.model_res_partner.id, "activity_type_id": activity_type.id,
'res_id': self.partner_DecoAddict.id, "res_model_id": self.model_res_partner.id,
'date_deadline': self.today + relativedelta(days=5), "res_id": self.partner_DecoAddict.id,
}) "date_deadline": self.today + relativedelta(days=5),
}
)
with freeze_time(self.now): with freeze_time(self.now):
activities = self.MailActivity._get_activities_to_remind() activities = self.MailActivity._get_activities_to_remind()
@ -120,19 +118,20 @@ class TestMailActivityReminder(common.SavepointCase):
self.assertFalse(activities) self.assertFalse(activities)
def test_reminder_flow(self): def test_reminder_flow(self):
activity_type = self.MailActivityType.create({ activity_type = self.MailActivityType.create(
'name': 'Activity Type', {"name": "Activity Type", "reminders": "0/2",}
'reminders': '0/2', )
})
with freeze_time(self.now): with freeze_time(self.now):
activity = self.MailActivity.create({ activity = self.MailActivity.create(
'summary': 'Activity', {
'activity_type_id': activity_type.id, "summary": "Activity",
'res_model_id': self.model_res_partner.id, "activity_type_id": activity_type.id,
'res_id': self.partner_DecoAddict.id, "res_model_id": self.model_res_partner.id,
'date_deadline': self.today + relativedelta(days=5), "res_id": self.partner_DecoAddict.id,
}) "date_deadline": self.today + relativedelta(days=5),
}
)
with freeze_time(self.now): with freeze_time(self.now):
activities = self.MailActivity._process_reminders() activities = self.MailActivity._process_reminders()
@ -155,19 +154,20 @@ class TestMailActivityReminder(common.SavepointCase):
self.assertEqual(activities, activity) self.assertEqual(activities, activity)
def test_repeated_reminder(self): def test_repeated_reminder(self):
activity_type = self.MailActivityType.create({ activity_type = self.MailActivityType.create(
'name': 'Activity Type', {"name": "Activity Type", "reminders": "0",}
'reminders': '0', )
})
with freeze_time(self.now): with freeze_time(self.now):
activity = self.MailActivity.create({ activity = self.MailActivity.create(
'summary': 'Activity', {
'activity_type_id': activity_type.id, "summary": "Activity",
'res_model_id': self.model_res_partner.id, "activity_type_id": activity_type.id,
'res_id': self.partner_DecoAddict.id, "res_model_id": self.model_res_partner.id,
'date_deadline': self.today + relativedelta(days=1), "res_id": self.partner_DecoAddict.id,
}) "date_deadline": self.today + relativedelta(days=1),
}
)
with freeze_time(self.now + relativedelta(days=1)): with freeze_time(self.now + relativedelta(days=1)):
activities = self.MailActivity._process_reminders() activities = self.MailActivity._process_reminders()
@ -177,19 +177,20 @@ class TestMailActivityReminder(common.SavepointCase):
self.assertFalse(activities) self.assertFalse(activities)
def test_overdue_reminder(self): def test_overdue_reminder(self):
activity_type = self.MailActivityType.create({ activity_type = self.MailActivityType.create(
'name': 'Activity Type', {"name": "Activity Type", "reminders": "0",}
'reminders': '0', )
})
with freeze_time(self.now): with freeze_time(self.now):
self.MailActivity.create({ self.MailActivity.create(
'summary': 'Activity', {
'activity_type_id': activity_type.id, "summary": "Activity",
'res_model_id': self.model_res_partner.id, "activity_type_id": activity_type.id,
'res_id': self.partner_DecoAddict.id, "res_model_id": self.model_res_partner.id,
'date_deadline': self.today + relativedelta(days=1), "res_id": self.partner_DecoAddict.id,
}) "date_deadline": self.today + relativedelta(days=1),
}
)
with freeze_time(self.now + relativedelta(days=2)): with freeze_time(self.now + relativedelta(days=2)):
activities = self.MailActivity._get_activities_to_remind() activities = self.MailActivity._get_activities_to_remind()