Skip to content

Resolve Problem Statement 1 and Problem Statement 2 #114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ D1, false, "", ""
D2, true, P1, 4500
```

## Check Solution 1

Run in console ```irb``` command.
Run in irb console this lines:
```
require_relative 'lib/services/deliveries_processor'

Services::DeliveriesProcessor.new.call
```

It should generate **result_1.csv** file with result.

## Problem Statement 2

Each partner specifies the **maximum capacity** they can serve, across all their deliveries in following manner:
Expand Down Expand Up @@ -114,3 +126,15 @@ D3, true, P1, 3900
To submit a solution, fork this repo and send a Pull Request on Github.

For any questions or clarifications, raise an issue on this repo and we'll answer your questions as fast as we can.

## Check Solution 2

Run in console ```irb``` command.
Run in irb console this lines:
```
require_relative 'lib/services/deliveries_processor_by_capacity'

Services::DeliveriesProcessorByCapacity.new.call
```

It should generate **result_2.csv** file with result.
43 changes: 43 additions & 0 deletions lib/parsers/csv/parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require_relative '../file_parser'
require 'csv'

module Parsers
module Csv
class Parser < Parsers::FileParser
def parse
CSV.foreach(file_name, parser_opts) do |row|
content << parsed_row(row)
end
content
end

private

def parser_opts
{
headers: !!opts[:headers],
col_sep: opts[:col_sep] || ','
}
end

def parsed_row(row)
opts[:schema].each_with_object({}) do |(field_name, field_params), h|
h[field_name] = format_field(row[field_params[:index]], field_params)
end
end

def format_field(field, field_params)
striped_field = field.strip

formated_field = case field_params[:type].to_s
when 'Integer'
striped_field.to_i
when 'Range'
Range.new(*striped_field.split(/\-/).map(&:to_i))
else
striped_field
end
end
end
end
end
52 changes: 52 additions & 0 deletions lib/parsers/csv/schemas.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module Parsers
module Csv
module Schemas
INPUT_SCHEMA = {
delivery_id: {
index: 0,
type: ::String
},
delivery_size: {
index: 1,
type: ::Integer
},
theatre_id: {
index: 2,
type: ::String
}
}.freeze
PARTNERS_SCHEMA = {
theatre_id: {
index: 0,
type: ::String
},
slab_size: {
index: 1,
type: ::Range
},
min_cost: {
index: 2,
type: ::Integer
},
gb_cost: {
index: 3,
type: ::Integer
},
partner_id: {
index: 4,
type: ::String
}
}.freeze
CAPACITY_SCHEMA = {
partner_id: {
index: 0,
type: ::String
},
capacity: {
index: 1,
type: ::Integer
}
}
end
end
end
16 changes: 16 additions & 0 deletions lib/parsers/file_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Parsers
class FileParser
attr_reader :file_name, :opts
attr_accessor :content

def initialize(file_name:, **opts)
@file_name = file_name
@opts = opts
@content = []
end

def parse
raise NotImplementedError
end
end
end
77 changes: 77 additions & 0 deletions lib/services/deliveries_processor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require_relative '../parsers/csv/parser'
require_relative '../parsers/csv/schemas'

module Services
class DeliveriesProcessor
INPUT_FILENAME = 'input.csv'
PARTNERS_FILENAME = 'partners.csv'
RESULT_FILENAME = 'result_1.csv'.freeze

attr_reader :input_parser, :partners_parser

def initialize(file_name = INPUT_FILENAME)
@input_parser = Parsers::Csv::Parser.new(
file_name: file_name,
schema: Parsers::Csv::Schemas::INPUT_SCHEMA
)
@partners_parser = Parsers::Csv::Parser.new(
file_name: PARTNERS_FILENAME,
schema: Parsers::Csv::Schemas::PARTNERS_SCHEMA,
headers: true
)
end

def call
delete_csv(RESULT_FILENAME)

deliveries.each do |delivery_row|
proposals_by_theatre = proposals_by_theatre(delivery_row, partners)
proposal = proposals_by_theatre.min_by { |delivery_row| delivery_row[:price] }

write_to_csv(delivery_row, proposal)
end
end

private

def deliveries
@deliveries ||= input_parser.parse
end

def partners
@partners ||= partners_parser.parse
end

def proposals_by_theatre(delivery_row, partners)
partners.each_with_object([]) do |partner_row, partner_row_with_price|
next if !proposal_available?(partner_row, delivery_row)

delivery_price = delivery_row[:delivery_size] * partner_row[:gb_cost]
delivery_price = partner_row[:min_cost] if delivery_price < partner_row[:min_cost]

partner_row_with_price << partner_row.merge(price: delivery_price)
end
end

def write_to_csv(delivery_row, partner_row)
CSV.open('result_1.csv', 'a+') do |csv|
csv << build_row(delivery_row, partner_row)
end
end

def build_row(delivery_row, partner_row)
return [delivery_row[:delivery_id], false, '', ''] if !partner_row

[delivery_row[:delivery_id], true, partner_row[:partner_id], partner_row[:price]]
end

def proposal_available?(partner_row, delivery_row)
partner_row[:theatre_id] == delivery_row[:theatre_id] &&
partner_row[:slab_size].include?(delivery_row[:delivery_size])
end

def delete_csv(file_name)
File.delete(file_name) if File.exists? file_name
end
end
end
97 changes: 97 additions & 0 deletions lib/services/deliveries_processor_by_capacity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
require_relative 'deliveries_processor'

module Services
class DeliveriesProcessorByCapacity < DeliveriesProcessor
CAPACITIES_FILENAME = 'capacities.csv'
RESULT_FILENAME = 'result_2.csv'.freeze

attr_reader :capacities_parser, :result

def initialize(file_name = INPUT_FILENAME)
super

@capacities_parser = Parsers::Csv::Parser.new(
file_name: CAPACITIES_FILENAME,
schema: Parsers::Csv::Schemas::CAPACITY_SCHEMA,
headers: true
)
@result = []
end

def call
delete_csv(RESULT_FILENAME)

deliveries.each do |delivery_row|
proposals_by_theatre = proposals_by_theatre(delivery_row, partners)
delivery_row[:proposals] = proposals_by_theatre.sort_by { |proposal| proposal[:price] }

result << delivery_row
end

process_proposals_by_capacity

result.each do |delivery|
write_to_csv(delivery)
end
end

private

def capacities
@capacities ||= capacities_parser.parse
end

def process_proposals_by_capacity
capacities.each do |hash|
partner_deliveries = result.select do |delivery|
delivery[:proposals].any? &&
delivery[:proposals][0][:partner_id] == hash[:partner_id]
end

next if partner_deliveries.empty?

sum_delivery_sizes = partner_deliveries.sum { |h| h[:delivery_size] }

if sum_delivery_sizes <= hash[:capacity]
next
else
partner_deliveries[0][:proposals].delete_if do |proposal|
proposal[:partner_id] == hash[:partner_id]
end

process_proposals_by_capacity
end
end
end

def proposals_by_theatre(delivery_row, partners_rows)
partners_rows.each_with_object([]) do |partner_row, partner_row_with_price|
next if !proposal_available?(partner_row, delivery_row)

delivery_price = delivery_row[:delivery_size] * partner_row[:gb_cost]
delivery_price = partner_row[:min_cost] if delivery_price < partner_row[:min_cost]

partner_row_with_price << partner_row.merge(price: delivery_price)
end
end

def write_to_csv(delivery)
CSV.open('result_2.csv', 'a+') do |csv|
csv << build_row(delivery)
end
end

def build_row(delivery)
return [delivery[:delivery_id], false, '', ''] if delivery[:proposals].empty?

best_proposal = delivery[:proposals][0]

[delivery[:delivery_id], true, best_proposal[:partner_id], best_proposal[:price]]
end

def proposal_available?(partner_row, delivery_row)
partner_row[:theatre_id] == delivery_row[:theatre_id] &&
partner_row[:slab_size].include?(delivery_row[:delivery_size])
end
end
end