aftermarket_research 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e4ecca47e251a96412c5eca41997580b197ee3f6
4
+ data.tar.gz: 15c0bb6ff8f9d4fc08a05e988e39797ad7f47a7f
5
+ SHA512:
6
+ metadata.gz: 113338a90aa8723ffd2792bc069ac97f6b86785d6867a94ed9f664075b992283f3588515c4d72931d14bf9a82cbb31079b475b44ea7fab9f3f69f8967d8e4750
7
+ data.tar.gz: e135e4ff8f0f141a584c9309b6d55ebbe934e0fff6a5e51dc6b4f12b6b3dbc68250b9524307d0d7557ffed23810b3d66cc15cc8cc5f1b3bffd988d10b73534fc
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'aftermarket_research'
data/bin/runner.rb ADDED
@@ -0,0 +1,4 @@
1
+ # require_relative "../config/environment.rb"
2
+ require_relative "../lib/environment.rb"
3
+
4
+ Runner.new.interface
@@ -0,0 +1,3 @@
1
+ require_relative 'environment.rb'
2
+
3
+ Runner.new.interface
data/lib/analyzer.rb ADDED
@@ -0,0 +1,32 @@
1
+ class Analyzer
2
+
3
+ attr_accessor :num_array
4
+
5
+ def initialize(num_array)
6
+ self.num_array = num_array
7
+ end
8
+
9
+ def mean
10
+ self.num_array.inject(&:+)/num_array.count
11
+ # [a, b, c].inject(&:+) # => ((a + b)+ c)+ d)
12
+ # [b, c, d].inject(a, &:+) # => ((a + b)+ c)+ d)
13
+ end
14
+
15
+ def median
16
+ sorted = self.num_array.sort!
17
+ sorted[sorted.count/2]
18
+ end
19
+
20
+ def mode
21
+ freq = self.num_array.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
22
+ self.num_array.max_by { |v| freq[v] }
23
+ end
24
+
25
+ def print_calculations
26
+ puts "Analysis complete, based on #{num_array.length} craigslist listings!"
27
+ puts "The mean price for your search is #{mean}"
28
+ puts "The median price for your search is #{median}"
29
+ puts "The mode price for your search is #{mode}"
30
+ end
31
+
32
+ end
@@ -0,0 +1,9 @@
1
+ require 'bundler/setup'
2
+ require 'open-uri'
3
+
4
+ Bundler.require
5
+
6
+ require_relative 'scraper.rb'
7
+ require_relative 'query.rb'
8
+ require_relative 'runner.rb'
9
+ require_relative 'analyzer.rb'
data/lib/query.rb ADDED
@@ -0,0 +1,26 @@
1
+ class Query
2
+
3
+ attr_accessor :keyword, :search_query, :min_price, :max_price
4
+
5
+ def parse(query_string)
6
+ query_array = query_string.split(" ")
7
+ self.keyword = query_array.shift
8
+ self.max_price = query_array.pop.to_i
9
+ self.min_price = query_array.pop.to_i
10
+ self.search_query = query_array.join(" ")
11
+ return self
12
+ end
13
+
14
+ def valid_query?
15
+ Runner.keywords.keys.include?(self.keyword) && self.search_query.is_a?(String) && self.max_price.is_a?(Integer) && self.min_price.is_a?(Integer)
16
+ end
17
+
18
+ def keyword_to_url
19
+ if keyword == "nyc"
20
+ ""
21
+ else
22
+ "/#{keyword}"
23
+ end
24
+ end
25
+
26
+ end
data/lib/runner.rb ADDED
@@ -0,0 +1,85 @@
1
+ class Runner
2
+
3
+ def self.keywords
4
+ {
5
+ "mnh" => "Manhattan",
6
+ "brk" => "Brooklyn",
7
+ "que" => "Queens",
8
+ "brx" => "The Bronx",
9
+ "stn" => "Staten Island",
10
+ "jsy" => "New Jersey",
11
+ "lgi" => "Long Island",
12
+ "wch" => "Westchester",
13
+ "fct" => "Fairfield",
14
+ "nyc" => "New York City",
15
+ }
16
+ end
17
+
18
+ def interface
19
+ on = true
20
+
21
+ while on do
22
+
23
+ puts "======================================="
24
+ puts "Hello! Welcome to AfterMarket Research."
25
+ puts
26
+ puts "Please enter your query in the following format:"
27
+ puts "[location keyword] [product query] [min price] [max price]"
28
+ puts
29
+ puts "(Type 'help' for more information, and type 'exit' to exit)"
30
+
31
+ raw_user_command = gets.chomp.downcase
32
+ query = Query.new.parse(raw_user_command)
33
+ # binding.pry
34
+ if query.valid_query?
35
+ puts "running search..."
36
+ puts
37
+ scraper = Scraper.new(query)
38
+ scraper.scrape
39
+ if scraper.product_rows.length > 0
40
+ analyzer = Analyzer.new(scraper.number_extraction)
41
+ analyzer.print_calculations
42
+ # puts
43
+ # puts "Would you like to see your search results? (Y/n)"
44
+ # to_web = gets.chomp.downcase
45
+ # if to_web == "y"
46
+ # binding.pry
47
+ # scraper.open_url
48
+ # end
49
+ else
50
+ puts "Sorry, your search returned no results."
51
+ end
52
+ elsif query.keyword == "help"
53
+ help
54
+ elsif query.keyword == "exit"
55
+ exit
56
+ on = false
57
+ else
58
+ puts "Invalid Query! Please try again. Hit 'enter' to continue."
59
+ gets
60
+ "Invalid Query"
61
+ end
62
+ end
63
+ end
64
+
65
+ def help
66
+ puts "Run a craigslist search by including four query terms, as follows:"
67
+ puts
68
+ puts "FIRST, the area key -- #{Runner.keywords} "
69
+ # area_array = keywords.each_with_object([]) {|key, value, array| array << "#{key}: #{value}"}
70
+ puts
71
+ puts "NEXT, your actual search. Feel free to format however you like, but please write it in quotes."
72
+ puts
73
+ puts "Finally, your minimum and maximum prices, as integers. These will be used to narrow the search."
74
+ puts
75
+ puts "Here is a sample search: \"brk 'macbook pro' 200 800\" "
76
+ puts
77
+ puts "Hit 'enter' to continue."
78
+ gets
79
+ end
80
+
81
+ def exit
82
+ puts "Thanks for using AfterMarket Research! Go get that surplus value!"
83
+ end
84
+
85
+ end
data/lib/scraper.rb ADDED
@@ -0,0 +1,33 @@
1
+ class Scraper
2
+
3
+ attr_accessor :query, :product_rows, :encoded_url
4
+
5
+ def initialize(query_object)
6
+ self.query = query_object
7
+ self.product_rows = []
8
+ end
9
+
10
+ def htmlify(string)
11
+ string.gsub(" ", "+")
12
+ end
13
+
14
+ def scrape
15
+ self.encoded_url = URI.encode("https://newyork.craigslist.org/search/sss#{query.keyword_to_url}?zoomToPosting=&catAbb=sss&query=#{htmlify(query.search_query)}&minAsk=#{query.min_price}&maxAsk=#{query.max_price}&sort=rel&excats=")
16
+ html = open(URI.parse(self.encoded_url)) # /sss?zo
17
+ cl_page = Nokogiri::HTML(html.read)
18
+ cl_page.css('div.content p.row').map do |product_row|
19
+ self.product_rows << product_row
20
+ end
21
+ end
22
+
23
+ def number_extraction
24
+ self.product_rows.map do |product_row|
25
+ product_row.css('span.l2 span.price').text[1..-1].to_i
26
+ end
27
+ end
28
+
29
+ def open_url # in development, not implemented
30
+ `open #{self.encoded_url}`
31
+ end
32
+
33
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aftermarket_research
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Kronovet
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ description: Research and analyze second-hand price information through Craiglist
28
+ email: kronovet@gmail.com
29
+ executables:
30
+ - aftermarket_research
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - bin/aftermarket_research
35
+ - bin/runner.rb
36
+ - lib/aftermarket_research.rb
37
+ - lib/analyzer.rb
38
+ - lib/environment.rb
39
+ - lib/query.rb
40
+ - lib/runner.rb
41
+ - lib/scraper.rb
42
+ homepage: http://rubygems.org/gems/aftermarket_research
43
+ licenses:
44
+ - GNU GPL V2
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.2.1
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Aftermarket Research from your Terminal
66
+ test_files: []