effective_messaging 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/admin/notifications_controller.rb +0 -2
- data/app/datatables/admin/effective_notification_logs_datatable.rb +4 -3
- data/app/datatables/admin/effective_notifications_datatable.rb +2 -0
- data/app/jobs/effective/notification_job.rb +2 -2
- data/app/mailers/effective/notifications_mailer.rb +1 -0
- data/app/models/effective/notification.rb +60 -18
- data/app/models/effective/notification_log.rb +3 -2
- data/app/views/admin/notifications/_form_notification.html.haml +0 -4
- data/app/views/admin/notifications/_notification.html.haml +9 -41
- data/app/views/admin/notifications/_summary.html.haml +49 -0
- data/config/routes.rb +6 -1
- data/db/migrate/101_create_effective_messaging.rb +1 -0
- data/lib/effective_messaging/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dedeb21248c1deac281cc473a00f64e23fc41c74ccff84cde9cb8cad9f3295c9
|
4
|
+
data.tar.gz: 6b102bd482f7b03b62f1caaff60a406fd561439316d7a26c5c1839428b161c7d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8726856fc8632e2c7159ff98ca1b97098eee126dcaa5a29ae87905759769f8613ec02a1c711a8233fd236c1fecf8bfd3a75d638a87dea2c74903c6b5187bff78
|
7
|
+
data.tar.gz: f890bac4407411221c371fbcdcfd3e545db95b37ff078a782376b1d3e562debd15a622b3996a04b30422927488fe7157abc18c0ad9ef173b47f8a083f4efd792
|
@@ -8,9 +8,10 @@ module Admin
|
|
8
8
|
|
9
9
|
col :notification
|
10
10
|
col :report, visible: !attributes[:inline]
|
11
|
-
col :resource
|
12
|
-
col :user
|
13
|
-
col :email
|
11
|
+
col :resource, search: :string
|
12
|
+
col :user, search: :string
|
13
|
+
col :email
|
14
|
+
col :skipped
|
14
15
|
|
15
16
|
actions_col
|
16
17
|
end
|
@@ -11,6 +11,7 @@ module Effective
|
|
11
11
|
|
12
12
|
# Attach report
|
13
13
|
attach_report!(notification)
|
14
|
+
rendered.delete(:content_type) if notification.attach_report?
|
14
15
|
|
15
16
|
# Works with effective_logging to associate this email with the notification
|
16
17
|
headers = headers_for(notification, opts)
|
@@ -102,6 +102,10 @@ module Effective
|
|
102
102
|
validates :immediate_times, presence: true, numericality: { greater_than_or_equal_to: 1 }
|
103
103
|
end
|
104
104
|
|
105
|
+
validate(if: -> { immediate? && immediate_days.present? && immediate_times.present? }) do
|
106
|
+
self.errors.add(:immediate_times, "must be 1 when when using every 0 days") if immediate_days == 0 && immediate_times != 1
|
107
|
+
end
|
108
|
+
|
105
109
|
# Scheduled
|
106
110
|
validates :scheduled_method, presence: true, inclusion: { in: SCHEDULED_METHODS.map(&:last) }, if: -> { scheduled? }
|
107
111
|
|
@@ -154,6 +158,8 @@ module Effective
|
|
154
158
|
end
|
155
159
|
end
|
156
160
|
|
161
|
+
# This operates on each row of the resource.
|
162
|
+
# We track the number of notifications total to see if we should notify again or not
|
157
163
|
def immediate?
|
158
164
|
schedule_type == 'immediate'
|
159
165
|
end
|
@@ -210,24 +216,55 @@ module Effective
|
|
210
216
|
@rows_count ||= report.collection().count if report
|
211
217
|
end
|
212
218
|
|
213
|
-
|
219
|
+
def notifiable_rows_count
|
220
|
+
report.collection().select { |resource| notifiable?(resource) }.count if report
|
221
|
+
end
|
222
|
+
|
223
|
+
def notifiable_tomorrow_rows_count
|
224
|
+
report.collection().select { |resource| notifiable_tomorrow?(resource) }.count if report
|
225
|
+
end
|
226
|
+
|
227
|
+
# Enqueues this notification to send right away.
|
228
|
+
# Only applies to scheduled_email? notifications
|
214
229
|
def send_now!
|
215
230
|
raise('expected to be persisted') unless persisted?
|
216
|
-
NotificationJob.perform_later(id)
|
231
|
+
NotificationJob.perform_later(id, force: true)
|
217
232
|
true
|
218
233
|
end
|
219
234
|
|
220
|
-
#
|
221
|
-
|
222
|
-
|
235
|
+
# Only applies to immedate? notifications
|
236
|
+
# Skips over one notification on the immediate notifications
|
237
|
+
def skip_once!
|
238
|
+
notified = 0
|
239
|
+
|
240
|
+
report.collection().find_each do |resource|
|
241
|
+
print('.')
|
242
|
+
|
243
|
+
# For logging
|
244
|
+
assign_attributes(current_resource: resource)
|
245
|
+
|
246
|
+
# Send the resource email
|
247
|
+
build_notification_log(resource: resource, skipped: true).save!
|
248
|
+
|
249
|
+
notified += 1
|
250
|
+
|
251
|
+
GC.start if (notified % 250) == 0
|
252
|
+
end
|
253
|
+
|
254
|
+
touch
|
255
|
+
end
|
256
|
+
|
257
|
+
# The main function to send this thing
|
258
|
+
def notify!(force: false)
|
259
|
+
scheduled_email? ? notify_by_schedule!(force: force) : notify_by_resources!(force: force)
|
223
260
|
end
|
224
261
|
|
225
262
|
# Operates on every resource in the data source. Sends one email for each row
|
226
|
-
def notify_by_resources!
|
263
|
+
def notify_by_resources!(force: false)
|
227
264
|
notified = 0
|
228
265
|
|
229
266
|
report.collection().find_each do |resource|
|
230
|
-
next unless notifiable?(resource)
|
267
|
+
next unless notifiable?(resource) || force
|
231
268
|
print('.')
|
232
269
|
|
233
270
|
# For logging
|
@@ -245,10 +282,10 @@ module Effective
|
|
245
282
|
notified > 0 ? update!(last_notified_at: Time.zone.now, last_notified_count: notified) : touch
|
246
283
|
end
|
247
284
|
|
248
|
-
def notify_by_schedule!
|
285
|
+
def notify_by_schedule!(force: false)
|
249
286
|
notified = 0
|
250
287
|
|
251
|
-
if notifiable_scheduled?
|
288
|
+
if notifiable_scheduled? || force
|
252
289
|
build_notification_log(resource: nil).save!
|
253
290
|
Effective::NotificationsMailer.notify(self).deliver_now
|
254
291
|
notified += 1
|
@@ -257,22 +294,27 @@ module Effective
|
|
257
294
|
notified > 0 ? update!(last_notified_at: Time.zone.now, last_notified_count: notified) : touch
|
258
295
|
end
|
259
296
|
|
260
|
-
def notifiable?(resource)
|
297
|
+
def notifiable?(resource, date: nil)
|
261
298
|
raise('expected an acts_as_reportable resource') unless resource.class.try(:acts_as_reportable?)
|
262
299
|
|
263
300
|
if schedule_type == 'immediate'
|
264
|
-
notifiable_immediate?(resource: resource)
|
301
|
+
notifiable_immediate?(resource: resource, date: date)
|
265
302
|
elsif schedule_type == 'scheduled'
|
266
|
-
notifiable_scheduled?(date:
|
303
|
+
notifiable_scheduled?(date: date)
|
267
304
|
else
|
268
305
|
raise("unsupported schedule_type")
|
269
306
|
end
|
270
307
|
end
|
271
308
|
|
309
|
+
def notifiable_tomorrow?(resource)
|
310
|
+
date = Time.zone.now.beginning_of_day.advance(days: 1)
|
311
|
+
notifiable?(resource, date: date)
|
312
|
+
end
|
313
|
+
|
272
314
|
# Consider the notification logs which track how many and how long ago this notification was sent
|
273
315
|
# It's notifiable? when first time or if it's been immediate_days since last notification
|
274
|
-
def notifiable_immediate?(resource:)
|
275
|
-
raise('expected an
|
316
|
+
def notifiable_immediate?(resource:, date: nil)
|
317
|
+
raise('expected an immediate? notification') unless immediate?
|
276
318
|
|
277
319
|
email = resource_email(resource) || resource_user(resource).try(:email)
|
278
320
|
raise("expected an email for #{report} #{report&.id} and #{resource} #{resource&.id}") unless email.present?
|
@@ -283,8 +325,8 @@ module Effective
|
|
283
325
|
true # This is the first time. We should send.
|
284
326
|
elsif logs.count < immediate_times
|
285
327
|
# We still have to send it but consider dates.
|
286
|
-
last_sent_days_ago = logs.map(
|
287
|
-
last_sent_days_ago >= immediate_days
|
328
|
+
last_sent_days_ago = logs.map { |log| log.days_ago(date: date) }.min || 0
|
329
|
+
(last_sent_days_ago >= immediate_days)
|
288
330
|
else
|
289
331
|
false # We've already sent enough times
|
290
332
|
end
|
@@ -348,13 +390,13 @@ module Effective
|
|
348
390
|
report_assigns.merge(reportable_view_assigns)
|
349
391
|
end
|
350
392
|
|
351
|
-
def build_notification_log(resource: nil)
|
393
|
+
def build_notification_log(resource: nil, skipped: false)
|
352
394
|
user = resource_user(resource)
|
353
395
|
|
354
396
|
email = resource_email(resource) || user.try(:email)
|
355
397
|
email ||= audience_emails_to_s if scheduled_email?
|
356
398
|
|
357
|
-
notification_logs.build(email: email, report: report, resource: resource, user: user)
|
399
|
+
notification_logs.build(email: email, report: report, resource: resource, user: user, skipped: skipped)
|
358
400
|
end
|
359
401
|
|
360
402
|
private
|
@@ -12,6 +12,7 @@ module Effective
|
|
12
12
|
|
13
13
|
effective_resource do
|
14
14
|
email :string
|
15
|
+
skipped :boolean
|
15
16
|
|
16
17
|
timestamps
|
17
18
|
end
|
@@ -25,8 +26,8 @@ module Effective
|
|
25
26
|
model_name.human
|
26
27
|
end
|
27
28
|
|
28
|
-
def days_ago
|
29
|
-
now = Time.zone.now.to_date
|
29
|
+
def days_ago(date: nil)
|
30
|
+
now = (date || Time.zone.now).to_date
|
30
31
|
(now - (created_at&.to_date || now)).to_i
|
31
32
|
end
|
32
33
|
|
@@ -76,9 +76,5 @@
|
|
76
76
|
|
77
77
|
= f.submit do
|
78
78
|
= f.save 'Save'
|
79
|
-
|
80
|
-
- if EffectiveResources.authorized?(self, :send_now, f.object)
|
81
|
-
= f.save 'Send Now', class: 'btn btn-warning', 'data-confirm': "Really send now?"
|
82
|
-
|
83
79
|
= f.save 'Add New', class: 'btn btn-secondary'
|
84
80
|
= f.save 'Continue', class: 'btn btn-secondary'
|
@@ -1,43 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
report at the time of sending.
|
1
|
+
= tabs do
|
2
|
+
= tab 'Notification' do
|
3
|
+
= render 'admin/notifications/summary', notification: notification
|
5
4
|
|
6
|
-
|
5
|
+
- if notification.persisted? && notification.respond_to?(:logs_datatable)
|
6
|
+
= tab 'Email Logs' do
|
7
|
+
- datatable = Admin::EffectiveNotificationLogsDatatable.new(notification: notification)
|
8
|
+
= render_inline_datatable(datatable)
|
7
9
|
|
8
|
-
=
|
9
|
-
|
10
|
-
|
11
|
-
- if notification.rows_count > 0
|
12
|
-
%p Using a random row from the data source, a preview of the notification follows:
|
13
|
-
|
14
|
-
= card('Preview of Notification') do
|
15
|
-
- resource = notification.report.collection.order('RANDOM()').first
|
16
|
-
- rendered = notification.assign_renderer(self).render_email(resource)
|
17
|
-
|
18
|
-
%table.table
|
19
|
-
%tbody
|
20
|
-
%tr
|
21
|
-
%th To
|
22
|
-
%td= rendered.fetch(:to)
|
23
|
-
%tr
|
24
|
-
%th From
|
25
|
-
%td= rendered.fetch(:from)
|
26
|
-
|
27
|
-
- if (cc = rendered[:cc]).present?
|
28
|
-
%tr
|
29
|
-
%th CC
|
30
|
-
%td= cc
|
31
|
-
|
32
|
-
- if (bcc = rendered[:bcc]).present?
|
33
|
-
%tr
|
34
|
-
%th BCC
|
35
|
-
%td= bcc
|
36
|
-
|
37
|
-
%tr
|
38
|
-
%th Subject
|
39
|
-
%td= rendered.fetch(:subject)
|
40
|
-
|
41
|
-
%tr
|
42
|
-
%td{colspan: 2}
|
43
|
-
= simple_format(rendered.fetch(:body).to_s)
|
10
|
+
= tab 'Logs' do
|
11
|
+
= render_inline_datatable(notification.logs_datatable)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
%p
|
2
|
+
This notification will be sent to all rows from the
|
3
|
+
= link_to(notification.report, effective_reports.admin_report_path(notification.report), target: '_blank')
|
4
|
+
report at the time of sending.
|
5
|
+
|
6
|
+
%p Tomorrow morning, #{pluralize(notification.notifiable_tomorrow_rows_count, 'rows')} notifications will be sent.
|
7
|
+
|
8
|
+
.mb-2
|
9
|
+
= collapse('Show notification details') do
|
10
|
+
= effective_table_with(notification)
|
11
|
+
|
12
|
+
.mb-2
|
13
|
+
= collapse("Show report data") do
|
14
|
+
%h4= notification.report.to_s
|
15
|
+
= render 'admin/reports/report', report: notification.report
|
16
|
+
|
17
|
+
- if notification.rows_count > 0
|
18
|
+
%p Using a random row from the data source, a preview of the notification follows:
|
19
|
+
|
20
|
+
= card('Preview') do
|
21
|
+
- resource = notification.report.collection.order('RANDOM()').first
|
22
|
+
- rendered = notification.assign_renderer(self).render_email(resource)
|
23
|
+
|
24
|
+
%table.table
|
25
|
+
%tbody
|
26
|
+
%tr
|
27
|
+
%th To
|
28
|
+
%td= rendered.fetch(:to)
|
29
|
+
%tr
|
30
|
+
%th From
|
31
|
+
%td= rendered.fetch(:from)
|
32
|
+
|
33
|
+
- if (cc = rendered[:cc]).present?
|
34
|
+
%tr
|
35
|
+
%th CC
|
36
|
+
%td= cc
|
37
|
+
|
38
|
+
- if (bcc = rendered[:bcc]).present?
|
39
|
+
%tr
|
40
|
+
%th BCC
|
41
|
+
%td= bcc
|
42
|
+
|
43
|
+
%tr
|
44
|
+
%th Subject
|
45
|
+
%td= rendered.fetch(:subject)
|
46
|
+
|
47
|
+
%tr
|
48
|
+
%td{colspan: 2}
|
49
|
+
= simple_format(rendered.fetch(:body).to_s)
|
data/config/routes.rb
CHANGED
@@ -13,7 +13,12 @@ EffectiveMessaging::Engine.routes.draw do
|
|
13
13
|
namespace :admin do
|
14
14
|
resources :chats
|
15
15
|
resources :chat_messages, only: [:index, :show, :destroy]
|
16
|
-
|
16
|
+
|
17
|
+
resources :notifications do
|
18
|
+
post :send_now, on: :member
|
19
|
+
post :skip_once, on: :member
|
20
|
+
end
|
21
|
+
|
17
22
|
resources :notification_logs, only: [:index, :destroy]
|
18
23
|
end
|
19
24
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_messaging
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -237,6 +237,7 @@ files:
|
|
237
237
|
- app/views/admin/notifications/_form.html.haml
|
238
238
|
- app/views/admin/notifications/_form_notification.html.haml
|
239
239
|
- app/views/admin/notifications/_notification.html.haml
|
240
|
+
- app/views/admin/notifications/_summary.html.haml
|
240
241
|
- app/views/effective/chats/_chat.html.haml
|
241
242
|
- app/views/effective/chats/_chat_message.html.haml
|
242
243
|
- app/views/effective/chats/_form.html.haml
|