Skip to content
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

Far Mar - Primary and (some) Optional Requirements #36

Open
wants to merge 35 commits into
base: jln/master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2f0b67c
Added self.all and self.find methods and specs for Market and Product…
noglows Oct 19, 2015
db8ebaa
Finished code/specs for all and find methods. Added vendor method to …
noglows Oct 20, 2015
d19bcfd
Updated the to-do list
noglows Oct 20, 2015
c1bdb53
Added the market method to Vendor class and added associated specs
noglows Oct 20, 2015
a39dfa9
Added self.by_market method for Vendor class
noglows Oct 20, 2015
a454df5
Added vendor method to Product class
noglows Oct 20, 2015
052352c
Added vendor and product methods to Sale class
noglows Oct 21, 2015
673450f
Added sales method to Vendor class
noglows Oct 21, 2015
d67d913
Added spec for Vendor sales method
noglows Oct 21, 2015
2d7ee1f
Added a product method to the Vendor class
noglows Oct 21, 2015
55ae21b
Added revenue method to Vendor class
noglows Oct 21, 2015
a398ec2
Added sales method to Product class
noglows Oct 21, 2015
8b4e86f
Added number_of_sales method to Product
noglows Oct 21, 2015
5fc02cf
Added the self.by_vendor method to the Product class
noglows Oct 21, 2015
0c3ba3e
Added the self.between method to the Sale class
noglows Oct 21, 2015
4f8fabb
Fixed the between method for Sale class
noglows Oct 21, 2015
cfd85ce
Rewrote all self.all methods to use class variables, significantly re…
noglows Oct 21, 2015
721bed5
Removed class variables and went with instance variables instead
noglows Oct 21, 2015
3a84755
Updated to-do list
noglows Oct 21, 2015
78808a4
Comments to Market,Product,Sale, Vendor classes
noglows Oct 21, 2015
a6afe39
Added the class variables back in. Oops.
noglows Oct 21, 2015
748afd3
Added products method to Market class
noglows Oct 21, 2015
839ede6
Added the preferred vendor method to Market class
noglows Oct 21, 2015
6f00dd8
Added worst_vendor method to Market class
noglows Oct 21, 2015
390d3e1
Setting up test data files
noglows Oct 22, 2015
1a38a49
Modified class files to take csv as an input
noglows Oct 22, 2015
88cd3da
Added self.search method to Market class
noglows Oct 22, 2015
0a0f2ea
Working preffered_vendor(date) method
noglows Oct 22, 2015
29864c8
Functioning worst_vendor(date) method in Market class
noglows Oct 22, 2015
53cd0e1
Added comments to all Class files
noglows Oct 23, 2015
e397b5f
Working most_revenue method for Product class
noglows Oct 23, 2015
6e1a346
Added most_revenue method to Vendor class
noglows Oct 23, 2015
4031ff1
Working most_items method for Vendor class
noglows Oct 23, 2015
023ae5d
Removed unnecessary files
noglows Oct 23, 2015
c3277d6
Restoring the markets.csv file I accidentally deleted
noglows Oct 23, 2015
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
9 changes: 9 additions & 0 deletions To-Do.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

# Improve #vendors spec - suspect this could be written more efficiently
# Rewrite the vendor by_market spec - using length but should be testing result accuracy
# Rewrite tests to take test data
# Investigate .nip
# accomodate multiple best and worst vendors
# Make Product.most_revenue(n) work for the whole dataset
# Make Vendor.most_revenue(n) work for the whole dataset
# Make Vendor.most_items(n) work for the whole dataset
2 changes: 2 additions & 0 deletions lib/far_mar.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require "csv"
require "time"
require "pry"
require "date"

require "./lib/far_mar/market"
require "./lib/far_mar/vendor"
Expand Down
196 changes: 195 additions & 1 deletion lib/far_mar/market.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,199 @@
module FarMar
class Market
#market_csv = CSV.read("support/markets.csv")
attr_accessor :id, :name, :markets_all, :all_vendors
def initialize(market_hash)
@id = market_hash[:id].to_i
@name = market_hash[:name]
@address = market_hash[:address]
@city = market_hash[:city]
@county = market_hash[:county]
@state = market_hash[:state]
@zip = market_hash[:zip]
end

# Returns a collection of all market instances, representing all the markets described in the CSV
def self.all(csv = "support/markets.csv")
# Only reload the CSV if @@markets is an empty array
@@markets ||= []
if @@markets == []
markets_csv = CSV.read(csv)
# Create a new market object for each row in the CSV
markets_csv.each do |id, name, address, city, county, state, zip|
hash = {:id => id, :name => name, :address => address, :city => city, :county => county, :state => state, :zip => zip}
market = FarMar::Market.new(hash)
@@markets.push(market)
end
end
# Return array of all market objects
return @@markets
end

# Returns an instance of Market where the value of the ID field matches the passed parameter
def self.find(id)
@@markets.find do |market|
market.id == id
end
end

# Returns a collection of Vendor instances that are associated with the market
def vendors
# Generate the vendor list
vendors = FarMar::Vendor.all
# Find the vendors with matching market ids
matched_vendors = vendors.find_all do |vendor|
vendor.market_id == self.id
end
return matched_vendors
end

