hackatime/app/mailers/weekly_summary_mailer.rb
Mahad Kalam 922e7384c0
Fix email from name/dates, login flash, wakatime_setup redirect, Sunday 6:30pm GMT (#1067)
* Smol fixes

* Map <<LAST_LANGUAGE>

* whoops

* Fix

* Move emails from Friday to Sunday
2026-03-13 10:53:57 +00:00

58 lines
2.1 KiB
Ruby

class WeeklySummaryMailer < ApplicationMailer
helper :application
def weekly_summary(user, recipient_email:, starts_at:, ends_at:)
@user = user
@unsubscribe_url = mailkick_unsubscribe_url(@user, "weekly_summary")
user_timezone = ActiveSupport::TimeZone[@user.timezone]
@timezone = user_timezone || ActiveSupport::TimeZone["UTC"]
@timezone_label = user_timezone ? @user.timezone : @timezone.tzinfo.identifier
@starts_at = starts_at.utc
@ends_at = ends_at.utc
@starts_at_local = @starts_at.in_time_zone(@timezone)
@ends_at_local = @ends_at.in_time_zone(@timezone)
@subject_period_label = "#{@starts_at_local.strftime("%b %-d")} - #{@ends_at_local.strftime("%b %-d, %Y")}"
@period_label = @subject_period_label
coding_heartbeats = @user.heartbeats.where(time: @starts_at.to_f...@ends_at.to_f)
@total_seconds = coding_heartbeats.duration_seconds
num_days = [ (@ends_at - @starts_at) / 1.day, 1 ].max
@daily_average_seconds = (@total_seconds / num_days).round
@total_heartbeats = coding_heartbeats.count
@active_days = active_days_count(coding_heartbeats)
@top_projects = breakdown(coding_heartbeats, :project)
@top_languages = breakdown(coding_heartbeats, :language)
mail(
to: recipient_email,
subject: "Your Hackatime weekly summary (#{@subject_period_label})"
)
end
private
def breakdown(scope, column, limit: 5)
scope.group(column)
.duration_seconds
.sort_by { |_name, seconds| -seconds.to_i }
.first(limit)
.map do |name, seconds|
{
name: name.presence || "Other",
seconds: seconds.to_i,
duration_label: ApplicationController.helpers.short_time_simple(seconds)
}
end
end
def active_days_count(scope)
timezone = @timezone_label
timezone_sql = ActiveRecord::Base.connection.quote(timezone)
scope.where.not(time: nil)
.distinct
.count(Arel.sql("DATE(to_timestamp(time) AT TIME ZONE #{timezone_sql})"))
rescue StandardError
scope.where.not(time: nil).pluck(:time).map { |time| Time.at(time).in_time_zone(timezone).to_date }.uniq.count
end
end