|
|
|
@ -37,71 +37,42 @@ module Users::Scopes |
|
|
|
|
# * That user has an unread notification |
|
|
|
|
# * The user hasn't been informed about the unread notification before |
|
|
|
|
# * The user has configured reminder mails to be sent now. |
|
|
|
|
# This assumes that users only have full hours specified for the time they desire |
|
|
|
|
# to receive a reminder mail. |
|
|
|
|
def having_reminder_mail_to_send_now |
|
|
|
|
# Left outer join as not all user instances have preferences associated |
|
|
|
|
# but we still want to select them |
|
|
|
|
recipient_candidates = User.active |
|
|
|
|
.left_joins(:preference) |
|
|
|
|
.where(where_statement) |
|
|
|
|
# but we still want to select them. |
|
|
|
|
recipient_candidates = User |
|
|
|
|
.active |
|
|
|
|
.left_joins(:preference) |
|
|
|
|
.joins(local_time_join) |
|
|
|
|
|
|
|
|
|
subscriber_ids = Notification |
|
|
|
|
.unsent_reminders_before(recipient: recipient_candidates, time: Time.current) |
|
|
|
|
.group(:recipient_id) |
|
|
|
|
.select(:recipient_id) |
|
|
|
|
|
|
|
|
|
User.where(id: subscriber_ids) |
|
|
|
|
where(id: subscriber_ids) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def where_statement |
|
|
|
|
current_timestamp_utc = Time.current.getutc |
|
|
|
|
|
|
|
|
|
age = age_statement(current_timestamp_utc) |
|
|
|
|
<<-SQL.squish |
|
|
|
|
#{age} < make_interval(mins=>30) |
|
|
|
|
AND |
|
|
|
|
#{age} >= make_interval(mins=>0) |
|
|
|
|
SQL |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# Creates a SQL snippet for calculating the time between now and |
|
|
|
|
# the reminder time slot with the each user's time zone |
|
|
|
|
# @param [Time] current_timestamp_utc |
|
|
|
|
def age_statement(current_timestamp_utc) |
|
|
|
|
year = current_timestamp_utc.year |
|
|
|
|
month = current_timestamp_utc.month |
|
|
|
|
day = current_timestamp_utc.day |
|
|
|
|
|
|
|
|
|
case_statement = case_statement_zone_name_to_offset |
|
|
|
|
|
|
|
|
|
slot_time = Time.zone.parse(Setting.notification_email_digest_time) |
|
|
|
|
|
|
|
|
|
<<-SQL.squish |
|
|
|
|
age( |
|
|
|
|
now(), |
|
|
|
|
make_timestamptz(#{year}, #{month}, #{day}, #{slot_time.hour}, #{slot_time.min}, 0, (#{case_statement})) |
|
|
|
|
) |
|
|
|
|
def local_time_join |
|
|
|
|
<<~SQL.squish |
|
|
|
|
JOIN (#{local_time_table}) AS local_times |
|
|
|
|
ON COALESCE(user_preferences.settings->>'time_zone', 'UTC') = local_times.zone |
|
|
|
|
AND local_times.time = '#{Setting.notification_email_digest_time}:00+00:00' |
|
|
|
|
SQL |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def case_statement_zone_name_to_offset |
|
|
|
|
return @case_statement_zone_name_to_offset if @case_statement_zone_name_to_offset |
|
|
|
|
|
|
|
|
|
def local_time_table |
|
|
|
|
current_time = Time.current |
|
|
|
|
statement = ActiveSupport::TimeZone.all.map do |zone| |
|
|
|
|
offset = current_offset(zone, current_time) |
|
|
|
|
"WHEN user_preferences.settings->>'time_zone' = '#{zone.name.gsub("'") { "''" }}' THEN '#{offset}'" |
|
|
|
|
end |
|
|
|
|
@case_statement_zone_name_to_offset = "CASE\n#{statement.join("\n")}\nELSE '+00:00'\nEND" |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# The real offset of a time zone depends of the moment we ask. Winter and summer time |
|
|
|
|
# have different offsets of UTC. For instance, time zone "Berlin" in winter has +01:00 |
|
|
|
|
# and in summer +02:00 |
|
|
|
|
# @param [Time] time |
|
|
|
|
# @param [TimeZone] zone |
|
|
|
|
# @return [String] |
|
|
|
|
def current_offset(zone, time = Time.current) |
|
|
|
|
time.in_time_zone(zone).formatted_offset |
|
|
|
|
times_with_zones = ActiveSupport::TimeZone |
|
|
|
|
.all |
|
|
|
|
.map do |z| |
|
|
|
|
[current_time.in_time_zone(z).strftime('%H:00:00+00:00'), z.name.gsub("'", "''")] |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
"SELECT * FROM #{arel_table.grouping(Arel::Nodes::ValuesList.new(times_with_zones)).as('t(time, zone)').to_sql}" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|