# ----------- OPTIONAL REQUIREMENTS: Part I --------------------- #

# Returns a collection of product instances that are associated to the market though the FarMar::Vendor class
def products
products = []
# Find the vendors that correlate to the market in question
matched_vendors = self.vendors
# For each vendor, pull the product list and add to an array
matched_vendors.each { |vendor| products.push(vendor.products)}
products.flatten!
return products
end

# Returns a collection of Market instances where the market name or vendor name contain the search term
def self.search(search_term)
# initialize the matches array
match_search = []
# Generate all markets and vendors
markets = FarMar::Market.all("./support/markets.csv")
vendors = FarMar::Vendor.all("./support/vendors.csv")
# Check market names for matches. If a match is found, push it to the match_search array
markets.each do |market|
if market.name.downcase.include? search_term.downcase
match_search.push(market)
end
end
# Check vendor names for matches. Push any matches to the array.
vendors.each do |vendor|
if vendor.vendor_name.downcase.include? search_term.downcase
match_search.push(vendor.market)
end
end
return match_search
end

# Helper method to simplify preferred_vendor and worst_vendor methods (code is shared between the two)
# runs self.vendors only if it hasn't already been run
def matched_vendor
@all_vendors ||= []
if @all_vendors == []
@all_vendors = self.vendors
end
return @all_vendors
end

# Returns the vendor with the highest revenue (Can also take a date as an input to return the vendor with the highest revenue on a given date)
# date must be provided as year, month, day
# default value is nil - if a date is not entered, the function returns the best vendor for all days
def preferred_vendor(year = nil, month = nil, day = nil)
vendors = matched_vendor
sales_day = []
# If no date is provided, the vendor array is sorted and the vendor with the highest revenue is returned
if day == nil
vendors.sort_by! { |vendor| vendor.revenue }
return @all_vendors[-1]
# If a date is provided the method will return the vendor with the highest revenue for that date
else
# Create a DateTime object with the provided date
start_time = DateTime.new(year,month,day)
# end_time is one day later
end_time = start_time + 1

# Generate all sales
sales = FarMar::Sale.all
# Narrow sales down to only the sales for the day by iterating through sales and checking if the purchase_time falls into the day range (this could be rewritten to use the Sale.between method. It was causing a lot of issues so I just rewrote the function here)
sales.each do |sale|
if sale.purchase_time.day >= start_time.day && sale.purchase_time.day < end_time.day
sales_day.push(sale)
end
end

market_sales = []
# For each vendor, iterate through the sales for the day and see if the sale is associated with the vendor
vendors.each do |vendor|
sales_day.each do |sale|
if vendor.vendor_id == sale.vendor_id
market_sales.push(sale)
end
end
end
# Initizalize a new hash with a default value of 0
vendor_hash = Hash.new(0)
# Iterate through each matching sale and set the hash to use the vendor_id as the key and update the value to the sale amount. If you have multiple sales for a vendor, the purchase_amounts are summed in the value.
market_sales.each do |sale|
vendor_hash[sale.vendor_id] += sale.amount
end
# Sort the hash in revenue order (by value)
sorted = vendor_hash.sort_by { |vendor, rev| rev}
# Return the vendor associated with the key for the last item in sorted
return FarMar::Vendor.find(sorted[-1][0])
end
end

# Returns the vendor with the lowest revenue
# Can take a date as an input to return the vendor with the lowest revenue on a given date
# Works very simialrily to preferred_vendor - comments below detail the differences between the 2. All other functionality should be assumed to be the same.
def worst_vendor(year = nil, month = nil, day = nil)
vendors = matched_vendor
sales_day = []
if day == nil
vendors.sort_by! { |vendor| vendor.revenue }
return @all_vendors[0]
else
start_time = DateTime.new(year,month,day)
end_time = start_time + 1

sales = FarMar::Sale.all
sales.each do |sale|
if sale.purchase_time.day >= start_time.day && sale.purchase_time.day < end_time.day
sales_day.push(sale)
end
end
sales_day.sort_by!{ |sale| sale.sale_id }

market_sales = []

vendors.each do |vendor|
vendor_sales = []
sales_day.each do |sale|
if vendor.vendor_id == sale.vendor_id
vendor_sales.push(sale)
end
end
# Difference from preferred_vendor: Need to accomodate that there may be a vendor without a sale for the day. We need to return this vendor as the worst vendor.
# If after going through the sales for the day, the vendor_sales array is empty, push a string into the array.
if vendor_sales == []
vendor_sales.push("NoSales:#{vendor.vendor_id}")
end
market_sales.push(vendor_sales)
end
# Flatten creates a single array from an array of arrays
market_sales.flatten!

