Class: ProblematicDeliveryDigestWorker
- Inherits:
-
Object
- Object
- ProblematicDeliveryDigestWorker
- Includes:
- Sidekiq::Job
- Defined in:
- app/workers/problematic_delivery_digest_worker.rb
Overview
Daily roll-up of every currently-flagged problematic delivery, mailed to
orders@ via ProblematicDeliveryDigestMailer.
Split of responsibilities with ProblematicDeliverySweep: the sweep runs
hourly and owns DETECTION — it decides what is problematic and records each
first sighting as an Events::ProblematicDeliveryDetected. This worker owns
NOTIFICATION and runs once a day, reading the sweep's current verdict rather
than keeping its own.
Why the split: the sweep used to email on each detection, and that put a
per-incident message in a sales rep's inbox. Sales asked off it on
2026-08-06 — "we lack the time to manage delayed shipments; this is an
operational and logistical matter rather than a sales responsibility" — and
a per-incident email can't answer the reader's only question, which is what
changed since yesterday. Reading ProblematicDeliverySweep.flagged fresh
each morning also means anything delivered or dismissed overnight simply
isn't in the message, with no state of our own to expire.
Scheduled daily via sidekiq-scheduler — see
config/sidekiq_production_schedule.yml and
config/sidekiq_staging_schedule.yml.
Constant Summary collapse
- NEW_SINCE =
A row counts as new when its detection event landed inside this window —
one digest interval, so "new" means "you have not seen this in a digest
before". Slightly wider than 24h so a late-running job doesn't silently
drop a row's NEW marker. 25.hours
Class Method Summary collapse
-
.rows ⇒ Array<Hash>
Every flagged delivery, critical first and newest-scan first within a severity, decorated with what the digest needs to render.
Instance Method Summary collapse
-
#perform ⇒ Object
Runs the job.
Class Method Details
.rows ⇒ Array<Hash>
Every flagged delivery, critical first and newest-scan first within a
severity, decorated with what the digest needs to render.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'app/workers/problematic_delivery_digest_worker.rb', line 57 def rows recent = recently_detected_shipment_ids ProblematicDeliverySweep.flagged.map do |shipment, reason| events = ProblematicDeliverySweep.scans_for(shipment) { shipment: shipment, reason: reason, severity: ProblematicDeliverySweep.severity(shipment, reason), new: recent.include?(shipment.id), last_event: events.max_by(&:occurred_at) } end.sort_by { |row| [row[:severity] == :critical ? 0 : 1, -row[:last_event]&.occurred_at.to_i] } end |
Instance Method Details
#perform ⇒ Object
Runs the job.
38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'app/workers/problematic_delivery_digest_worker.rb', line 38 def perform rows = self.class.rows if rows.empty? Rails.logger.info '[ProblematicDeliveryDigestWorker] nothing flagged — no digest sent' return end Rails.logger.info "[ProblematicDeliveryDigestWorker] #{rows.size} flagged " \ "(#{rows.count { |r| r[:severity] == :critical }} critical, " \ "#{rows.count { |r| r[:new] }} new)" ProblematicDeliveryDigestMailer.digest(rows).deliver_now end |