2019-09-05 19:11:07 +03:00
defmodule Mix.Tasks.SendEmailReports do
use Mix.Task
use Plausible.Repo
require Logger
2020-01-22 12:16:53 +03:00
alias Plausible.Stats
2019-09-05 19:11:07 +03:00
2020-01-22 12:16:53 +03:00
def run ( _args ) do
2019-09-05 19:11:07 +03:00
Application . ensure_all_started ( :plausible )
2020-01-22 12:16:53 +03:00
execute ( Timex . now ( ) )
2019-09-05 19:11:07 +03:00
end
@doc """
The email report should be sent on Monday at 9 am according to the timezone
of the site . This job runs every hour to be able to send it with hourly precision .
"""
2020-01-22 12:16:53 +03:00
def execute ( job_start ) do
send_weekly_emails ( job_start )
send_monthly_emails ( job_start )
2019-09-11 18:35:30 +03:00
end
2020-01-22 12:16:53 +03:00
defp send_weekly_emails ( job_start ) do
2019-09-05 19:11:07 +03:00
sites = Repo . all (
from s in Plausible.Site ,
2019-09-09 14:19:21 +03:00
join : wr in Plausible.Site.WeeklyReport , on : wr . site_id == s . id ,
2020-01-22 12:16:53 +03:00
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 ) ,
2019-09-05 19:11:07 +03:00
where : is_nil ( se ) , # We haven't sent a report for this site on this week
2020-01-22 12:16:53 +03:00
where : fragment ( " EXTRACT(dow from (? at time zone ?)) " , ^ job_start , s . timezone ) == 1 , # It's monday in the local timezone
where : fragment ( " EXTRACT(hour from (? at time zone ?)) " , ^ job_start , s . timezone ) >= 9 , # It's after 9am
2019-09-09 14:19:21 +03:00
preload : [ weekly_report : wr ]
2019-09-05 19:11:07 +03:00
)
for site <- sites do
2020-01-22 12:16:53 +03:00
query = Stats.Query . from ( site . timezone , %{ " period " = > " 7d " } )
2019-09-11 18:35:30 +03:00
2020-01-22 12:16:53 +03:00
for email <- site . weekly_report . recipients do
2020-02-04 16:44:13 +03:00
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 } "
2020-01-22 12:16:53 +03:00
send_report ( email , site , " Weekly " , unsubscribe_link , query )
end
2019-09-11 18:35:30 +03:00
2020-01-22 12:16:53 +03:00
weekly_report_sent ( site , job_start )
2019-09-11 18:35:30 +03:00
end
end
2020-01-22 12:16:53 +03:00
defp send_monthly_emails ( job_start ) do
2019-09-11 18:35:30 +03:00
sites = Repo . all (
from s in Plausible.Site ,
join : mr in Plausible.Site.MonthlyReport , on : mr . site_id == s . id ,
2020-01-22 12:16:53 +03:00
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 ) ,
2019-09-11 18:35:30 +03:00
where : is_nil ( se ) , # We haven't sent a report for this site this month
2020-01-22 12:16:53 +03:00
where : fragment ( " EXTRACT(day from (? at time zone ?)) " , ^ job_start , s . timezone ) == 1 , # It's the 1st of the month in the local timezone
where : fragment ( " EXTRACT(hour from (? at time zone ?)) " , ^ job_start , s . timezone ) >= 9 , # It's after 9am
2019-09-11 18:35:30 +03:00
preload : [ monthly_report : mr ]
)
for site <- sites do
2020-01-22 12:16:53 +03:00
last_month = job_start |> Timex.Timezone . convert ( site . timezone ) |> Timex . shift ( months : - 1 ) |> Timex . beginning_of_month
query = Stats.Query . from ( site . timezone , %{ " period " = > " month " , " date " = > Timex . format! ( last_month , " {ISOdate} " ) } )
2019-09-11 18:35:30 +03:00
2020-01-22 12:16:53 +03:00
for email <- site . monthly_report . recipients do
Logger . info ( " Sending monthly report for #{ site . domain } to #{ email } " )
2020-02-04 16:44:13 +03:00
unsubscribe_link = PlausibleWeb.Endpoint . url ( ) <> " /sites/ #{ URI . encode_www_form ( site . domain ) } /monthly-report/unsubscribe?email= #{ email } "
2020-01-22 12:16:53 +03:00
send_report ( email , site , Timex . format! ( last_month , " {Mfull} " ) , unsubscribe_link , query )
end
2019-09-11 18:35:30 +03:00
2020-01-22 12:16:53 +03:00
monthly_report_sent ( site , job_start )
2019-09-05 19:11:07 +03:00
end
end
2020-01-22 12:16:53 +03:00
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 , Stats.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 )
2019-09-05 19:11:07 +03:00
PlausibleWeb.Email . weekly_report ( email , site ,
unique_visitors : unique_visitors ,
change_visitors : change_visitors ,
pageviews : pageviews ,
change_pageviews : change_pageviews ,
2020-01-22 12:16:53 +03:00
bounce_rate : bounce_rate ,
change_bounce_rate : change_bounce_rate ,
2019-09-05 19:11:07 +03:00
referrers : referrers ,
2020-01-22 12:16:53 +03:00
unsubscribe_link : unsubscribe_link ,
view_link : view_link ( site , query ) ,
2019-09-05 19:11:07 +03:00
pages : pages ,
2020-01-22 12:16:53 +03:00
query : query ,
name : name
2019-09-05 19:11:07 +03:00
) |> Plausible.Mailer . deliver_now ( )
end
2020-01-22 12:16:53 +03:00
defp weekly_report_sent ( site , time ) do
{ year , week } = time |> DateTime . to_date |> Timex . iso_week
2019-09-05 19:11:07 +03:00
2019-09-09 14:19:21 +03:00
Repo . insert_all ( " sent_weekly_reports " , [ %{
2019-09-05 19:11:07 +03:00
site_id : site . id ,
year : year ,
week : week ,
timestamp : Timex . now ( )
} ] )
end
2019-09-11 18:35:30 +03:00
2020-01-22 12:16:53 +03:00
defp monthly_report_sent ( site , time ) do
date = DateTime . to_date ( time )
2019-09-11 18:35:30 +03:00
Repo . insert_all ( " sent_monthly_reports " , [ %{
site_id : site . id ,
year : date . year ,
month : date . month ,
timestamp : Timex . now ( )
} ] )
end
2020-01-22 12:16:53 +03:00
defp view_link ( site , % Plausible.Stats.Query { period : " 7d " } ) do
2020-02-04 16:44:13 +03:00
PlausibleWeb.Endpoint . url ( ) <> " / #{ URI . encode_www_form ( site . domain ) } ?period=7d "
2020-01-22 12:16:53 +03:00
end
defp view_link ( site , % Plausible.Stats.Query { period : " month " , date_range : range } ) do
month = Timex . format! ( range . first , " {ISOdate} " )
2020-02-04 16:44:13 +03:00
PlausibleWeb.Endpoint . url ( ) <> " / #{ URI . encode_www_form ( site . domain ) } ?period=month&date= #{ month } "
2020-01-22 12:16:53 +03:00
end
2019-09-05 19:11:07 +03:00
end