# # googleplot - a tool to plot a graph of Google's results count # # Copyright (C) 2003 Satoru Takabayashi # All rights reserved. # This is free software with ABSOLUTELY NO WARRANTY. # # You can redistribute it and/or modify it under the terms of # the GNU General Public License version 2. # require 'mingplot' require 'net/http' require 'rexml/document' module AmazonPlotCommon module_function def get_currency (locale) currency_table = { "ca" => "Canada Doller", "de" => "Euro", "fr" => "Euro", "jp" => "Yen", "uk" => "Pound", "us" => "Doller", } (currency_table[locale] or "??") end def get_web_host_name (locale) web_host_table = { "jp" => "www.amazon.co.jp", "fr" => "www.amazon.fr", "de" => "www.amazon.de", "uk" => "www.amazon.co.uk", "ca" => "www.amazon.ca", "us" => "www.amazon.com", } (web_host_table[locale] or "www.amazon.com") end def get_xml_host_name (locale) xml_host_table = { "jp" => "xml.amazon.co.jp", "fr" => "xml-eu.amazon.com", "de" => "xml-eu.amazon.com", "uk" => "xml-eu.amazon.com", "ca" => "xml.amazon.com", "us" => "xml.amazon.com", } (xml_host_table[locale] or "xml.amazon.com") end end class AmazonPlotInfo < MingPlotInfoBase include AmazonPlotCommon include Commify def initialize (info = {}) @info = info end def title @info[:title] end def key @info[:asin] end def count @info[:rank] end def locale (@info[:locale] or "jp") end def currency (get_currency(locale) or "Yen") end def method_missing (symbol, *args) ((@info and @info[symbol]) or nil) end def add_list (g, text, name, value, commify_p = false, price_p = false) if value text << g.li { g.strong { name } + if commify_p if price_p commify(value) + " " + get_currency(self.locale) else commify(value) end else value.to_s end } end end def hack_image_url (url) if self.catalog == "Music" and self.features and /Import/.match(self.features) and !/Original recording/.match(self.features) url.sub(/(\.)(09)(\.\w+\.jpg)$/, '\101\3') else url end end def to_html g = SimpleHtmlGenerator.new list = g.ul(:class => "chart_info") { text = "" add_list(g, text, "Author: ", self.author) add_list(g, text, "Artist: ", self.artist) name = if self.catalog == "Book" "Publisher: " else "Manufacturer: " end add_list(g, text, name, self.manufacturer) add_list(g, text, "Price: ", self.price, true, true) add_list(g, text, "Used Price: ", self.used_price, true, true) add_list(g, text, "Third Party Price: ", self.third_party_price, true, true) add_list(g, text, "Sales Rank: ", self.rank, true) add_list(g, text, "Rating: ", self.rating, true) add_list(g, text, "Number of Reviews: ", self.nreviews, true) add_list(g, text, "Release Date: ", self.release_date) add_list(g, text, "ASIN: ", self.asin) text } if self.image_url g.table(:class => "chart_info") { g.tr { g.td { g.a(:href => self.search_url) { g.img(:src => hack_image_url(self.image_url), :alt => "", :class => "chart_info") } } + g.td { list } } } else list end end def search_url sprintf("http://%s/exec/obidos/ASIN/%s/%s", get_web_host_name(locale), key, (associate_id or "")) end end class AmazonPlotSearcher include AmazonPlotCommon def initialize (config = {}) @token = config[:token] @locale = config[:locale] @id = config[:id] @service_host = get_xml_host_name(@locale) raise "token must be specified" unless @token raise "locale must be specified" unless @locale end private def get_number (doc, path) doc.elements.to_a(path).each {|element| if m = /([\d,.]+)/u.match(element.text) number = m[1].gsub(/,/, "") number = if /\./.match(number) number.to_f else number.to_i end return number end } return nil end def get_string (doc, path, join_p = false) if join_p texts = doc.elements.to_a(path).map {|element| element.text } return (if texts.empty? then nil else texts.join(", ") end) else doc.elements.to_a(path).each {|element| return element.text } end return nil end def search_amazon (key) response = nil Net::HTTP.start(@service_host, 80) {|http| query = CGI.escape(key) format = "/onca/xml3?t=%s&dev-t=%s&AsinSearch=%s&type=%s&f=%s&locale=%s" path = sprintf(format, "webservices-20", @token, query, "heavy", "xml", @locale) response = http.get2(path) } REXML::Document.new(response.body) end def collect_info (doc) prefix = "/ProductInfo/Details/" info = Hash.new info[:asin] = get_string(doc, prefix + "Asin") info[:rank] = get_number(doc, prefix + "SalesRank") info[:price] = get_number(doc, prefix + "OurPrice") info[:used_price] = get_number(doc, prefix + "UsedPrice") info[:third_party_price] = get_number(doc, prefix + "ThirdPartyNewPrice") info[:release_date] = get_string(doc, prefix + "ReleaseDate") info[:rating] = get_number(doc, prefix + "Reviews/AvgCustomerRating") info[:nreviews] = get_number(doc, prefix + "Reviews/TotalCustomerReviews") info[:title] = get_string(doc, "/ProductInfo/Details/ProductName") info[:author] = get_string(doc, prefix + "Authors/Author", true) info[:artist] = get_string(doc, prefix + "Artists/Artist", true) info[:manufacturer] = get_string(doc, prefix + "Manufacturer") info[:catalog] = get_string(doc, prefix + "Catalog") info[:image_url] = get_string(doc, prefix + "ImageUrlMedium") info[:features] = get_string(doc, prefix + "Features/Feature", true) info[:locale] = @locale info[:associate_id] = @id return info end public def search (key) doc = search_amazon(key) if get_string(doc, "ProductInfo/ErrorMsg") # error occurred return nil else info = collect_info(doc) AmazonPlotInfo.new(info) end end end class AmazonPlotStat < MingPlotStatBase include AmazonPlotCommon def initialize (key, config ={}) super(key, config) @locale = config[:locale] @token = config[:token] @id = config[:id] raise "token must be specified" unless @token raise "locale must be specified" unless @locale end private def draw_chart_internal (data, file_name) currency = get_currency(@locale) chart = MingChart.new(:flash_font => flash_font, :x_time_scale => true, :left_y_label => "Rank", :right_y_label => "Price (#{currency})", :y_axes => [:left, :right, :right, :right], :left_y_direction => :upside_down, :line_titles => ["Sales Rank", "Price", "Used Price", "Third Party Price"], :grid => true) data = data.map {|time, info| [time, info.rank, info.price, info.used_price, info.third_party_price]} chart.add_data(data, 1) chart.add_data(data, 2) chart.add_data(data, 3) chart.add_data(data, 4) chart.draw chart.save(file_name) end public def get_info search = AmazonPlotSearcher.new(:token => @token, :locale => @locale, :id => @id) search.search(key) end def service_name "amazon" end end