Minor change to oclc lookup heuristic.
[quanlib.git] / classify / lookup.rb
1 require 'erb'
2 require 'net/http'
3 require 'nokogiri'
4
5 require 'bookclass'
6 require 'classset'
7 require 'fastset'
8
9 class Lookup
10   def initialize
11     @class_set = ClassSet.new()
12     @fast_set = FastSet.new()
13   end
14  
15   def construct_url(params)
16     first = true
17     cmd = 'http://classify.oclc.org/classify2/Classify'
18   
19     params += [ ['summary', 'false' ] ]
20   
21     params.each do |tuple|
22       name, value = tuple
23       if (first)
24         cmd += '?'
25         first = false
26       else
27         cmd += '&'
28       end
29     cmd += name + '='
30       cmd += ERB::Util.url_encode(value)
31     end
32   
33     return cmd
34   end
35   
36   def isUpper?(c)
37       return /[[:upper:]]/.match(c)
38   end
39   
40   def lookup(author_grouping, pathname)
41     params = [ 
42         ['author', massage_author(author_grouping)],
43         ['title', massage_title(pathname)]
44       ]
45   
46     cmd = construct_url(params)
47     res = submit_request(cmd)
48   
49     doc = Nokogiri::XML(res.body)
50
51     if "4" == response_code(doc)
52       # Multiple matches; pick the first one and re-query
53       owi = doc.css("works work")[0]["owi"]
54   
55       params = [
56           ['owi', owi]
57         ]
58       cmd = construct_url(params)
59       res = submit_request(cmd)
60     
61       #puts res.body
62     
63       doc = Nokogiri::XML(res.body)
64     end
65   
66     if "2" != response_code(doc)
67       # Lookup failed.  Let's try shortening the title, if it's in multiple parts
68
69       #TODO TODO
70       puts "Lookup failed"
71       return nil
72     end
73
74     title = doc.css("classify editions edition")[0]["title"]
75   
76     info = BookClass.new(author_grouping, title)
77
78     author = doc.css("classify editions edition")[0]["author"]
79     info.author = author
80   
81     nodes = doc.css("classify recommendations ddc mostPopular")
82     if nil != nodes && nodes.length > 0
83       ddc = nodes[0]["sfa"]
84       info.ddc = ddc
85     end
86   
87     nodes = doc.css("classify recommendations lcc mostPopular")
88     if nil != nodes && nodes.length > 0
89       lcc = nodes[0]["sfa"]
90     end
91     info.lcc = lcc
92
93     headings = doc.css("classify recommendations fast headings heading")
94     headings.each do |heading|
95       #puts heading.inspect
96       id = heading['ident']
97       #puts 'ID: ' + id
98       descr = heading.content
99       #puts 'DESCR: ' + descr
100       info.add_fast(id)
101       @fast_set.add(id, descr)
102     end
103
104     info.filename = title
105
106     @class_set.ensure_contains!(info)
107
108     return info
109   end
110
111   def massage_author(input)
112       if nil == input
113         return nil
114       end
115   
116       reading_order = ""
117       input.each_char do |c|
118         if isUpper?(c) and (reading_order.length > 0)
119           reading_order += " "
120        end
121         reading_order += c
122       end
123   
124       return reading_order
125   end
126
127   def massage_title(pathname)
128     basename = File.basename(pathname, '.*')
129
130     basename.gsub!('_', ' ')
131     basename.gsub!('--', ': ')
132     basename.gsub!('-s ', "'s ")
133     basename.gsub!('s- ', "s' ")
134
135     return basename
136   end
137
138   def response_code(doc)
139     return doc.css("classify response")[0]["code"]
140   end
141
142   def save_state
143     @class_set.save_state()
144     @fast_set.save_state()
145   end
146
147   def submit_request(cmd)
148     puts ('GET ' + cmd)
149   
150     url = URI.parse(cmd)
151     req = Net::HTTP::Get.new(url.to_s)
152     res = Net::HTTP.start(url.host, url.port) {|http|
153       http.request(req)
154     }
155     return res
156   end
157 end
158