social/mail_activity_reminder/models/mail_activity.py

129 lines
4.6 KiB
Python

# Copyright 2020 Brainbean Apps (https://brainbeanapps.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import datetime, time
from dateutil.relativedelta import relativedelta
from pytz import UTC, timezone
from odoo import _, api, fields, models
class MailActivity(models.Model):
_inherit = "mail.activity"
next_reminder = fields.Datetime(
string="Next reminder",
compute="_compute_next_reminder",
compute_sudo=True,
store=True,
)
last_reminder_local = fields.Datetime(string="Last reminder (local)",)
deadline = fields.Datetime(
string="Deadline", compute="_compute_deadline", compute_sudo=True, store=True,
)
@api.model
def _get_activities_to_remind_domain(self):
"""Hook for extensions"""
return [
("next_reminder", "<=", fields.Datetime.now()),
("deadline", ">=", fields.Datetime.now()),
]
@api.model
def _get_activities_to_remind(self):
return self.search(self._get_activities_to_remind_domain())
@api.model
def _process_reminders(self):
activities = self._get_activities_to_remind()
activities.action_remind()
return activities
@api.depends(
"user_id.tz", "activity_type_id.reminders", "deadline", "last_reminder_local",
)
def _compute_next_reminder(self):
now = fields.Datetime.now()
for activity in self:
if activity.deadline < now:
activity.next_reminder = None
continue
reminders = activity.activity_type_id._get_reminder_offsets()
if not reminders:
activity.next_reminder = None
continue
reminders.sort(reverse=True)
tz = timezone(activity.user_id.sudo().tz or "UTC")
last_reminder_local = (
tz.localize(activity.last_reminder_local)
if activity.last_reminder_local
else None
)
local_deadline = tz.localize(
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
if last_reminder_local and next_reminder_local <= last_reminder_local:
activity.next_reminder = None
continue
activity.next_reminder = next_reminder_local.astimezone(UTC).replace(
tzinfo=None
)
@api.depends("user_id.tz", "date_deadline")
def _compute_deadline(self):
for activity in self:
tz = timezone(activity.user_id.sudo().tz or "UTC")
activity.deadline = (
tz.localize(datetime.combine(activity.date_deadline, time.max))
.astimezone(UTC)
.replace(tzinfo=None)
)
def action_notify(self):
super().action_notify()
utc_now = fields.Datetime.now().replace(tzinfo=UTC)
for activity in self:
if activity.last_reminder_local:
continue
tz = timezone(activity.user_id.sudo().tz or "UTC")
activity.last_reminder_local = utc_now.astimezone(tz).replace(tzinfo=None)
def action_remind(self):
"""
Group reminders by user and type and send them together
"""
MailThread = self.env["mail.thread"]
message_activity_assigned = self.env.ref(
"mail_activity_reminder.message_activity_assigned"
)
utc_now = fields.Datetime.now().replace(tzinfo=UTC)
for user in self.mapped("user_id"):
activities = self.filtered(lambda activity: activity.user_id == user)
tz = timezone(user.sudo().tz or "UTC")
local_now = utc_now.astimezone(tz)
subject = _("Some activities you are assigned too expire soon.")
body = message_activity_assigned.render(
dict(activities=activities, model_description="Activities"),
engine="ir.qweb",
minimal_qcontext=True,
)
MailThread.message_notify(
partner_ids=user.partner_id.ids,
body=body,
subject=subject,
model_description="Activity",
notif_layout="mail.mail_notification_light",
)
activities.update({"last_reminder_local": local_now.replace(tzinfo=None)})