vendor_hash = Hash.new(0)
market_sales.each do |sale|
# If an item in the array is not a Sale, we know it is one of the strings we placed earlier (for a no sale situation)
if sale.class != FarMar::Sale
# split the string to get just the vendor_id and find the associated vendor object. Return this vendor as the worst vendor
sale = sale.split(':')[1].to_i
return FarMar::Vendor.find(sale)
# Otherwise the behavior is the same as preffered_vendor
else
vendor_hash[sale.vendor_id] += sale.amount
end
end
sorted = vendor_hash.sort_by { |vendor, rev| rev}
return FarMar::Vendor.find(sorted[0][0])
end
end
end
end
80 changes: 79 additions & 1 deletion lib/far_mar/product.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,83 @@
module FarMar
class Product
#product_csv = CSV.read("support/products.csv")
attr_accessor :product_id, :product_name, :vendor_id
def initialize(product_hash)
@product_id = product_hash[:id].to_i
@product_name = product_hash[:name]
@vendor_id = product_hash[:vendor_id].to_i
end

# Return a collection of all Product instances, representing all the products described in the CSV
def self.all(csv = "support/products.csv")
# Only reload the CSV if @products is empty array
@@products ||= []
if @@products == []
# Create a new Product object for each row in the CSV and add the products to an array
products_csv = CSV.read(csv)
products_csv.each do |id, name, vendor_id|
hash = {:id => id, :name => name, :vendor_id => vendor_id}
product = FarMar::Product.new(hash)
@@products.push(product)
end
end
return @@products
end

# Returns an instance of Product with the passed in ID
def self.find(id)
@@products.find { |product| product.product_id == id }
end

# Returns the FarMar::Vendor instance that is associated with the product
def vendor
FarMar::Vendor.find(self.vendor_id)
end

# Returns a collection for FarMar::Sale instances associated with the product
def sales(csv = "./support/sales.csv")
all_sales = FarMar::Sale.all(csv)
matched_sales = all_sales.find_all { |sale| sale.product_id == self.product_id }
return matched_sales
end

# Returns the number of times this product has been sold
def number_of_sales
return self.sales.length
end

# Returns all of the products with the given Vendor ID
def self.by_vendor(vendor_id)
all_products = FarMar::Product.all
matched_products = all_products.find_all { |product| product.vendor_id == vendor_id }
return matched_products
end

# Returns the top n product instances ranked by total revenue
# Written so that the method only runs on a partial version of the data. Needs to be optimized so it can run on the full data without taking forever
def self.most_revenue(n)
return_array = []
@@sales = []
@@products = []
products = FarMar::Product.all("./test_data/test_products.csv")
#products = FarMar::Product.all("./support/products.csv")
product_sales = Hash.new(0)
products.each do |product|
associated_sales = product.sales("./test_data/test_sales.csv")
#associated_sales = product.sales("./support/sales.csv")
revenue = 0
associated_sales.each do |sale|
revenue += sale.amount
end
product_sales[product.product_id] = revenue
end
sorted = product_sales.sort_by { |product, rev| rev }
sorted.reverse!
best_products = sorted[0...n]
best_products.each do |product|
return_array.push(FarMar::Product.find(product[0]))
end
return return_array
end

end
end
60 changes: 60 additions & 0 deletions lib/far_mar/sale.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,65 @@
module FarMar
class Sale
#sale_csv = CSV.read("support/sales.csv")
attr_accessor :sale_id, :vendor_id, :product_id, :amount, :purchase_time
def initialize(sale_hash)
@sale_id = sale_hash[:id].to_i
@amount = sale_hash[:amount].to_i
@purchase_time = DateTime.parse(sale_hash[:purchase_time])
@vendor_id = sale_hash[:vendor_id].to_i
@product_id = sale_hash[:product_id].to_i
end

# Returns a collection of Sale instances, representing all of the sales described in the CSV
def self.all(csv = "./support/sales.csv")
# Only reload the CSV is sales is empty array
@@sales ||= []
# Checking that the array doesn't contain test data - this seems to not be necessary
# if @@sales.length == 105
# @@sales = []
# end
if @@sales == []
sales_csv = CSV.read(csv)
sales_csv.each do |id, amount, purchase_time, vendor_id, product_id|
hash = {:id => id, :amount => amount, :purchase_time => purchase_time, :vendor_id => vendor_id, :product_id => product_id}
sale = FarMar::Sale.new(hash)
@@sales.push(sale)
end
end
return @@sales
end

# Returns an instance of Sale where the value of the ID field matches the passed parameter
def self.find(id)
@@sales.find do |sale|
sale.sale_id == id
end
end

# Returns the FarMar::Vendor instance associated with this sale
def vendor
FarMar::Vendor.find(self.vendor_id)
end

# Returns the FarMar::Product instance associated with this sale
def product
FarMar::Product.find(self.product_id)
end

# Returns a collection of FarMar::Sale objects where the purchase time is between the two given times
def self.between(beginning_time, end_time)
# generate all Sale objects
all_sales = FarMar::Sale.all
sales_between = []
# For each sale, check if the purchase time is between is between the giving beginning_time and end_time
all_sales.find_all do |sale|
if sale.purchase_time > beginning_time && sale.purchase_time < end_time
sales_between.push(sale)
end
end
# return a sorted list of sales
sales_between.sort_by! { |sale| sale.purchase_time }
return sales_between
end
end
end
Loading