mirror of
https://github.com/plausible/analytics.git
synced 2024-12-23 09:33:19 +03:00
More robust scheduling for email reports (#189)
* More robust scheduling for email reports * Update config for new email report jobs * Only build docker on master * Use Postgres 12 in travis * Cache elixir files * use Postgres 12 in travis * Specify env on one line * Wrong postgres version
This commit is contained in:
parent
9465ff4c6c
commit
80b98762f4
18
.travis.yml
18
.travis.yml
@ -8,10 +8,24 @@ before_install:
|
|||||||
- echo "CREATE DATABASE plausible_test" > $HOME/init.sql
|
- echo "CREATE DATABASE plausible_test" > $HOME/init.sql
|
||||||
- docker pull yandex/clickhouse-server
|
- docker pull yandex/clickhouse-server
|
||||||
- docker run -d -p 8123:8123 --ulimit nofile=262144:262144 --volume=$HOME/init.sql:/docker-entrypoint-initdb.d/init.sql yandex/clickhouse-server
|
- docker run -d -p 8123:8123 --ulimit nofile=262144:262144 --volume=$HOME/init.sql:/docker-entrypoint-initdb.d/init.sql yandex/clickhouse-server
|
||||||
- docker build -t plausible/analytics .
|
# setting up postgres 12 is quite a pain, see:
|
||||||
|
# https://travis-ci.community/t/test-against-postgres-12/6768/8
|
||||||
|
- sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/12/main/postgresql.conf
|
||||||
|
- sudo cp /etc/postgresql/{9.4,12}/main/pg_hba.conf
|
||||||
|
- sudo pg_ctlcluster 12 main restart
|
||||||
env:
|
env:
|
||||||
- MIX_ENV=test
|
- MIX_ENV=test PGVER=12
|
||||||
script: mix coveralls.travis
|
script: mix coveralls.travis
|
||||||
|
addons:
|
||||||
|
postgresql: '12'
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- postgresql-12
|
||||||
|
- postgresql-client-12
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- _build
|
||||||
|
- deps
|
||||||
deploy:
|
deploy:
|
||||||
provider: script
|
provider: script
|
||||||
script: bash docker_push
|
script: bash docker_push
|
||||||
|
@ -91,7 +91,7 @@ crontab = [
|
|||||||
# hourly
|
# hourly
|
||||||
{"0 * * * *", Plausible.Workers.SendSiteSetupEmails},
|
{"0 * * * *", Plausible.Workers.SendSiteSetupEmails},
|
||||||
# hourly
|
# hourly
|
||||||
{"0 * * * *", Plausible.Workers.SendEmailReports},
|
{"0 * * * *", Plausible.Workers.ScheduleEmailReports},
|
||||||
# Daily at midnight
|
# Daily at midnight
|
||||||
{"0 0 * * *", Plausible.Workers.FetchTweets},
|
{"0 0 * * *", Plausible.Workers.FetchTweets},
|
||||||
# Daily at midday
|
# Daily at midday
|
||||||
@ -106,9 +106,10 @@ queues = [
|
|||||||
provision_ssl_certificates: 1,
|
provision_ssl_certificates: 1,
|
||||||
fetch_tweets: 1,
|
fetch_tweets: 1,
|
||||||
check_stats_emails: 1,
|
check_stats_emails: 1,
|
||||||
email_reports: 1,
|
|
||||||
site_setup_emails: 1,
|
site_setup_emails: 1,
|
||||||
trial_notification_emails: 1
|
trial_notification_emails: 1,
|
||||||
|
schedule_email_reports: 1,
|
||||||
|
send_email_reports: 1
|
||||||
]
|
]
|
||||||
|
|
||||||
config :plausible, Oban,
|
config :plausible, Oban,
|
||||||
|
@ -141,7 +141,7 @@ crontab = [
|
|||||||
# hourly
|
# hourly
|
||||||
{"0 * * * *", Plausible.Workers.SendSiteSetupEmails},
|
{"0 * * * *", Plausible.Workers.SendSiteSetupEmails},
|
||||||
# hourly
|
# hourly
|
||||||
{"0 * * * *", Plausible.Workers.SendEmailReports},
|
{"0 * * * *", Plausible.Workers.ScheduleEmailReports},
|
||||||
# Daily at midnight
|
# Daily at midnight
|
||||||
{"0 0 * * *", Plausible.Workers.FetchTweets},
|
{"0 0 * * *", Plausible.Workers.FetchTweets},
|
||||||
# Daily at midday
|
# Daily at midday
|
||||||
@ -156,9 +156,10 @@ queues = [
|
|||||||
provision_ssl_certificates: 1,
|
provision_ssl_certificates: 1,
|
||||||
fetch_tweets: 1,
|
fetch_tweets: 1,
|
||||||
check_stats_emails: 1,
|
check_stats_emails: 1,
|
||||||
email_reports: 1,
|
|
||||||
site_setup_emails: 1,
|
site_setup_emails: 1,
|
||||||
trial_notification_emails: 1
|
trial_notification_emails: 1,
|
||||||
|
schedule_email_reports: 1,
|
||||||
|
send_email_reports: 1
|
||||||
]
|
]
|
||||||
|
|
||||||
config :plausible, Oban,
|
config :plausible, Oban,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||||
|
docker build -t plausible/analytics .
|
||||||
docker push plausible/analytics
|
docker push plausible/analytics
|
||||||
|
@ -115,7 +115,7 @@ defmodule PlausibleWeb.Email do
|
|||||||
def weekly_report(email, site, assigns) do
|
def weekly_report(email, site, assigns) do
|
||||||
base_email()
|
base_email()
|
||||||
|> to(email)
|
|> to(email)
|
||||||
|> from("Plausible Insights <info@plausible.io>")
|
|> from("Plausible Analytics <info@plausible.io>")
|
||||||
|> tag("weekly-report")
|
|> tag("weekly-report")
|
||||||
|> subject("#{assigns[:name]} report for #{site.domain}")
|
|> subject("#{assigns[:name]} report for #{site.domain}")
|
||||||
|> render("weekly_report.html", Keyword.put(assigns, :site, site))
|
|> render("weekly_report.html", Keyword.put(assigns, :site, site))
|
||||||
|
@ -10,7 +10,7 @@ We will keep recording stats for another month to give you time to upgrade.
|
|||||||
<br /><br />
|
<br /><br />
|
||||||
Thanks,<br />
|
Thanks,<br />
|
||||||
Uku Taht<br />
|
Uku Taht<br />
|
||||||
Founder, Plausible Insights
|
Founder, Plausible Analytics
|
||||||
<br /><br />
|
<br /><br />
|
||||||
--
|
--
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<%= render @view_module, @view_template, assigns %>
|
<%= render @view_module, @view_template, assigns %>
|
||||||
|
|
||||||
<p class="text-center text-gray-500 text-xs py-8">
|
<p class="text-center text-gray-500 text-xs py-8">
|
||||||
©2020 Plausible Insights. All rights reserved.
|
©2020 Plausible Analytics. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
|
<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
|
||||||
|
86
lib/workers/schedule_email_reports.ex
Normal file
86
lib/workers/schedule_email_reports.ex
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
defmodule Plausible.Workers.ScheduleEmailReports do
|
||||||
|
use Plausible.Repo
|
||||||
|
use Oban.Worker, queue: :schedule_email_reports
|
||||||
|
alias Plausible.Workers.SendEmailReport
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@impl Oban.Worker
|
||||||
|
@doc """
|
||||||
|
Email reports should be sent on Monday at 9am according to the timezone
|
||||||
|
of a site. This job runs every day at midnight to ensure that all sites
|
||||||
|
have a scheduled job for email reports.
|
||||||
|
"""
|
||||||
|
def perform(_args, _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,
|
||||||
|
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),
|
||||||
|
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
|
||||||
|
Timex.now(timezone)
|
||||||
|
|> Timex.shift(weeks: 1)
|
||||||
|
|> Timex.beginning_of_week()
|
||||||
|
|> Timex.shift(hours: 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,
|
||||||
|
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),
|
||||||
|
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
|
||||||
|
Timex.now(timezone)
|
||||||
|
|> Timex.shift(months: 1)
|
||||||
|
|> Timex.beginning_of_month()
|
||||||
|
|> Timex.shift(hours: 9)
|
||||||
|
end
|
||||||
|
end
|
81
lib/workers/send_email_report.ex
Normal file
81
lib/workers/send_email_report.ex
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
defmodule Plausible.Workers.SendEmailReport do
|
||||||
|
use Plausible.Repo
|
||||||
|
use Oban.Worker, queue: :send_email_reports, max_attempts: 1
|
||||||
|
alias Plausible.Stats.Query
|
||||||
|
alias Plausible.Stats.Clickhouse, as: Stats
|
||||||
|
|
||||||
|
@impl Oban.Worker
|
||||||
|
def perform(%{"interval" => "weekly", "site_id" => site_id}, _job) do
|
||||||
|
site = Repo.get(Plausible.Site, site_id) |> Repo.preload(:weekly_report)
|
||||||
|
query = Query.from(site.timezone, %{"period" => "7d"})
|
||||||
|
|
||||||
|
for email <- site.weekly_report.recipients do
|
||||||
|
unsubscribe_link =
|
||||||
|
PlausibleWeb.Endpoint.url() <>
|
||||||
|
"/sites/#{URI.encode_www_form(site.domain)}/weekly-report/unsubscribe?email=#{email}"
|
||||||
|
|
||||||
|
send_report(email, site, "Weekly", unsubscribe_link, query)
|
||||||
|
end
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Oban.Worker
|
||||||
|
def perform(%{"interval" => "monthly", "site_id" => site_id}, _job) do
|
||||||
|
site = Repo.get(Plausible.Site, site_id) |> Repo.preload(:monthly_report)
|
||||||
|
last_month =
|
||||||
|
Timex.now(site.timezone)
|
||||||
|
|> Timex.shift(months: -1)
|
||||||
|
|> Timex.beginning_of_month()
|
||||||
|
|
||||||
|
query =
|
||||||
|
Query.from(site.timezone, %{
|
||||||
|
"period" => "month",
|
||||||
|
"date" => Timex.format!(last_month, "{ISOdate}")
|
||||||
|
})
|
||||||
|
|
||||||
|
for email <- site.monthly_report.recipients do
|
||||||
|
unsubscribe_link =
|
||||||
|
PlausibleWeb.Endpoint.url() <>
|
||||||
|
"/sites/#{URI.encode_www_form(site.domain)}/monthly-report/unsubscribe?email=#{email}"
|
||||||
|
|
||||||
|
send_report(email, site, Timex.format!(last_month, "{Mfull}"), unsubscribe_link, query)
|
||||||
|
end
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
defp send_report(email, site, name, unsubscribe_link, query) do
|
||||||
|
{pageviews, unique_visitors} = Stats.pageviews_and_visitors(site, query)
|
||||||
|
|
||||||
|
{change_pageviews, change_visitors} =
|
||||||
|
Stats.compare_pageviews_and_visitors(site, query, {pageviews, unique_visitors})
|
||||||
|
|
||||||
|
bounce_rate = Stats.bounce_rate(site, query)
|
||||||
|
prev_bounce_rate = Stats.bounce_rate(site, Query.shift_back(query))
|
||||||
|
change_bounce_rate = if prev_bounce_rate > 0, do: bounce_rate - prev_bounce_rate
|
||||||
|
referrers = Stats.top_referrers(site, query)
|
||||||
|
pages = Stats.top_pages(site, query)
|
||||||
|
user = Plausible.Auth.find_user_by(email: email)
|
||||||
|
login_link = user && Plausible.Sites.is_owner?(user.id, site)
|
||||||
|
|
||||||
|
template = PlausibleWeb.Email.weekly_report(email, site,
|
||||||
|
unique_visitors: unique_visitors,
|
||||||
|
change_visitors: change_visitors,
|
||||||
|
pageviews: pageviews,
|
||||||
|
change_pageviews: change_pageviews,
|
||||||
|
bounce_rate: bounce_rate,
|
||||||
|
change_bounce_rate: change_bounce_rate,
|
||||||
|
referrers: referrers,
|
||||||
|
unsubscribe_link: unsubscribe_link,
|
||||||
|
login_link: login_link,
|
||||||
|
pages: pages,
|
||||||
|
query: query,
|
||||||
|
name: name
|
||||||
|
)
|
||||||
|
|
||||||
|
try do
|
||||||
|
Plausible.Mailer.send_email(template)
|
||||||
|
rescue
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,173 +0,0 @@
|
|||||||
defmodule Plausible.Workers.SendEmailReports do
|
|
||||||
use Plausible.Repo
|
|
||||||
use Oban.Worker, queue: :email_reports
|
|
||||||
require Logger
|
|
||||||
alias Plausible.Stats.Query
|
|
||||||
alias Plausible.Stats.Clickhouse, as: Stats
|
|
||||||
|
|
||||||
@impl Oban.Worker
|
|
||||||
@doc """
|
|
||||||
The email report should be sent on Monday at 9am according to the timezone
|
|
||||||
of the site. This job runs every hour to be able to send it with hourly precision.
|
|
||||||
"""
|
|
||||||
def perform(args, _job) do
|
|
||||||
current_time =
|
|
||||||
if args["current_time"],
|
|
||||||
do: Timex.parse!(args["current_time"], "{ISO:Extended}"),
|
|
||||||
else: Timex.now()
|
|
||||||
|
|
||||||
send_weekly_emails(current_time)
|
|
||||||
send_monthly_emails(current_time)
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
defp send_weekly_emails(job_start) do
|
|
||||||
sites =
|
|
||||||
Repo.all(
|
|
||||||
from s in Plausible.Site,
|
|
||||||
join: wr in Plausible.Site.WeeklyReport,
|
|
||||||
on: wr.site_id == s.id,
|
|
||||||
left_join: se in "sent_weekly_reports",
|
|
||||||
on:
|
|
||||||
se.site_id == s.id and
|
|
||||||
se.year ==
|
|
||||||
fragment("EXTRACT(isoyear from (? at time zone ?))", ^job_start, s.timezone) and
|
|
||||||
se.week == fragment("EXTRACT(week from (? at time zone ?))", ^job_start, s.timezone),
|
|
||||||
# We haven't sent a report for this site on this week
|
|
||||||
where: is_nil(se),
|
|
||||||
# It's monday in the local timezone
|
|
||||||
where: fragment("EXTRACT(dow from (? at time zone ?))", ^job_start, s.timezone) == 1,
|
|
||||||
# It's after 9am
|
|
||||||
where: fragment("EXTRACT(hour from (? at time zone ?))", ^job_start, s.timezone) >= 9,
|
|
||||||
preload: [weekly_report: wr]
|
|
||||||
)
|
|
||||||
|
|
||||||
for site <- sites do
|
|
||||||
query = Query.from(site.timezone, %{"period" => "7d"})
|
|
||||||
|
|
||||||
sent = Enum.map(site.weekly_report.recipients, fn email ->
|
|
||||||
Logger.info("Sending weekly report for #{URI.encode_www_form(site.domain)} to #{email}")
|
|
||||||
|
|
||||||
unsubscribe_link =
|
|
||||||
PlausibleWeb.Endpoint.url() <>
|
|
||||||
"/sites/#{URI.encode_www_form(site.domain)}/weekly-report/unsubscribe?email=#{email}"
|
|
||||||
|
|
||||||
send_report(email, site, "Weekly", unsubscribe_link, query)
|
|
||||||
end)
|
|
||||||
|
|
||||||
if Enum.all?(sent), do: weekly_report_sent(site, job_start)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp send_monthly_emails(job_start) do
|
|
||||||
sites =
|
|
||||||
Repo.all(
|
|
||||||
from s in Plausible.Site,
|
|
||||||
join: mr in Plausible.Site.MonthlyReport,
|
|
||||||
on: mr.site_id == s.id,
|
|
||||||
left_join: se in "sent_monthly_reports",
|
|
||||||
on:
|
|
||||||
se.site_id == s.id and
|
|
||||||
se.year == fragment("EXTRACT(year from (? at time zone ?))", ^job_start, s.timezone) and
|
|
||||||
se.month ==
|
|
||||||
fragment("EXTRACT(month from (? at time zone ?))", ^job_start, s.timezone),
|
|
||||||
# We haven't sent a report for this site this month
|
|
||||||
where: is_nil(se),
|
|
||||||
# It's the 1st of the month in the local timezone
|
|
||||||
where: fragment("EXTRACT(day from (? at time zone ?))", ^job_start, s.timezone) == 1,
|
|
||||||
# It's after 9am
|
|
||||||
where: fragment("EXTRACT(hour from (? at time zone ?))", ^job_start, s.timezone) >= 9,
|
|
||||||
preload: [monthly_report: mr]
|
|
||||||
)
|
|
||||||
|
|
||||||
for site <- sites do
|
|
||||||
last_month =
|
|
||||||
job_start
|
|
||||||
|> Timex.Timezone.convert(site.timezone)
|
|
||||||
|> Timex.shift(months: -1)
|
|
||||||
|> Timex.beginning_of_month()
|
|
||||||
|
|
||||||
query =
|
|
||||||
Query.from(site.timezone, %{
|
|
||||||
"period" => "month",
|
|
||||||
"date" => Timex.format!(last_month, "{ISOdate}")
|
|
||||||
})
|
|
||||||
|
|
||||||
sent = Enum.map(site.monthly_report.recipients, fn email ->
|
|
||||||
Logger.info("Sending monthly report for #{site.domain} to #{email}")
|
|
||||||
|
|
||||||
unsubscribe_link =
|
|
||||||
PlausibleWeb.Endpoint.url() <>
|
|
||||||
"/sites/#{URI.encode_www_form(site.domain)}/monthly-report/unsubscribe?email=#{email}"
|
|
||||||
|
|
||||||
send_report(email, site, Timex.format!(last_month, "{Mfull}"), unsubscribe_link, query)
|
|
||||||
end)
|
|
||||||
|
|
||||||
if Enum.all?(sent), do: monthly_report_sent(site, job_start)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp send_report(email, site, name, unsubscribe_link, query) do
|
|
||||||
{pageviews, unique_visitors} = Stats.pageviews_and_visitors(site, query)
|
|
||||||
|
|
||||||
{change_pageviews, change_visitors} =
|
|
||||||
Stats.compare_pageviews_and_visitors(site, query, {pageviews, unique_visitors})
|
|
||||||
|
|
||||||
bounce_rate = Stats.bounce_rate(site, query)
|
|
||||||
prev_bounce_rate = Stats.bounce_rate(site, Query.shift_back(query))
|
|
||||||
change_bounce_rate = if prev_bounce_rate > 0, do: bounce_rate - prev_bounce_rate
|
|
||||||
referrers = Stats.top_referrers(site, query)
|
|
||||||
pages = Stats.top_pages(site, query)
|
|
||||||
user = Plausible.Auth.find_user_by(email: email)
|
|
||||||
login_link = user && Plausible.Sites.is_owner?(user.id, site)
|
|
||||||
|
|
||||||
template = PlausibleWeb.Email.weekly_report(email, site,
|
|
||||||
unique_visitors: unique_visitors,
|
|
||||||
change_visitors: change_visitors,
|
|
||||||
pageviews: pageviews,
|
|
||||||
change_pageviews: change_pageviews,
|
|
||||||
bounce_rate: bounce_rate,
|
|
||||||
change_bounce_rate: change_bounce_rate,
|
|
||||||
referrers: referrers,
|
|
||||||
unsubscribe_link: unsubscribe_link,
|
|
||||||
login_link: login_link,
|
|
||||||
pages: pages,
|
|
||||||
query: query,
|
|
||||||
name: name
|
|
||||||
)
|
|
||||||
|
|
||||||
try do
|
|
||||||
Plausible.Mailer.send_email(template)
|
|
||||||
true
|
|
||||||
rescue
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp weekly_report_sent(site, time) do
|
|
||||||
{year, week} = time |> DateTime.to_date() |> Timex.iso_week()
|
|
||||||
|
|
||||||
Repo.insert_all("sent_weekly_reports", [
|
|
||||||
%{
|
|
||||||
site_id: site.id,
|
|
||||||
year: year,
|
|
||||||
week: week,
|
|
||||||
timestamp: Timex.now()
|
|
||||||
}
|
|
||||||
])
|
|
||||||
end
|
|
||||||
|
|
||||||
defp monthly_report_sent(site, time) do
|
|
||||||
date = DateTime.to_date(time)
|
|
||||||
|
|
||||||
Repo.insert_all("sent_monthly_reports", [
|
|
||||||
%{
|
|
||||||
site_id: site.id,
|
|
||||||
year: date.year,
|
|
||||||
month: date.month,
|
|
||||||
timestamp: Timex.now()
|
|
||||||
}
|
|
||||||
])
|
|
||||||
end
|
|
||||||
end
|
|
76
test/workers/schedule_email_reports_test.exs
Normal file
76
test/workers/schedule_email_reports_test.exs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
defmodule Plausible.Workers.ScheduleEmailReportsTest do
|
||||||
|
use Plausible.DataCase
|
||||||
|
use Oban.Testing, repo: Plausible.Repo
|
||||||
|
alias Plausible.Workers.{ScheduleEmailReports, SendEmailReport}
|
||||||
|
|
||||||
|
defp perform(args) do
|
||||||
|
ScheduleEmailReports.new(args) |> Oban.insert!()
|
||||||
|
Oban.drain_queue(:schedule_email_reports)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "weekly reports" do
|
||||||
|
test "schedules weekly report on Monday 9am local timezone" do
|
||||||
|
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
|
||||||
|
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
||||||
|
|
||||||
|
perform(%{})
|
||||||
|
|
||||||
|
assert_enqueued worker: SendEmailReport, args: %{site_id: site.id, interval: "weekly"}, scheduled_at: ScheduleEmailReports.monday_9am(site.timezone)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not schedule more than one weekly report at a time" do
|
||||||
|
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
|
||||||
|
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
||||||
|
|
||||||
|
perform(%{})
|
||||||
|
perform(%{})
|
||||||
|
|
||||||
|
assert Enum.count(all_enqueued(worker: SendEmailReport)) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "schedules a new report as soon as a previous one is completed" do
|
||||||
|
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
|
||||||
|
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
||||||
|
|
||||||
|
perform(%{})
|
||||||
|
Repo.update_all("oban_jobs", [set: [state: "completed"]])
|
||||||
|
assert Enum.count(all_enqueued(worker: SendEmailReport)) == 0
|
||||||
|
perform(%{})
|
||||||
|
assert Enum.count(all_enqueued(worker: SendEmailReport)) == 1
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "monthly_reports" do
|
||||||
|
test "schedules monthly report on first of the next month at 9am local timezone" do
|
||||||
|
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
|
||||||
|
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
||||||
|
|
||||||
|
perform(%{})
|
||||||
|
|
||||||
|
assert_enqueued worker: SendEmailReport, args: %{site_id: site.id, interval: "monthly"}, scheduled_at: ScheduleEmailReports.first_of_month_9am(site.timezone)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not schedule more than one monthly report at a time" do
|
||||||
|
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
|
||||||
|
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
||||||
|
|
||||||
|
perform(%{})
|
||||||
|
perform(%{})
|
||||||
|
|
||||||
|
assert Enum.count(all_enqueued(worker: SendEmailReport)) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "schedules a new report as soon as a previous one is completed" do
|
||||||
|
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
|
||||||
|
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
||||||
|
|
||||||
|
perform(%{})
|
||||||
|
Repo.update_all("oban_jobs", [set: [state: "completed"]])
|
||||||
|
assert Enum.count(all_enqueued(worker: SendEmailReport)) == 0
|
||||||
|
perform(%{})
|
||||||
|
assert Enum.count(all_enqueued(worker: SendEmailReport)) == 1
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
49
test/workers/send_email_report_test.exs
Normal file
49
test/workers/send_email_report_test.exs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
defmodule Plausible.Workers.SendEmailReportTest do
|
||||||
|
use Plausible.DataCase
|
||||||
|
use Bamboo.Test
|
||||||
|
alias Plausible.Workers.SendEmailReport
|
||||||
|
|
||||||
|
defp perform(args) do
|
||||||
|
SendEmailReport.new(args) |> Oban.insert!()
|
||||||
|
Oban.drain_queue(:send_email_reports)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "weekly reports" do
|
||||||
|
test "sends weekly report to all recipients" do
|
||||||
|
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
|
||||||
|
insert(:weekly_report, site: site, recipients: ["user@email.com", "user2@email.com"])
|
||||||
|
|
||||||
|
perform(%{"site_id" => site.id, "interval" => "weekly"})
|
||||||
|
|
||||||
|
assert_email_delivered_with(
|
||||||
|
subject: "Weekly report for #{site.domain}",
|
||||||
|
to: [nil: "user@email.com"]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_email_delivered_with(
|
||||||
|
subject: "Weekly report for #{site.domain}",
|
||||||
|
to: [nil: "user2@email.com"]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "monthly_reports" do
|
||||||
|
test "sends monthly report to all recipients" do
|
||||||
|
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
|
||||||
|
insert(:monthly_report, site: site, recipients: ["user@email.com", "user2@email.com"])
|
||||||
|
last_month = Timex.now(site.timezone) |> Timex.shift(months: -1) |> Timex.beginning_of_month() |> Timex.format!("{Mfull}")
|
||||||
|
|
||||||
|
perform(%{"site_id" => site.id, "interval" => "monthly"})
|
||||||
|
|
||||||
|
assert_email_delivered_with(
|
||||||
|
subject: "#{last_month} report for #{site.domain}",
|
||||||
|
to: [nil: "user@email.com"]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_email_delivered_with(
|
||||||
|
subject: "#{last_month} report for #{site.domain}",
|
||||||
|
to: [nil: "user2@email.com"]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,115 +0,0 @@
|
|||||||
defmodule Plausible.Workers.EmailReportsTest do
|
|
||||||
use Plausible.DataCase
|
|
||||||
use Bamboo.Test
|
|
||||||
alias Plausible.Workers.SendEmailReports
|
|
||||||
|
|
||||||
defp perform(args) do
|
|
||||||
SendEmailReports.new(args) |> Oban.insert!()
|
|
||||||
Oban.drain_queue(:email_reports)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "weekly reports" do
|
|
||||||
test "sends weekly report on Monday 9am local timezone" do
|
|
||||||
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
|
|
||||||
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
|
||||||
# 2pm UTC is 10am EST
|
|
||||||
time = Timex.now() |> Timex.beginning_of_week() |> Timex.shift(hours: 14)
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
|
|
||||||
assert_email_delivered_with(
|
|
||||||
subject: "Weekly report for #{site.domain}",
|
|
||||||
to: [nil: "user@email.com"]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does not send a report on Monday before 9am in local timezone" do
|
|
||||||
site = insert(:site, timezone: "US/Eastern")
|
|
||||||
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
|
||||||
# 12pm UTC is 8am EST
|
|
||||||
time = Timex.now() |> Timex.beginning_of_week() |> Timex.shift(hours: 12)
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
|
|
||||||
assert_no_emails_delivered()
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does not send a report on Tuesday" do
|
|
||||||
site = insert(:site)
|
|
||||||
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
|
||||||
time = Timex.now() |> Timex.beginning_of_week() |> Timex.shift(days: 1, hours: 10)
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
|
|
||||||
assert_no_emails_delivered()
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does not send the same report multiple times on the same week" do
|
|
||||||
site = insert(:site)
|
|
||||||
insert(:weekly_report, site: site, recipients: ["user@email.com"])
|
|
||||||
time = Timex.now() |> Timex.beginning_of_week() |> Timex.shift(hours: 10)
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
|
|
||||||
assert_email_delivered_with(
|
|
||||||
subject: "Weekly report for #{site.domain}",
|
|
||||||
to: [nil: "user@email.com"]
|
|
||||||
)
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
assert_no_emails_delivered()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "monthly_reports" do
|
|
||||||
test "sends monthly report on the 1st of the month after 9am local timezone" do
|
|
||||||
site = insert(:site, timezone: "US/Eastern")
|
|
||||||
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
|
||||||
{:ok, time, _} = DateTime.from_iso8601("2019-04-01T14:00:00Z")
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
|
|
||||||
assert_email_delivered_with(
|
|
||||||
subject: "March report for #{site.domain}",
|
|
||||||
to: [nil: "user@email.com"]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does not send a report on the 1st of the month before 9am in local timezone" do
|
|
||||||
site = insert(:site, timezone: "US/Eastern")
|
|
||||||
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
|
||||||
# 12pm UTC is 8am EST
|
|
||||||
time = Timex.now() |> Timex.beginning_of_month() |> Timex.shift(hours: 12)
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
|
|
||||||
assert_no_emails_delivered()
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does not send a report on the 2nd of the month" do
|
|
||||||
site = insert(:site)
|
|
||||||
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
|
||||||
time = Timex.now() |> Timex.beginning_of_month() |> Timex.shift(days: 1, hours: 10)
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
|
|
||||||
assert_no_emails_delivered()
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does not send the same report multiple times on the same month" do
|
|
||||||
site = insert(:site)
|
|
||||||
insert(:monthly_report, site: site, recipients: ["user@email.com"])
|
|
||||||
{:ok, time, _} = DateTime.from_iso8601("2019-02-01T11:00:00Z")
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
|
|
||||||
assert_email_delivered_with(
|
|
||||||
subject: "January report for #{site.domain}",
|
|
||||||
to: [nil: "user@email.com"]
|
|
||||||
)
|
|
||||||
|
|
||||||
perform(%{"current_time" => time})
|
|
||||||
assert_no_emails_delivered()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in New Issue
Block a user