Class: Effective::Notification
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Effective::Notification
- Defined in:
- app/models/effective/notification.rb
Constant Summary collapse
- AUDIENCES =
[ ['Send to user or email from the report', 'report'], ['Send to specific addresses', 'emails'] ]
- SCHEDULE_TYPES =
[ ['On the first day they appear in the report and every x days thereafter', 'immediate'], ['When present in the report on the following dates', 'scheduled'] ]
- SCHEDULED_METHODS =
TODO: [‘Send once’, ‘Send daily’, ‘Send weekly’, ‘Send monthly’, ‘Send quarterly’, ‘Send yearly’, ‘Send now’]
[ ['The following dates...', 'dates'], ]
- CONTENT_TYPES =
['text/plain', 'text/html']
Instance Attribute Summary collapse
-
#current_resource ⇒ Object
Returns the value of attribute current_resource.
-
#current_user ⇒ Object
Returns the value of attribute current_user.
-
#view_context ⇒ Object
Returns the value of attribute view_context.
Instance Method Summary collapse
- #already_notified_today?(resource) ⇒ Boolean
- #assign_renderer(view_context) ⇒ Object
-
#assigns_for(resource = nil) ⇒ Object
We pull the Assigns from 2 places: 1.
- #audience_emails ⇒ Object
- #audience_emails? ⇒ Boolean
- #audience_report? ⇒ Boolean
- #build_notification_log(resource: nil, skipped: false) ⇒ Object
- #disable! ⇒ Object
- #email_template ⇒ Object
- #email_template_variables ⇒ Object
- #enable! ⇒ Object
-
#immediate? ⇒ Boolean
This operates on each row of the resource.
- #notifiable?(resource, date: nil) ⇒ Boolean
-
#notifiable_immediate?(resource:, date: nil) ⇒ Boolean
Consider the notification logs which track how many and how long ago this notification was sent It’s notifiable? when first time or if it’s been immediate_days since last notification.
- #notifiable_rows_count ⇒ Object
- #notifiable_scheduled?(date: nil) ⇒ Boolean
- #notifiable_tomorrow?(resource) ⇒ Boolean
- #notifiable_tomorrow_rows_count ⇒ Object
-
#notify!(force: false) ⇒ Object
The main function to send this thing.
-
#notify_by_resources!(force: false) ⇒ Object
Operates on every resource in the data source.
- #notify_by_schedule!(force: false) ⇒ Object
-
#preview ⇒ Object
Returns a message.
- #renderer ⇒ Object
- #rows_count ⇒ Object
- #schedule ⇒ Object
- #scheduled? ⇒ Boolean
- #scheduled_dates ⇒ Object
-
#scheduled_email? ⇒ Boolean
Only scheduled emails can have attached reports.
-
#send_now! ⇒ Object
Enqueues this notification to send right away.
-
#skip_once! ⇒ Object
Only applies to immedate? notifications Skips over one notification on the immediate notifications.
- #to_email(resource) ⇒ Object
- #to_s ⇒ Object
Instance Attribute Details
#current_resource ⇒ Object
Returns the value of attribute current_resource.
10 11 12 |
# File 'app/models/effective/notification.rb', line 10 def current_resource @current_resource end |
#current_user ⇒ Object
Returns the value of attribute current_user.
9 10 11 |
# File 'app/models/effective/notification.rb', line 9 def current_user @current_user end |
#view_context ⇒ Object
Returns the value of attribute view_context.
11 12 13 |
# File 'app/models/effective/notification.rb', line 11 def view_context @view_context end |
Instance Method Details
#already_notified_today?(resource) ⇒ Boolean
345 346 347 348 349 350 351 352 353 354 |
# File 'app/models/effective/notification.rb', line 345 def already_notified_today?(resource) email = resource_emails_to_s(resource) raise("expected an email for #{report} #{report&.id} and #{resource} #{resource&.id}") unless email.present? logs = notification_logs.select { |log| log.email == email } return false if logs.count == 0 # If we already notified today logs.any? { |log| log.created_at&.beginning_of_day == Time.zone.now.beginning_of_day } end |
#assign_renderer(view_context) ⇒ Object
190 191 192 193 194 |
# File 'app/models/effective/notification.rb', line 190 def assign_renderer(view_context) raise('expected renderer to respond to') unless view_context.respond_to?(:root_url) assign_attributes(view_context: view_context) self end |
#assigns_for(resource = nil) ⇒ Object
We pull the Assigns from 2 places:
-
The report.report_columns
-
The class’s def reportable_view_assigns(view) method
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'app/models/effective/notification.rb', line 397 def assigns_for(resource = nil) return {} unless report.present? resource ||= report.reportable.new raise('expected an acts_as_reportable resource') unless resource.class.try(:acts_as_reportable?) report_assigns = Array(report.report_columns).inject({}) do |h, column| value = resource.send(column.name) h[column.name] = column.format(value); h end reportable_view_assigns = resource.reportable_view_assigns(renderer).deep_stringify_keys raise('expected notification assigns to return a Hash') unless reportable_view_assigns.kind_of?(Hash) # Merge all assigns report_assigns.merge(reportable_view_assigns) end |
#audience_emails ⇒ Object
174 175 176 |
# File 'app/models/effective/notification.rb', line 174 def audience_emails Array(self[:audience_emails]) - [nil, ''] end |
#audience_emails? ⇒ Boolean
160 161 162 |
# File 'app/models/effective/notification.rb', line 160 def audience_emails? audience == 'emails' end |
#audience_report? ⇒ Boolean
164 165 166 |
# File 'app/models/effective/notification.rb', line 164 def audience_report? audience == 'report' end |
#build_notification_log(resource: nil, skipped: false) ⇒ Object
415 416 417 418 419 420 421 422 |
# File 'app/models/effective/notification.rb', line 415 def build_notification_log(resource: nil, skipped: false) emailable = resource_emailable(resource) email = resource_emails_to_s(resource) email ||= audience_emails_to_s if scheduled_email? notification_logs.build(email: email, report: report, resource: resource, user: emailable, skipped: skipped) end |
#disable! ⇒ Object
216 217 218 |
# File 'app/models/effective/notification.rb', line 216 def disable! update!(enabled: false) end |
#email_template ⇒ Object
182 183 184 |
# File 'app/models/effective/notification.rb', line 182 def email_template :notification # We always use this email template end |
#email_template_variables ⇒ Object
186 187 188 |
# File 'app/models/effective/notification.rb', line 186 def email_template_variables assigns_for().keys end |
#enable! ⇒ Object
212 213 214 |
# File 'app/models/effective/notification.rb', line 212 def enable! update!(enabled: true) end |
#immediate? ⇒ Boolean
This operates on each row of the resource. We track the number of notifications total to see if we should notify again or not
152 153 154 |
# File 'app/models/effective/notification.rb', line 152 def immediate? schedule_type == 'immediate' end |
#notifiable?(resource, date: nil) ⇒ Boolean
328 329 330 331 332 333 334 335 336 337 338 |
# File 'app/models/effective/notification.rb', line 328 def notifiable?(resource, date: nil) raise('expected an acts_as_reportable resource') unless resource.class.try(:acts_as_reportable?) if schedule_type == 'immediate' notifiable_immediate?(resource: resource, date: date) elsif schedule_type == 'scheduled' notifiable_scheduled?(date: date) else raise("unsupported schedule_type") end end |
#notifiable_immediate?(resource:, date: nil) ⇒ Boolean
Consider the notification logs which track how many and how long ago this notification was sent It’s notifiable? when first time or if it’s been immediate_days since last notification
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'app/models/effective/notification.rb', line 358 def notifiable_immediate?(resource:, date: nil) raise('expected an immediate? notification') unless immediate? email = resource_emails_to_s(resource) raise("expected an email for #{report} #{report&.id} and #{resource} #{resource&.id}") unless email.present? logs = notification_logs.select { |log| log.email == email } if logs.count == 0 true # This is the first time. We should send. elsif logs.count < immediate_times # We still have to send it but consider dates. last_sent_days_ago = logs.map { |log| log.days_ago(date: date) }.min || 0 (last_sent_days_ago >= immediate_days) else false # We've already sent enough times end end |
#notifiable_rows_count ⇒ Object
204 205 206 |
# File 'app/models/effective/notification.rb', line 204 def notifiable_rows_count report.collection().select { |resource| notifiable?(resource) }.count if report end |
#notifiable_scheduled?(date: nil) ⇒ Boolean
377 378 379 380 381 382 383 384 385 386 387 388 |
# File 'app/models/effective/notification.rb', line 377 def notifiable_scheduled?(date: nil) raise('expected a scheduled? notification') unless scheduled? date ||= Time.zone.now.beginning_of_day case scheduled_method when 'dates' scheduled_dates.find { |day| day == date.strftime('%F') }.present? else raise('unsupported scheduled_method') end end |
#notifiable_tomorrow?(resource) ⇒ Boolean
340 341 342 343 |
# File 'app/models/effective/notification.rb', line 340 def notifiable_tomorrow?(resource) date = Time.zone.now.beginning_of_day.advance(days: 1) notifiable?(resource, date: date) end |
#notifiable_tomorrow_rows_count ⇒ Object
208 209 210 |
# File 'app/models/effective/notification.rb', line 208 def notifiable_tomorrow_rows_count report.collection().select { |resource| notifiable_tomorrow?(resource) }.count if report end |
#notify!(force: false) ⇒ Object
The main function to send this thing
251 252 253 |
# File 'app/models/effective/notification.rb', line 251 def notify!(force: false) scheduled_email? ? notify_by_schedule!(force: force) : notify_by_resources!(force: force) end |
#notify_by_resources!(force: false) ⇒ Object
Operates on every resource in the data source. Sends one email for each row
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'app/models/effective/notification.rb', line 270 def notify_by_resources!(force: false) notified = 0 report.collection().find_each do |resource| next unless notifiable?(resource) || force # Send Now functionality. Don't duplicate if it's same day. next if already_notified_today?(resource) && !force print('.') begin # For logging assign_attributes(current_resource: resource) # Send the resource email Effective::NotificationsMailer.notification(self, resource, email_notification_params).deliver_now # Log that it was sent build_notification_log(resource: resource).save! # Count how many we actually sent notified += 1 rescue => e EffectiveLogger.error(e., associated: self) if defined?(EffectiveLogger) ExceptionNotifier.notify_exception(e, data: { notification_id: id, resource_id: resource.id, resource_type: resource.class.name }) if defined?(ExceptionNotifier) raise(e) if Rails.env.test? || Rails.env.development? end GC.start if (notified % 250) == 0 end notified > 0 ? update!(last_notified_at: Time.zone.now, last_notified_count: notified) : touch end |
#notify_by_schedule!(force: false) ⇒ Object
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'app/models/effective/notification.rb', line 305 def notify_by_schedule!(force: false) notified = 0 if notifiable_scheduled? || force begin Effective::NotificationsMailer.notification(self, nil, email_notification_params).deliver_now # Log that it was sent build_notification_log(resource: nil).save! # Count how many we actually sent notified += 1 rescue => e EffectiveLogger.error(e., associated: self) if defined?(EffectiveLogger) ExceptionNotifier.notify_exception(e, data: { notification_id: id }) if defined?(ExceptionNotifier) raise(e) if Rails.env.test? || Rails.env.development? end end notified > 0 ? update!(last_notified_at: Time.zone.now, last_notified_count: notified) : touch end |
#preview ⇒ Object
Returns a message. Do not call deliver.
256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'app/models/effective/notification.rb', line 256 def preview return unless report.present? if audience_emails? # notify_by_schedule Effective::NotificationsMailer.notification(self, nil, email_notification_params) else # notify_by_resources resource = report.collection.order('RANDOM()').first Effective::NotificationsMailer.notification(self, resource, email_notification_params) if resource end end |
#renderer ⇒ Object
196 197 198 |
# File 'app/models/effective/notification.rb', line 196 def renderer view_context || nil # This isn't ideal end |
#rows_count ⇒ Object
200 201 202 |
# File 'app/models/effective/notification.rb', line 200 def rows_count @rows_count ||= report.collection().count if report end |
#schedule ⇒ Object
140 141 142 143 144 145 146 147 148 |
# File 'app/models/effective/notification.rb', line 140 def schedule if immediate? "Send immediately then every #{immediate_days} days for #{immediate_times} times total" elsif scheduled? && scheduled_method == 'dates' "Send on #{scheduled_dates.length} scheduled days: #{scheduled_dates.sort.to_sentence}" else 'todo' end end |
#scheduled? ⇒ Boolean
156 157 158 |
# File 'app/models/effective/notification.rb', line 156 def scheduled? schedule_type == 'scheduled' end |
#scheduled_dates ⇒ Object
178 179 180 |
# File 'app/models/effective/notification.rb', line 178 def scheduled_dates Array(self[:scheduled_dates]) - [nil, ''] end |
#scheduled_email? ⇒ Boolean
Only scheduled emails can have attached reports. Only scheduled emails can do Send Now
170 171 172 |
# File 'app/models/effective/notification.rb', line 170 def scheduled_email? scheduled? && audience_emails? end |
#send_now! ⇒ Object
Enqueues this notification to send right away. Only applies to scheduled_email? notifications
222 223 224 225 226 |
# File 'app/models/effective/notification.rb', line 222 def send_now! raise('expected to be persisted') unless persisted? NotificationJob.perform_later(id, force: true) true end |
#skip_once! ⇒ Object
Only applies to immedate? notifications Skips over one notification on the immediate notifications
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'app/models/effective/notification.rb', line 230 def skip_once! notified = 0 report.collection().find_each do |resource| print('.') # For logging assign_attributes(current_resource: resource) # Send the resource email build_notification_log(resource: resource, skipped: true).save! notified += 1 GC.start if (notified % 250) == 0 end touch end |
#to_email(resource) ⇒ Object
390 391 392 |
# File 'app/models/effective/notification.rb', line 390 def to_email(resource) audience == 'emails' ? audience_emails.presence : resource_emails_to_s(resource) end |
#to_s ⇒ Object
136 137 138 |
# File 'app/models/effective/notification.rb', line 136 def to_s subject.presence || model_name.human end |