File size: 2,683 Bytes
c27e67a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
defmodule Plausible.Workers.ScheduleEmailReports do
  @moduledoc """
  Job scheduling dispatch of weekly and monthly email reports.
  """
  use Plausible.Repo
  use Oban.Worker, queue: :schedule_email_reports
  alias Plausible.Workers.SendEmailReport

  @impl Oban.Worker
  @doc """
  Email reports should be sent on Monday at 9am according to the timezone
  of a site.
  """
  def perform(_job) do
    schedule_weekly_emails()
    schedule_monthly_emails()
  end

  defp schedule_weekly_emails() do
    weekly_jobs =
      from(
        j in Oban.Job,
        where:
          j.worker == "Plausible.Workers.SendEmailReport" and
            fragment("(? ->> 'interval')", j.args) == "weekly"
      )

    sites =
      Repo.all(
        from s in Plausible.Site,
          inner_join: t in assoc(s, :team),
          join: wr in Plausible.Site.WeeklyReport,
          on: wr.site_id == s.id,
          left_join: job in subquery(weekly_jobs),
          on:
            fragment("(? -> 'site_id')::int", job.args) == s.id and
              job.state not in ["completed", "discarded"],
          where: is_nil(job),
          where: not t.locked,
          preload: [weekly_report: wr]
      )

    for site <- sites do
      SendEmailReport.new(%{site_id: site.id, interval: "weekly"},
        scheduled_at: monday_9am(site.timezone)
      )
      |> Oban.insert!()
    end

    :ok
  end

  def monday_9am(timezone) do
    DateTime.now!(timezone)
    |> DateTime.shift(week: 1)
    |> Plausible.Times.beginning_of_week()
    |> DateTime.shift(hour: 9)
  end

  defp schedule_monthly_emails() do
    monthly_jobs =
      from(
        j in Oban.Job,
        where:
          j.worker == "Plausible.Workers.SendEmailReport" and
            fragment("(? ->> 'interval')", j.args) == "monthly"
      )

    sites =
      Repo.all(
        from s in Plausible.Site,
          inner_join: t in assoc(s, :team),
          join: mr in Plausible.Site.MonthlyReport,
          on: mr.site_id == s.id,
          left_join: job in subquery(monthly_jobs),
          on:
            fragment("(? -> 'site_id')::int", job.args) == s.id and
              job.state not in ["completed", "discarded"],
          where: is_nil(job),
          where: not t.locked,
          preload: [monthly_report: mr]
      )

    for site <- sites do
      SendEmailReport.new(%{site_id: site.id, interval: "monthly"},
        scheduled_at: first_of_month_9am(site.timezone)
      )
      |> Oban.insert!()
    end

    :ok
  end

  def first_of_month_9am(timezone) do
    DateTime.now!(timezone)
    |> DateTime.shift(month: 1)
    |> Plausible.Times.beginning_of_month()
    |> DateTime.shift(hour: 9)
  end
end