2021-04-30 15:07:14 +03:00
|
|
|
package services
|
|
|
|
|
|
|
|
import (
|
2023-07-08 20:54:15 +03:00
|
|
|
"github.com/duke-git/lancet/v2/datetime"
|
2022-11-20 00:21:51 +03:00
|
|
|
"github.com/duke-git/lancet/v2/slice"
|
2021-04-30 15:07:14 +03:00
|
|
|
"github.com/emvi/logbuch"
|
2021-04-30 17:20:08 +03:00
|
|
|
"github.com/leandro-lugaresi/hub"
|
2022-12-01 17:31:19 +03:00
|
|
|
"github.com/muety/artifex/v2"
|
2021-04-30 15:07:14 +03:00
|
|
|
"github.com/muety/wakapi/config"
|
|
|
|
"github.com/muety/wakapi/models"
|
2023-07-08 20:54:15 +03:00
|
|
|
"github.com/muety/wakapi/utils"
|
2021-05-04 22:04:11 +03:00
|
|
|
"math/rand"
|
2021-04-30 15:07:14 +03:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2022-11-20 00:21:51 +03:00
|
|
|
// delay between evey report generation task (to throttle email sending frequency)
|
2022-12-01 12:57:51 +03:00
|
|
|
const reportDelay = 10 * time.Second
|
2021-04-30 15:07:14 +03:00
|
|
|
|
2022-11-20 00:21:51 +03:00
|
|
|
// past time range to cover in the report
|
|
|
|
const reportRange = 7 * 24 * time.Hour
|
2021-05-04 22:04:11 +03:00
|
|
|
|
2021-04-30 15:07:14 +03:00
|
|
|
type ReportService struct {
|
2021-05-04 22:04:11 +03:00
|
|
|
config *config.Config
|
|
|
|
eventBus *hub.Hub
|
|
|
|
summaryService ISummaryService
|
|
|
|
userService IUserService
|
|
|
|
mailService IMailService
|
|
|
|
rand *rand.Rand
|
2022-11-20 12:10:24 +03:00
|
|
|
queueDefault *artifex.Dispatcher
|
|
|
|
queueWorkers *artifex.Dispatcher
|
2021-04-30 15:07:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewReportService(summaryService ISummaryService, userService IUserService, mailService IMailService) *ReportService {
|
2021-04-30 17:20:08 +03:00
|
|
|
srv := &ReportService{
|
2021-05-04 22:04:11 +03:00
|
|
|
config: config.Get(),
|
|
|
|
eventBus: config.EventBus(),
|
|
|
|
summaryService: summaryService,
|
|
|
|
userService: userService,
|
|
|
|
mailService: mailService,
|
|
|
|
rand: rand.New(rand.NewSource(time.Now().Unix())),
|
2022-11-20 12:10:24 +03:00
|
|
|
queueDefault: config.GetDefaultQueue(),
|
|
|
|
queueWorkers: config.GetQueue(config.QueueReports),
|
2021-04-30 15:07:14 +03:00
|
|
|
}
|
2021-04-30 17:20:08 +03:00
|
|
|
|
|
|
|
return srv
|
2021-04-30 15:07:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *ReportService) Schedule() {
|
2022-11-20 12:10:24 +03:00
|
|
|
logbuch.Info("scheduling report generation")
|
2021-04-30 15:07:14 +03:00
|
|
|
|
2022-12-01 12:57:51 +03:00
|
|
|
scheduleUserReport := func(u *models.User) {
|
|
|
|
if err := srv.queueWorkers.Dispatch(func() {
|
|
|
|
t0 := time.Now()
|
|
|
|
|
2022-11-20 12:10:24 +03:00
|
|
|
if err := srv.SendReport(u, reportRange); err != nil {
|
|
|
|
config.Log().Error("failed to generate report for '%s', %v", u.ID, err)
|
|
|
|
}
|
2022-12-01 12:57:51 +03:00
|
|
|
|
|
|
|
// make the job take at least reportDelay seconds
|
|
|
|
if diff := reportDelay - time.Now().Sub(t0); diff > 0 {
|
|
|
|
logbuch.Debug("waiting for %v before sending next report", diff)
|
|
|
|
time.Sleep(diff)
|
|
|
|
}
|
|
|
|
}); err != nil {
|
2022-11-20 12:10:24 +03:00
|
|
|
config.Log().Error("failed to dispatch report generation job for user '%s', %v", u.ID, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := srv.queueDefault.DispatchCron(func() {
|
2022-11-20 00:21:51 +03:00
|
|
|
// fetch all users with reports enabled
|
|
|
|
users, err := srv.userService.GetAllByReports(true)
|
|
|
|
if err != nil {
|
|
|
|
config.Log().Error("failed to get users for report generation, %v", err)
|
|
|
|
return
|
|
|
|
}
|
2021-04-30 15:07:14 +03:00
|
|
|
|
2022-11-20 00:21:51 +03:00
|
|
|
// filter users who have their email set
|
|
|
|
users = slice.Filter[*models.User](users, func(i int, u *models.User) bool {
|
|
|
|
return u.Email != ""
|
|
|
|
})
|
|
|
|
|
2022-11-20 12:10:24 +03:00
|
|
|
// schedule jobs, throttled by one job per x seconds
|
2022-11-20 00:21:51 +03:00
|
|
|
logbuch.Info("scheduling report generation for %d users", len(users))
|
2022-12-01 12:57:51 +03:00
|
|
|
for _, u := range users {
|
|
|
|
scheduleUserReport(u)
|
2021-05-04 22:04:11 +03:00
|
|
|
}
|
2022-11-20 00:21:51 +03:00
|
|
|
}, srv.config.App.GetWeeklyReportCron())
|
2021-04-30 17:20:08 +03:00
|
|
|
|
2022-11-20 00:21:51 +03:00
|
|
|
if err != nil {
|
|
|
|
config.Log().Error("failed to dispatch report generation jobs, %v", err)
|
|
|
|
}
|
2021-04-30 15:07:14 +03:00
|
|
|
}
|
|
|
|
|
2022-11-20 00:21:51 +03:00
|
|
|
func (srv *ReportService) SendReport(user *models.User, duration time.Duration) error {
|
2021-04-30 17:20:08 +03:00
|
|
|
if user.Email == "" {
|
|
|
|
logbuch.Warn("not generating report for '%s' as no e-mail address is set")
|
2021-12-22 20:33:15 +03:00
|
|
|
return nil
|
2021-04-30 17:20:08 +03:00
|
|
|
}
|
|
|
|
|
2022-11-20 00:21:51 +03:00
|
|
|
logbuch.Info("generating report for '%s'", user.ID)
|
2021-04-30 17:20:08 +03:00
|
|
|
|
2021-04-30 15:07:14 +03:00
|
|
|
end := time.Now().In(user.TZ())
|
|
|
|
start := time.Now().Add(-1 * duration)
|
|
|
|
|
2023-07-08 20:54:15 +03:00
|
|
|
fullSummary, err := srv.summaryService.Aliased(start, end, user, srv.summaryService.Retrieve, nil, false)
|
2021-04-30 15:07:14 +03:00
|
|
|
if err != nil {
|
2022-02-17 14:20:22 +03:00
|
|
|
config.Log().Error("failed to generate report for '%s' - %v", user.ID, err)
|
2021-04-30 15:07:14 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-07-08 20:54:15 +03:00
|
|
|
// generate per-day summaries
|
|
|
|
dayIntervals := utils.SplitRangeByDays(start, end)
|
|
|
|
dailySummaries := make([]*models.Summary, len(dayIntervals))
|
|
|
|
|
|
|
|
for i, interval := range dayIntervals {
|
|
|
|
from, to := datetime.BeginOfDay(interval[0]), interval[1]
|
|
|
|
summary, err := srv.summaryService.Aliased(from, to, user, srv.summaryService.Retrieve, nil, false)
|
|
|
|
if err != nil {
|
|
|
|
config.Log().Error("failed to generate day summary (%v to %v) for report for '%s' - %v", from, to, user.ID, err)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
summary.FromTime = models.CustomTime(from)
|
|
|
|
summary.ToTime = models.CustomTime(to.Add(-1 * time.Second))
|
|
|
|
dailySummaries[i] = summary
|
|
|
|
}
|
|
|
|
|
2021-04-30 15:07:14 +03:00
|
|
|
report := &models.Report{
|
2023-07-08 20:54:15 +03:00
|
|
|
From: start,
|
|
|
|
To: end,
|
|
|
|
User: user,
|
|
|
|
Summary: fullSummary,
|
|
|
|
DailySummaries: dailySummaries,
|
2021-04-30 15:07:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := srv.mailService.SendReport(user, report); err != nil {
|
2022-11-20 00:21:51 +03:00
|
|
|
config.Log().Error("failed to send report for '%s', %v", user.ID, err)
|
2021-04-30 15:07:14 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
logbuch.Info("sent report to user '%s'", user.ID)
|
|
|
|
return nil
|
|
|
|
}
|