class Chooser
Copyright 2014 Maxine Red <maxine@furfind.net>
This file is part of toy-chooser.
toy-chooser is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
toy-chooser is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with toy-chooser. If not, see <www.gnu.org/licenses/>.
Public Class Methods
# File lib/chooser.rb, line 28 def initialize @db_file = File.expand_path("~/.toys.sqlite") if !File.exist?(@db_file) then database_create end end
Public Instance Methods
Add a toy to the 'owned' list. Toys are in the format sku/size.
# File lib/chooser.rb, line 44 def add(toys) toys.each do |toy| sku, size = toy.split("/") sku, size = SQLite3::Database.quote(sku), SQLite3::Database.quote(size.upcase) SQLite3::Database.new(@db_file) do |db| query = "SELECT id, name FROM toys WHERE sku = '#{sku}'" query += " AND size = '#{size}';" id, name = db.execute(query).flatten if !id then $stderr.puts "Unknown toy or size \"#{toy}\"." next end query = "INSERT INTO owned (sku, size, toy_id) VALUES" query += " ('#{sku}','#{size}',#{id});" db.execute(query) puts "Added \"#{name}\" (#{size}) to owned list." end end end
# File lib/manufacturer/bad-dragon.rb, line 21 def fetch_bd net = Net::HTTP.new("bad-dragon.com",80) toys = JSON.parser.new(net.get("/products/getproducts").body).parse toys.reject!{|c|c["cat_id"].to_i!=1} toys = toys.sort{|t1,t2|t1["release_date"].to_i <=> t2["release_date"].to_i} toys.map!{|c|c["link_url"].sub(/.+bad-dragon.com/,"")} toys.each do |t| body = net.get(t).body html = Nokogiri::HTML.parse(body) id = html.css("span[itemprop=productID]").first.text name = html.css("span[itemprop=model]").first.text sizes = html.css("table.divided th").map{|th|th.text} sizes.shift # Just drop the first item. table = html.css("table.divided tr").map do |tr| tr.css("td").map{|td|td.text} end table.shift sql_query = "INSERT INTO toys"; sizes.count.times do |i| size = sizes[i][0,1].sub("E","XL") size = "XS" if sizes[i] == "Mini" query = Hash.new query.store("sku",id) query.store("name",SQLite3::Database.quote(name)) query.store("size",size) price = html.css("#size_inputs label").map do |x| if x.text.match(/^#{sizes[i]}/i) then x.css("i").text.sub("$","").to_i end end.compact.first price = html.css("span[itemprop=price]").first.text.to_i if !price query.store("price",price) table.each do |t| next if t[0].match(/thinnest|smallest/i) # We look only for biggest measures. col = t[0].downcase.sub(/meter of /,"_").sub(/umference of /,"_") col.gsub!(" ","_") col.gsub!(/_\(.+\)/,"") col = remove_aliases(col) inches = (t[i+1].to_f*100).round.to_i query.store(col,inches) end if i == 0 then sql_query += " (#{query.keys.join(",")}) VALUES\n" end sql_query += " ('#{query.values.join("','")}'),\n" end sql_query[-2,2] = ";" SQLite3::Database.new(@db_file) do |db| begin db.execute(sql_query) rescue p sql_query raise end end puts "Updated \"#{name}\" successfully." end end
List existing toys.
# File lib/chooser.rb, line 103 def list(toy_list="toys") toy_list = toy_list.first toy_list = "toys" unless toy_list if !["toys","owned"].include?(toy_list) then $stderr.puts "No such list." abort end SQLite3::Database.new(@db_file) do |db| draw_list(db.execute("SELECT sku, size FROM #{toy_list};")) end end
# File lib/manufacturer/bad-dragon.rb, line 80 def remove_aliases(column) column = column.sub("usuable","usable").sub("avg._","") column = column.sub(/(thickest|largest)_part_of_/,"") column = column.sub(/_base/,"") # Other common aliases column = column.sub("tip","head").sub("middle","shaft").sub("bottom","knot") column = column.sub("shaft_ridges","knot").sub("medial_ring","knot") column = column.sub("bulb","knot") return column end
Show measurements of toys.
# File lib/chooser.rb, line 64 def show(toys) toys.each do |toy| sku, size = toy.split("/") sku, size = SQLite3::Database.quote(sku), SQLite3::Database.quote(size.to_s.upcase) SQLite3::Database.new(@db_file) do |db| query = "SELECT sku, size, price, dia_head, dia_shaft, dia_knot, " query += "circ_head, circ_shaft, circ_knot, usable_length, " query += "total_length FROM toys WHERE sku = '#{sku}';" draw_show(db.execute(query)) end end end
Suggests toys for future purchases guessed from what is owned already.
# File lib/chooser.rb, line 77 def suggest(noargs=[]) i, sizes = 0, Array.new(7) {0} ranges = [0.5, 0.4, 0.3, 0.6, 0.5, 0.4, 0.9] cols = ["dia_head","dia_shaft","dia_knot","circ_head","circ_shaft", "circ_knot","usable_length"] SQLite3::Database.new(@db_file) do |db| query = "SELECT #{cols.join(", ")} FROM toys, owned " query += "WHERE toys.id = owned.toy_id;" db.execute(query) do |row| row.each_with_index do |c,id| sizes[id] += c.to_i end i += 1 end sizes = sizes.map.with_index do |x,id| avg = (x/i.to_f).round.to_i line = "((#{cols[id]} >= #{(avg*(1-ranges[id])).round.to_i} " line += "AND #{cols[id]} <= #{(avg*(1+ranges[id])).round.to_i}) " line += "OR #{cols[id]} IS NULL)" line end query = "SELECT sku, size FROM toys WHERE #{sizes.join(" AND ")};" draw_list(db.execute(query)) end end
Update database for given manufacturer.
# File lib/chooser.rb, line 35 def update(manu) case manu.first when "bad-dragon" then fetch_bd else $stderr.puts "Manufacturer unkown.","Known manufacturers are: bad-dragon" abort end end
Private Instance Methods
# File lib/chooser.rb, line 166 def database_create SQLite3::Database.new(@db_file) do |db| db.execute("CREATE TABLE toys ( id INTEGER PRIMARY KEY, sku VARCHAR(127), name VARCHAR(255), size VARCHAR(3), price INTEGER, dia_head INTEGER, dia_shaft INTEGER, dia_knot INTEGER, circ_head INTEGER, circ_shaft INTEGER, circ_knot INTEGER, usable_length INTEGER, total_length INTEGER );") db.execute("CREATE TABLE owned ( id INTEGER PRIMARY KEY, sku VARCHAR(127), size VARCHAR(3), toy_id INTEGER REFERENCES toys(id) );") end end
# File lib/chooser.rb, line 115 def draw_list(sql_list) toys = Hash.new sizes = ["XS","S ","M ","L ","XL","O "] deli = "+-"+"-"*15+"-+"+"----+"*sizes.count+""+"\n" print deli puts "| #{" "*15} | #{sizes.join(" | ")} |" print deli sql_list.each do |t| if !toys.has_key?(t[0]) then toys.store(t[0],[t[1]]) else toys[t[0]] << t[1] end end toys.each do |k,v| cols = sizes.map do |c| v.include?(c.sub(" ","")) ? c : " " end puts "| #{k.pad(15)} | #{cols.join(" | ")} |" end print deli end
# File lib/chooser.rb, line 137 def draw_show(sql_list) toys = Hash.new sizes = [" XS "," S "," M "," L "," XL "," O "] measurements = ["Price", "Dia. of head", "Dia. of shaft", "Dia. of speccial", "Circ. of head", "Circ. of shaft", "Circ. of special", "Usable length", "Total length"] deli = "+-"+"-"*15+"-+"+"---------+"*sizes.count+""+"\n" print deli puts "| #{sql_list.first.first.pad(15)} | #{sizes.join(" | ")} |" print deli sql_list.map{|c|c.shift;c}.each do |t| toys.store(t.shift,t) end measurements.each_with_index do |m,id| cols = sizes.map do |c| c.gsub!(" ","") toys[c] ? toys[c][id].to_i : 0 end if m == "Price" then cols.map!{|c|c.pad(4," ").sub(/\s(\d)/,"$\\1")+".00"} else cols.map!{|c|(c/100.0).pad(7," ")} end puts "| #{m.pad(15)} | #{cols.join(" | ")} |" print deli if m == "Price" end print deli end