mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Add service objects for summarizing outcomes of standing order processing jobs
This commit is contained in:
43
lib/open_food_network/standing_order_summarizer.rb
Normal file
43
lib/open_food_network/standing_order_summarizer.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
require 'open_food_network/standing_order_summary'
|
||||
|
||||
# Used by for StandingOrderPlacementJob and StandingOrderConfirmJob to summarize the
|
||||
# result of automatic processing of standing orders for the relevant shop owners.
|
||||
module OpenFoodNetwork
|
||||
class StandingOrderSummarizer
|
||||
def initialize
|
||||
@summaries = {}
|
||||
end
|
||||
|
||||
def record_order(order)
|
||||
summary_for(order).record_order(order)
|
||||
end
|
||||
|
||||
def record_success(order)
|
||||
summary_for(order).record_success(order)
|
||||
end
|
||||
|
||||
def record_issue(type, order, message)
|
||||
summary_for(order).record_issue(type, order, message)
|
||||
end
|
||||
|
||||
def record_failure(order)
|
||||
line1 = "StandingOrderPlacementError: Cannot process order #{order.number} due to errors"
|
||||
line2 = "Errors: #{order.errors.full_messages.join(', ')}"
|
||||
Rails.logger.info("#{line1}\n#{line2}")
|
||||
record_issue(:failure, order, line2)
|
||||
end
|
||||
|
||||
def send_placement_summary_emails
|
||||
@summaries.values.each do |summary|
|
||||
StandingOrderMailer.placement_summary_email(summary).deliver
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def summary_for(order)
|
||||
shop_id = order.distributor_id
|
||||
@summaries[shop_id] ||= StandingOrderSummary.new(shop_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
37
lib/open_food_network/standing_order_summary.rb
Normal file
37
lib/open_food_network/standing_order_summary.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
module OpenFoodNetwork
|
||||
class StandingOrderSummary
|
||||
attr_reader :shop_id, :order_count, :success_count, :issues
|
||||
|
||||
def initialize(shop_id)
|
||||
@shop_id = shop_id
|
||||
@order_ids = []
|
||||
@success_ids = []
|
||||
@issues = {}
|
||||
end
|
||||
|
||||
def record_order(order)
|
||||
@order_ids << order.id
|
||||
end
|
||||
|
||||
def record_success(order)
|
||||
@success_ids << order.id
|
||||
end
|
||||
|
||||
def record_issue(type, order, message)
|
||||
issues[type] ||= []
|
||||
issues[type][order.id] = message
|
||||
end
|
||||
|
||||
def order_count
|
||||
@order_ids.count
|
||||
end
|
||||
|
||||
def success_count
|
||||
@success_ids.count
|
||||
end
|
||||
|
||||
def issue_count
|
||||
(@order_ids - @success_ids).count
|
||||
end
|
||||
end
|
||||
end
|
||||
96
spec/lib/open_food_network/standing_order_summarizer_spec.rb
Normal file
96
spec/lib/open_food_network/standing_order_summarizer_spec.rb
Normal file
@@ -0,0 +1,96 @@
|
||||
require 'open_food_network/standing_order_summarizer'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe StandingOrderSummarizer do
|
||||
let(:order) { create(:order) }
|
||||
let(:summarizer) { OpenFoodNetwork::StandingOrderSummarizer.new }
|
||||
|
||||
describe "#summary_for" do
|
||||
let(:order) { double(:order, distributor_id: 123) }
|
||||
|
||||
context "when a summary for the order's distributor doesn't already exist" do
|
||||
it "initializes a new summary object, and returns it" do
|
||||
expect(summarizer.instance_variable_get(:@summaries).count).to be 0
|
||||
summary = summarizer.send(:summary_for, order)
|
||||
expect(summary.shop_id).to be 123
|
||||
expect(summarizer.instance_variable_get(:@summaries).count).to be 1
|
||||
end
|
||||
end
|
||||
|
||||
context "when a summary for the order's distributor already exists" do
|
||||
let(:summary) { double(:summary) }
|
||||
|
||||
before do
|
||||
summarizer.instance_variable_set(:@summaries, { 123 => summary })
|
||||
end
|
||||
|
||||
it "returns the existing summary object" do
|
||||
expect(summarizer.instance_variable_get(:@summaries).count).to be 1
|
||||
expect(summarizer.send(:summary_for, order)).to eq summary
|
||||
expect(summarizer.instance_variable_get(:@summaries).count).to be 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "recording events" do
|
||||
let(:order) { double(:order) }
|
||||
let(:summary) { double(:summary) }
|
||||
before { allow(summarizer).to receive(:summary_for).with(order) { summary } }
|
||||
|
||||
describe "#record_order" do
|
||||
it "requests a summary for the order and calls #record_order on it" do
|
||||
expect(summary).to receive(:record_order).with(order).once
|
||||
summarizer.record_order(order)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#record_success" do
|
||||
it "requests a summary for the order and calls #record_success on it" do
|
||||
expect(summary).to receive(:record_success).with(order).once
|
||||
summarizer.record_success(order)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#record_issue" do
|
||||
it "requests a summary for the order and calls #record_issue on it" do
|
||||
expect(summary).to receive(:record_issue).with(:type, order, "message").once
|
||||
summarizer.record_issue(:type, order, "message")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#record_failure" do
|
||||
before do
|
||||
allow(order).to receive(:number) { "123" }
|
||||
allow(order).to receive(:errors) { double(:errors, full_messages: ["Some error"]) }
|
||||
allow(summarizer).to receive(:record_issue)
|
||||
end
|
||||
|
||||
it "sends error info to the rails logger" do
|
||||
expect(Rails.logger).to receive(:info)
|
||||
summarizer.record_failure(order)
|
||||
end
|
||||
|
||||
it "calls #record_issue on itself" do
|
||||
summarizer.record_failure(order)
|
||||
expect(summarizer).to have_received(:record_issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#send_placement_summary_emails" do
|
||||
let(:summary1) { double(:summary) }
|
||||
let(:summary2) { double(:summary) }
|
||||
let(:summaries) { { 1 => summary1, 2 => summary2 } }
|
||||
let(:mail_mock) { double(:mail, deliver: true) }
|
||||
|
||||
before do
|
||||
summarizer.instance_variable_set(:@summaries, summaries)
|
||||
end
|
||||
|
||||
it "sends a placement summary email for each summary" do
|
||||
expect(StandingOrderMailer).to receive(:placement_summary_email).twice { mail_mock }
|
||||
summarizer.send_placement_summary_emails
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
83
spec/lib/open_food_network/standing_order_summary_spec.rb
Normal file
83
spec/lib/open_food_network/standing_order_summary_spec.rb
Normal file
@@ -0,0 +1,83 @@
|
||||
require 'open_food_network/standing_order_summary'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe StandingOrderSummary do
|
||||
let(:summary) { OpenFoodNetwork::StandingOrderSummary.new(123) }
|
||||
|
||||
describe "#initialize" do
|
||||
it "initializes instance variables: shop_id, order_count, success_count and issues" do
|
||||
expect(summary.shop_id).to be 123
|
||||
expect(summary.order_count).to be 0
|
||||
expect(summary.success_count).to be 0
|
||||
expect(summary.issues).to be_a Hash
|
||||
end
|
||||
end
|
||||
|
||||
describe "#record_order" do
|
||||
let(:order) { double(:order, id: 37) }
|
||||
it "adds the order id to the order_ids array" do
|
||||
summary.record_order(order)
|
||||
expect(summary.instance_variable_get(:@order_ids)).to eq [order.id]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#record_success" do
|
||||
let(:order) { double(:order, id: 37) }
|
||||
it "adds the order id to the success_ids array" do
|
||||
summary.record_success(order)
|
||||
expect(summary.instance_variable_get(:@success_ids)).to eq [order.id]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#record_issue" do
|
||||
let(:order) { double(:order, id: 1) }
|
||||
|
||||
context "when no issues of the same type have been recorded yet" do
|
||||
it "adds a new type to the issues hash, and stores a new issue against it" do
|
||||
summary.record_issue(:some_type, order, "message")
|
||||
expect(summary.issues.keys).to include :some_type
|
||||
expect(summary.issues[:some_type][order.id]).to eq "message"
|
||||
end
|
||||
end
|
||||
|
||||
context "when an issue of the same type has already been recorded" do
|
||||
let(:existing_issue) { double(:existing_issue) }
|
||||
|
||||
before { summary.issues[:some_type] = [existing_issue] }
|
||||
|
||||
it "stores a new issue against the existing type" do
|
||||
summary.record_issue(:some_type, order, "message")
|
||||
expect(summary.issues[:some_type]).to include existing_issue
|
||||
expect(summary.issues[:some_type][order.id]).to eq "message"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#order_count" do
|
||||
let(:order_ids) { [1,2,3,4,5,6,7] }
|
||||
it "counts the number of items in the order_ids instance_variable" do
|
||||
summary.instance_variable_set(:@order_ids, order_ids)
|
||||
expect(summary.order_count).to be 7
|
||||
end
|
||||
end
|
||||
|
||||
describe "#success_count" do
|
||||
let(:success_ids) { [1,2,3,4,5,6,7] }
|
||||
it "counts the number of items in the success_ids instance_variable" do
|
||||
summary.instance_variable_set(:@success_ids, success_ids)
|
||||
expect(summary.success_count).to be 7
|
||||
end
|
||||
end
|
||||
|
||||
describe "#lissue_count" do
|
||||
let(:order_ids) { [1,3,5,7,9] }
|
||||
let(:success_ids) { [1,2,3,4,5] }
|
||||
|
||||
it "counts the number of items in order_ids that are not in success_ids" do
|
||||
summary.instance_variable_set(:@order_ids, order_ids)
|
||||
summary.instance_variable_set(:@success_ids, success_ids)
|
||||
expect(summary.issue_count).to be 2 # 7 & 9
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user