pcgs 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/pcgs.rb +339 -0
  2. metadata +46 -0
@@ -0,0 +1,339 @@
1
+ require "hpricot"
2
+ require "open-uri"
3
+
4
+ class PCGS
5
+ class Wallet
6
+ attr_accessor :coins
7
+
8
+ attr_accessor :tables
9
+
10
+ def initialize(type=nil)
11
+ doc = PCGS.scrape("http://www.pcgs.com/prices/")
12
+ cols = doc.search("//div[@class='twocolumn']").first.search("//div[@class='col']")
13
+ boxes = cols.map{|col|col.search("//div").find_all{|div|div["class"].include?("box")}}.flatten
14
+ as = boxes.map{|box|box.search("//ul").first.search("//a")}.flatten
15
+ urls = as.map{|a|"http://www.pcgs.com"+a["href"]}
16
+ if not type.nil?
17
+ urls = as.find_all{|a|a.inner_html==type}.map{|a|"http://www.pcgs.com"+a["href"]}
18
+ end
19
+
20
+ @tables = []
21
+
22
+ urls.each do |url|
23
+ # p "Getting prices for #{url.split('title=')[1].gsub('+',' ')}..."
24
+ doc = PCGS.scrape(url)
25
+ ti = PCGS.table_info(doc)
26
+ ti.each do |t|
27
+ # p " Making table for #{t[:tab_title]}, #{t[:coin_grade_type]}."
28
+ @tables << PCGS.objectify_table_info(t)
29
+ end
30
+ end
31
+
32
+ @coins = []
33
+
34
+ self.coinify_tables
35
+
36
+ self
37
+ end
38
+
39
+ def coinify_tables
40
+ self.tables.each do |table|
41
+ self.coinify_table(table)
42
+ end
43
+ end
44
+
45
+ def coinify_table(table)
46
+ # p "Adding #{table.coin_type} coins to wallet..."
47
+ table.rows.each do |row|
48
+ if row.elements.first.is_a?(Integer)
49
+ if row.elements[1] != "Type"
50
+ pcgs_no = row.elements[0].to_i
51
+ description = row.elements[1]
52
+ design = row.elements[2]
53
+ grade_type = table.coin_grade_type
54
+ subtype = row.coin_subtype
55
+ type = table.coin_type
56
+ y = description.to_s.scan(/\d\d\d\d/)
57
+ if y.empty?
58
+ year = ""
59
+ elsif y.size == 1
60
+ year = y[0]
61
+ elsif y.size == 2
62
+ if description.gsub(" ","").include?(y[0]+"-"+y[1])
63
+ year = y[0]+"-"+y[1]
64
+ else
65
+ year = y[0]
66
+ end
67
+ end
68
+ mint_mark = description.to_s.scan(/\d\d\d\d\-[A-Z]/)[0][-1] rescue ""
69
+ row.elements[3..-1].each do |e|
70
+ i = row.elements.index(e)
71
+ grade = row.header_row.elements[i]
72
+ price = row.elements[i]
73
+ coin = self.add_coin(pcgs_no, description, design, grade, price, grade_type, subtype, type, year, mint_mark)
74
+ coin = coin.set_name
75
+ # p "Adding a #{coin.name} to the wallet."
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def add_coin(pcgs_no, description, design, grade, price, grade_type, subtype, type, year, mint_mark)
83
+ coin = PCGS::Coin.new(pcgs_no, description, design, grade, price, grade_type, subtype, type, year, mint_mark)
84
+ self.coins << coin
85
+ coin
86
+ end
87
+
88
+ def coins_like(str)
89
+ wallet = self
90
+ types = wallet.coins.find_all{|c|c.type_like?(str)}
91
+ names = wallet.coins.find_all{|c|c.name_like?(str)}
92
+ (types+names).flatten.uniq
93
+ end
94
+ end
95
+
96
+ class Table
97
+ attr_accessor :coin_type
98
+ attr_accessor :tab
99
+ attr_accessor :coin_grade_type
100
+ attr_accessor :rows
101
+ attr_accessor :doc
102
+
103
+ def initialize(coin_type, tab, coin_grade_type, doc)
104
+ @coin_type = coin_type
105
+ @tab = tab
106
+ @coin_grade_type = coin_grade_type
107
+ @rows = []
108
+ @doc = doc
109
+ end
110
+
111
+ def add_row(coin_subtype, elements, header_row)
112
+ row = PCGS::Row.new(coin_subtype, elements, header_row)
113
+ self.rows << row
114
+ row
115
+ end
116
+
117
+ def add_rows(rows)
118
+ if not rows.empty?
119
+ table = self
120
+ length = rows.first.size
121
+ subtype = table.coin_type
122
+ header_row = nil
123
+ hh = "<img src=\"images/expand.gif\" align=\"left\" class=\"expandcollapseimages\" border=\"0\" />"
124
+ rows.each do |row|
125
+ if row.size == length
126
+ if row.first.to_s.include?(hh)
127
+ row[0] = row[0].gsub(hh,"").to_i
128
+ end
129
+ if row.first.is_a?(Integer)
130
+ table.add_row(subtype, row, header_row)
131
+ elsif row.first[0..3] == "PCGS"
132
+ header_row = table.add_row("Header", row, nil)
133
+ end
134
+ elsif row.size == 1
135
+ if row.first.include?("Price Changes") && rows[1].first.include?("Collectors Corner")
136
+ subtype = row.first.split(",")[0]
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ class Row
145
+ attr_accessor :coin_subtype
146
+ attr_accessor :elements
147
+ attr_accessor :header_row
148
+
149
+ def initialize(coin_subtype, elements, header_row)
150
+ @coin_subtype = coin_subtype
151
+ @elements = elements
152
+ @header_row = header_row
153
+ end
154
+ end
155
+
156
+ class Coin
157
+ attr_accessor :pcgs_no
158
+ attr_accessor :description
159
+ attr_accessor :design
160
+ attr_accessor :grade
161
+ attr_accessor :price
162
+ attr_accessor :grade_type
163
+ attr_accessor :subtype
164
+ attr_accessor :type
165
+
166
+ attr_accessor :year
167
+ attr_accessor :mint_mark
168
+ attr_accessor :name
169
+
170
+ NAMES = {"halfcent" => ["halfcent", "half cent", "hapenny", "halfpenny", "half penny", "1/2cent", "1/2 cent", "1/2penny", "1/2 penny"], "cent" => ["cent", "penny", "1cent", "1 cent", "1penny", "1 penny", "onepenny", "one penny", "draped bust cent", "lincoln"], "twocent" => ["two cent", "two cents", "twocent", "twocents", "2cent", "2cents", "2 cent", "2 cents", "2penny", "2pennies", "2 penny", "2 pennies"], "threecent" => ["threecent", "threecents", "three", "three cent", "three cents", "3cent", "3cents", "3 cent", "3 cents"], "nickel" => ["nickel", "fivecent", "fivecents", "five cents", "5cent", "5cents"], "dime" => ["dime", "tencent", "tencents", "ten cents", "10cent", "10cents"], "quarter" => ["quarter", "twentyfivecent", "twentyfivecents", "twentyfive cents", "25cent", "25cents"], "halfdollar" => ["halfdollar", "half dollar"], "dollar" => ["dollar", "onedollar", "1dollar", "one dollar", "1 dollar"], "commemorative" => ["commemorative"], "silver" => ["silver", "silver coins", "silver eagles"], "gold" => ["gold", "gold coins"], "gold dollar" => ["gold dollar", "golddollar"], "silver dollar" => ["silver dollar", "silverdollar"], "silver commemorative" => ["silver commemorative", "silvercommemorative"], "gold commemorative" => ["gold commemorative", "goldcommemorative"], "templeton reid" => ["templeton", "templetonreid", "templeton reid"]}
171
+
172
+ def initialize(pcgs_no, description, design, grade, price, grade_type, subtype, type, year, mint_mark)
173
+ @pcgs_no = pcgs_no
174
+ @description = description
175
+ @design = design
176
+ @grade = grade
177
+ @price = price
178
+ @grade_type = grade_type
179
+ @subtype = subtype
180
+ @type = type
181
+ @year = year
182
+ @mint_mark = mint_mark
183
+ end
184
+
185
+ def type_like?(type)
186
+ type.include?(type)
187
+ end
188
+
189
+ def name_like?(name)
190
+ name.include?(name)
191
+ end
192
+
193
+ def set_name
194
+ coin = self
195
+ possible_names = []
196
+ PCGS::Coin::NAMES.values.each_index do |i|
197
+ v = PCGS::Coin::NAMES.values[i]
198
+ score = 0
199
+ v.each do |name|
200
+ n = coin.subtype.downcase.split(" ") & name.split(" ")
201
+ score += n.size
202
+ end
203
+ if score > 0
204
+ possible_names << [PCGS::Coin::NAMES.keys[i], score]
205
+ end
206
+ end
207
+ coin.name = possible_names.sort_by{|e|e[1]}.reverse.first.first
208
+ coin
209
+ end
210
+ end
211
+
212
+ def self.scrape(url)
213
+ open(URI(url)){|f|Hpricot(f)}
214
+ end
215
+
216
+ def self.table_from_url(url, n)
217
+ doc = PCGS.scrape(url)
218
+ table_from_doc(doc, n)
219
+ end
220
+
221
+ def self.table_from_doc(doc, n)
222
+ doc.search("//div[@id='blue-table']").search("//table")[n]
223
+ end
224
+
225
+ def self.table_info(doc)
226
+ coin_type = doc.to_s.scan(/<a href=\"\/prices\">Home<\/a>[^<]+</).first.gsub("<a href=\"/prices\">Home</a>&nbsp;&gt;&nbsp;","").split("\r")[0]
227
+ tabs = doc.search("//div[@id='blue-table']").search("//ul")
228
+ tab_titles = tabs.map{|t|get_tabs(t)}
229
+ tab_links = tabs.map{|tab|tab.search("//a").map{|a|a["href"]}}
230
+ t = []
231
+ (0..(tab_links.size-1)).each do |i|
232
+ tab_links[i].each_index do |j|
233
+ tab_title = tab_titles[i][j]
234
+ link = tab_links[i][j]
235
+ table = table_from_url(link, i)
236
+ coin_grade_type = ["MS","PR"][i]#doc.to_s.scan(/Copper Type Coins, \S\S/).map{|t|t.gsub("Copper Type Coins, ","")}[i]
237
+ t << {:coin_type => coin_type, :coin_grade_type => coin_grade_type, :tab_title => tab_title, :link => link, :table => table}
238
+ end
239
+ end
240
+ t
241
+ end
242
+
243
+ def self.get_trs(table)
244
+ table.search("//tr")
245
+ end
246
+
247
+ def self.get_tds(tr)
248
+ tr.search("//td")
249
+ end
250
+
251
+ def self.get_ths(tr)
252
+ tr.search("//th")
253
+ end
254
+
255
+ def self.strip_span(text)
256
+ if text.include?("<span")
257
+ text.scan(/<span[^>]*>/).each do |replace|
258
+ text = text.gsub(replace, " ") rescue text
259
+ end
260
+ text = text.gsub("</span>", "")
261
+ else
262
+ text
263
+ end
264
+ end
265
+
266
+ def self.strip_a(text)
267
+ if text.include?("<a")
268
+ text.scan(/<a[^>]*>/).each do |replace|
269
+ text = text.gsub(replace, " ") rescue text
270
+ end
271
+ text = text.gsub("</a>", "")
272
+ else
273
+ text
274
+ end
275
+ end
276
+
277
+ def self.strip_br(text)
278
+ text.gsub("<br />", "")
279
+ end
280
+
281
+ def self.strip_nbsp(text)
282
+ text.gsub("&nbsp;", " ")
283
+ end
284
+
285
+ def self.pick_element(text)
286
+ if text[-1] == "-"
287
+ text = text[0..-2]
288
+ elsif text[-2..-1] == " +"
289
+ text = text[0..-3]
290
+ elsif (text.scan(/[\d,]+/).size == 2) && (text.scan(/[^\d^,^ ]/) == [] )
291
+ text = text.scan(/[\d,]+/)[0]
292
+ end
293
+ text
294
+ end
295
+
296
+ def self.convert_to_integer(text)
297
+ if text.gsub(",","").to_i.to_s == text.gsub(",","")
298
+ text.gsub(",","").to_i
299
+ else
300
+ text
301
+ end
302
+ end
303
+
304
+ def self.get_rows(table)
305
+ trs = get_trs(table)
306
+ rows = []
307
+ trs.each do |tr|
308
+ tds = get_tds(tr)
309
+ tds = get_ths(tr) if tds.empty?
310
+ row = tds.map do |td|
311
+ if td.search("//span").empty?
312
+ r = td.inner_html
313
+ else
314
+ r = td.search("//span").first.inner_html
315
+ r = strip_span(r)
316
+ end
317
+ r = strip_a(r)
318
+ r = strip_br(r)
319
+ r = strip_nbsp(r)
320
+ r = r.strip
321
+ r = pick_element(r)
322
+ r = convert_to_integer(r)
323
+ end
324
+ rows << row
325
+ end
326
+ rows
327
+ end
328
+
329
+ def self.get_tabs(tab_data)
330
+ tab_data.search("//a").map{|a|a.inner_html}
331
+ end
332
+
333
+ def self.objectify_table_info(t)
334
+ table = PCGS::Table.new(t[:coin_type], t[:tab_title], t[:coin_grade_type], t[:table])
335
+ rows = PCGS.get_rows(t[:table]) rescue []
336
+ table.add_rows(rows)
337
+ table
338
+ end
339
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pcgs
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Benjamin Godlove
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-02-25 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ''
15
+ email: bgodlove88@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/pcgs.rb
21
+ homepage: http://github.com/brg8/pcgs
22
+ licenses:
23
+ - GNU
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 1.8.23
43
+ signing_key:
44
+ specification_version: 3
45
+ summary: Pull coin data from the PCGS website at http://pcgs.com/prices
46
+ test_files: []