pcgs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []