From 66cf7ab50aeb22ed8b8279aeeb1f00826cd6de50 Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 20 Oct 2023 13:56:14 +0200 Subject: [PATCH] feat(reminders): include project in reminder notification --- pkg/models/notifications.go | 25 ++++++++++++++----------- pkg/models/project.go | 12 ++++++++++++ pkg/models/task_overdue_reminder.go | 23 +++++++++++++++++++---- pkg/models/task_reminder.go | 10 ++++++++-- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/pkg/models/notifications.go b/pkg/models/notifications.go index ad0e4bd5a31..32ed8d6d54b 100644 --- a/pkg/models/notifications.go +++ b/pkg/models/notifications.go @@ -32,17 +32,18 @@ import ( // ReminderDueNotification represents a ReminderDueNotification notification type ReminderDueNotification struct { - User *user.User `json:"user"` - Task *Task `json:"task"` + User *user.User `json:"user"` + Task *Task `json:"task"` + Project *Project `json:"project"` } // ToMail returns the mail notification for ReminderDueNotification func (n *ReminderDueNotification) ToMail() *notifications.Mail { return notifications.NewMail(). To(n.User.Email). - Subject(`Reminder for "`+n.Task.Title+`"`). + Subject(`Reminder for "`+n.Task.Title+`" (`+n.Project.Title+`)`). Greeting("Hi "+n.User.GetName()+","). - Line(`This is a friendly reminder of the task "`+n.Task.Title+`".`). + Line(`This is a friendly reminder of the task "`+n.Task.Title+`" (`+n.Project.Title+`).`). Action("Open Task", config.ServiceFrontendurl.GetString()+"tasks/"+strconv.FormatInt(n.Task.ID, 10)). Line("Have a nice day!") } @@ -203,17 +204,18 @@ func (n *TeamMemberAddedNotification) Name() string { // UndoneTaskOverdueNotification represents a UndoneTaskOverdueNotification notification type UndoneTaskOverdueNotification struct { - User *user.User - Task *Task + User *user.User + Task *Task + Project *Project } // ToMail returns the mail notification for UndoneTaskOverdueNotification func (n *UndoneTaskOverdueNotification) ToMail() *notifications.Mail { until := time.Until(n.Task.DueDate).Round(1*time.Hour) * -1 return notifications.NewMail(). - Subject(`Task "`+n.Task.Title+`" is overdue`). + Subject(`Task "`+n.Task.Title+`" (`+n.Project.Title+`) is overdue`). Greeting("Hi "+n.User.GetName()+","). - Line(`This is a friendly reminder of the task "`+n.Task.Title+`" which is overdue since `+utils.HumanizeDuration(until)+` and not yet done.`). + Line(`This is a friendly reminder of the task "`+n.Task.Title+`" (`+n.Project.Title+`) which is overdue since `+utils.HumanizeDuration(until)+` and not yet done.`). Action("Open Task", config.ServiceFrontendurl.GetString()+"tasks/"+strconv.FormatInt(n.Task.ID, 10)). Line("Have a nice day!") } @@ -230,8 +232,9 @@ func (n *UndoneTaskOverdueNotification) Name() string { // UndoneTasksOverdueNotification represents a UndoneTasksOverdueNotification notification type UndoneTasksOverdueNotification struct { - User *user.User - Tasks map[int64]*Task + User *user.User + Tasks map[int64]*Task + Projects map[int64]*Project } // ToMail returns the mail notification for UndoneTasksOverdueNotification @@ -249,7 +252,7 @@ func (n *UndoneTasksOverdueNotification) ToMail() *notifications.Mail { overdueLine := "" for _, task := range sortedTasks { until := time.Until(task.DueDate).Round(1*time.Hour) * -1 - overdueLine += `* [` + task.Title + `](` + config.ServiceFrontendurl.GetString() + "tasks/" + strconv.FormatInt(task.ID, 10) + `), overdue since ` + utils.HumanizeDuration(until) + "\n" + overdueLine += `* [` + task.Title + `](` + config.ServiceFrontendurl.GetString() + "tasks/" + strconv.FormatInt(task.ID, 10) + `) (` + n.Projects[task.ProjectID].Title + `), overdue since ` + utils.HumanizeDuration(until) + "\n" } return notifications.NewMail(). diff --git a/pkg/models/project.go b/pkg/models/project.go index ec46b0f4db8..3dfeb98540e 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -314,6 +314,18 @@ func GetProjectSimplByTaskID(s *xorm.Session, taskID int64) (l *Project, err err return &project, nil } +// GetProjectsSimplByTaskIDs gets a list of projects by a task ids +func GetProjectsSimplByTaskIDs(s *xorm.Session, taskIDs []int64) (ps map[int64]*Project, err error) { + ps = make(map[int64]*Project) + err = s. + Select("projects.*"). + Table(Project{}). + Join("INNER", "tasks", "projects.id = tasks.project_id"). + In("tasks.id", taskIDs). + Find(&ps) + return +} + // GetProjectsByIDs returns a map of projects from a slice with project ids func GetProjectsByIDs(s *xorm.Session, projectIDs []int64) (projects map[int64]*Project, err error) { projects = make(map[int64]*Project, len(projectIDs)) diff --git a/pkg/models/task_overdue_reminder.go b/pkg/models/task_overdue_reminder.go index 887b3ead9f7..d23a4ec45ac 100644 --- a/pkg/models/task_overdue_reminder.go +++ b/pkg/models/task_overdue_reminder.go @@ -132,10 +132,24 @@ func RegisterOverdueReminderCron() { log.Debugf("[Undone Overdue Tasks Reminder] Sending reminders to %d users", len(uts)) + taskIDs := []int64{} + for _, ut := range uts { + for _, t := range ut.tasks { + taskIDs = append(taskIDs, t.ID) + } + } + + projects, err := GetProjectsSimplByTaskIDs(s, taskIDs) + if err != nil { + log.Errorf("[Undone Overdue Tasks Reminder] Could not get projects for tasks: %s", err) + return + } + for _, ut := range uts { var n notifications.Notification = &UndoneTasksOverdueNotification{ - User: ut.user, - Tasks: ut.tasks, + User: ut.user, + Tasks: ut.tasks, + Projects: projects, } if len(ut.tasks) == 1 { @@ -143,8 +157,9 @@ func RegisterOverdueReminderCron() { // first entry without knowing the key of it. for _, t := range ut.tasks { n = &UndoneTaskOverdueNotification{ - User: ut.user, - Task: t, + User: ut.user, + Task: t, + Project: projects[t.ProjectID], } } } diff --git a/pkg/models/task_reminder.go b/pkg/models/task_reminder.go index 75a319323d6..46225bb43a4 100644 --- a/pkg/models/task_reminder.go +++ b/pkg/models/task_reminder.go @@ -173,6 +173,11 @@ func getTasksWithRemindersDueAndTheirUsers(s *xorm.Session, now time.Time) (remi seen := make(map[int64]map[int64]bool) + projects, err := GetProjectsSimplByTaskIDs(s, taskIDs) + if err != nil { + return + } + // Time zone cache per time zone string to avoid parsing the same time zone over and over again tzs := make(map[string]*time.Location) // Figure out which reminders are actually due in the time zone of the users @@ -208,8 +213,9 @@ func getTasksWithRemindersDueAndTheirUsers(s *xorm.Session, now time.Time) (remi actualReminder := r.Reminder.In(tz) if (actualReminder.After(now) && actualReminder.Before(now.Add(time.Minute))) || actualReminder.Equal(now) { reminderNotifications = append(reminderNotifications, &ReminderDueNotification{ - User: u.User, - Task: u.Task, + User: u.User, + Task: u.Task, + Project: projects[u.Task.ProjectID], }) } }