feat(reminders): include project in reminder notification

This commit is contained in:
kolaente 2023-10-20 13:56:14 +02:00
parent b2b4b5423f
commit 66cf7ab50a
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
4 changed files with 53 additions and 17 deletions

View File

@ -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().

View File

@ -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))

View File

@ -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],
}
}
}

View File

@ -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],
})
}
}