effective_messaging 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee0fc8653681a36c4de9e9756c348c2438e376bff57409b584a5d7e514a1e596
4
- data.tar.gz: bf10b4a5f6641193eac7f0da6300d6851b3d51417fe72b36095e6702823936f9
3
+ metadata.gz: dedeb21248c1deac281cc473a00f64e23fc41c74ccff84cde9cb8cad9f3295c9
4
+ data.tar.gz: 6b102bd482f7b03b62f1caaff60a406fd561439316d7a26c5c1839428b161c7d
5
5
  SHA512:
6
- metadata.gz: 6fdbe70ef0e37d29dc8126ac9416361e4cfd87929f20975dcff60d4281d2804b96c3def8d0a2303be2210e98985310a1858beddaeeb332366ec33bb0bfe69fb6
7
- data.tar.gz: 22dafc2abd52b17461126ceafc84164c6d9446f8fe2ff8f997da2b29ff8db5cdc959a1c59123f75ad45499df19057cc04cfd98a03074f12ce6a4958a4406d77e
6
+ metadata.gz: 8726856fc8632e2c7159ff98ca1b97098eee126dcaa5a29ae87905759769f8613ec02a1c711a8233fd236c1fecf8bfd3a75d638a87dea2c74903c6b5187bff78
7
+ data.tar.gz: f890bac4407411221c371fbcdcfd3e545db95b37ff078a782376b1d3e562debd15a622b3996a04b30422927488fe7157abc18c0ad9ef173b47f8a083f4efd792
@@ -5,8 +5,6 @@ module Admin
5
5
 
6
6
  include Effective::CrudController
7
7
 
8
- submit :send_now, 'Send Now'
9
-
10
8
  private
11
9
 
12
10
  def permitted_params
@@ -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, visible: false
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
@@ -34,6 +34,8 @@ module Admin
34
34
 
35
35
  col :report, search: Effective::Report.notifiable.sorted, action: :show
36
36
 
37
+ col :notifiable_tomorrow_rows_count, label: 'Tomorrow'
38
+
37
39
  col(:rows_count) do |notification|
38
40
  notification.report.collection().count
39
41
  end
@@ -1,8 +1,8 @@
1
1
  module Effective
2
2
  class NotificationJob < ApplicationJob
3
3
 
4
- def perform(id)
5
- Notification.find(id).notify!
4
+ def perform(id, force:)
5
+ Notification.find(id).notify!(force: force)
6
6
  end
7
7
 
8
8
  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
- # Button on the Admin interface. Enqueues the job to send right away.
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
- # The main function
221
- def notify!
222
- scheduled_email? ? notify_by_schedule! : notify_by_resources!
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: nil)
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 immexiate? notification') unless immediate?
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(&:days_ago).min || 0
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
- %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.
1
+ = tabs do
2
+ = tab 'Notification' do
3
+ = render 'admin/notifications/summary', notification: notification
5
4
 
6
- %p Currently there are #{pluralize(notification.rows_count, 'rows')} that would be notified.
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
- = card(notification) do
9
- = effective_table_with(notification)
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
- resources :notifications
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
 
@@ -87,6 +87,7 @@ class CreateEffectiveMessaging < ActiveRecord::Migration[6.0]
87
87
  t.string :resource_type
88
88
 
89
89
  t.string :email
90
+ t.boolean :skipped, default: false
90
91
 
91
92
  t.timestamps
92
93
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveMessaging
2
- VERSION = '0.4.2'.freeze
2
+ VERSION = '0.5.0'.freeze
3
3
  end
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.2
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-26 00:00:00.000000000 Z
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