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
344 345 346 347 348 349 350 351 352 353 |
# File 'app/models/effective/notification.rb', line 344 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
189 190 191 192 193 |
# File 'app/models/effective/notification.rb', line 189 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
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
# File 'app/models/effective/notification.rb', line 396 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
173 174 175 |
# File 'app/models/effective/notification.rb', line 173 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
414 415 416 417 418 419 420 421 |
# File 'app/models/effective/notification.rb', line 414 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
215 216 217 |
# File 'app/models/effective/notification.rb', line 215 def disable! update!(enabled: false) end |
#email_template ⇒ Object
181 182 183 |
# File 'app/models/effective/notification.rb', line 181 def email_template :notification # We always use this email template end |
#email_template_variables ⇒ Object
185 186 187 |
# File 'app/models/effective/notification.rb', line 185 def email_template_variables assigns_for().keys end |
#enable! ⇒ Object
211 212 213 |
# File 'app/models/effective/notification.rb', line 211 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
327 328 329 330 331 332 333 334 335 336 337 |
# File 'app/models/effective/notification.rb', line 327 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
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'app/models/effective/notification.rb', line 357 def notifiable_immediate?(resource:, date: nil) raise('expected an immediate? notification') unless immediate? email = resource_emails_to_s(resource) return false if email.blank? 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
203 204 205 |
# File 'app/models/effective/notification.rb', line 203 def notifiable_rows_count report.collection().select { |resource| notifiable?(resource) }.count if report end |
#notifiable_scheduled?(date: nil) ⇒ Boolean
376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'app/models/effective/notification.rb', line 376 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
339 340 341 342 |
# File 'app/models/effective/notification.rb', line 339 def notifiable_tomorrow?(resource) date = Time.zone.now.beginning_of_day.advance(days: 1) notifiable?(resource, date: date) end |
#notifiable_tomorrow_rows_count ⇒ Object
207 208 209 |
# File 'app/models/effective/notification.rb', line 207 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
250 251 252 |
# File 'app/models/effective/notification.rb', line 250 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
269 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 |
# File 'app/models/effective/notification.rb', line 269 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
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'app/models/effective/notification.rb', line 304 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.
255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'app/models/effective/notification.rb', line 255 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
195 196 197 |
# File 'app/models/effective/notification.rb', line 195 def renderer view_context || nil # This isn't ideal end |
#rows_count ⇒ Object
199 200 201 |
# File 'app/models/effective/notification.rb', line 199 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
177 178 179 |
# File 'app/models/effective/notification.rb', line 177 def scheduled_dates Array(self[:scheduled_dates]) - [nil, ''] end |
#scheduled_email? ⇒ Boolean
Only scheduled emails can have attached reports.
169 170 171 |
# File 'app/models/effective/notification.rb', line 169 def scheduled_email? scheduled? && audience_emails? end |
#send_now! ⇒ Object
Enqueues this notification to send right away. Only applies to scheduled_email? notifications
221 222 223 224 225 |
# File 'app/models/effective/notification.rb', line 221 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
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'app/models/effective/notification.rb', line 229 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
389 390 391 |
# File 'app/models/effective/notification.rb', line 389 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 |