pyradise 0.2.1 → 0.3.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.
- data/VERSION +1 -1
- data/bin/pyradise +14 -5
- data/lib/pyradise/cli.rb +164 -0
- data/lib/pyradise/i18n.rb +28 -0
- data/lib/pyradise/product.rb +3 -1
- data/lib/pyradise.rb +24 -135
- data/pyradise.gemspec +7 -4
- metadata +4 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/bin/pyradise
CHANGED
@@ -15,7 +15,6 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
15
15
|
require 'pyradise'
|
16
16
|
include Pyradise
|
17
17
|
|
18
|
-
OPTIONS = {}
|
19
18
|
MANDATORY_OPTIONS = %w( )
|
20
19
|
|
21
20
|
parser = OptionParser.new do |opts|
|
@@ -26,11 +25,21 @@ Usage: #{File.basename($0)} command [args]
|
|
26
25
|
|
27
26
|
Options are:
|
28
27
|
|
29
|
-
fetch
|
30
|
-
list/search
|
31
|
-
|
28
|
+
fetch - Start/refresh database.
|
29
|
+
list/search - List and filter items
|
30
|
+
-o --order - Order by: name, price, store.
|
31
|
+
info/show - More info about an item
|
32
|
+
clear - Wipe out the database
|
33
|
+
|
34
|
+
Common usage:
|
35
|
+
|
36
|
+
pyradise fetch
|
37
|
+
" list mb i7 - Searches for Intel i7 mobos.
|
38
|
+
" list mem -o name - Searches for 'mem' ordered by name.
|
39
|
+
" info <sid> - Show price history of the item.
|
32
40
|
|
33
41
|
BANNER
|
42
|
+
opts.on("-o", "--order ORDER", String, "Table order" ) { |order| Options[:order] = order }
|
34
43
|
opts.separator ""
|
35
44
|
opts.parse!(ARGV)
|
36
45
|
end
|
@@ -39,5 +48,5 @@ if ARGV.empty?
|
|
39
48
|
puts parser.banner
|
40
49
|
exit
|
41
50
|
else
|
42
|
-
Pyradise.run! ARGV
|
51
|
+
Pyradise::Cli.run! ARGV
|
43
52
|
end
|
data/lib/pyradise/cli.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
module Pyradise
|
2
|
+
class Cli
|
3
|
+
|
4
|
+
class << self
|
5
|
+
include I18n
|
6
|
+
|
7
|
+
def run! comm
|
8
|
+
if respond_to? comm[0]
|
9
|
+
send(*comm)
|
10
|
+
else
|
11
|
+
puts "Can't do that..."
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def fetch
|
17
|
+
create from_stores
|
18
|
+
end
|
19
|
+
|
20
|
+
def list(*query)
|
21
|
+
p query
|
22
|
+
t = Time.now
|
23
|
+
w = terminal_size[0]
|
24
|
+
s = w - 36
|
25
|
+
puts "\n#{t(:search)} #{'"' + green(query.join(" ")) + '"' if query[0]}... #{t(:order)}: #{green(Options[:order])}"
|
26
|
+
# puts "_" * w
|
27
|
+
puts
|
28
|
+
prods = Product.filter(:name.like("%#{query.join("%")}%")).order(Options[:order])
|
29
|
+
prods.each_with_index do |prod, i|
|
30
|
+
name = prod.name.length > s ? prod.name[0..s] + ".. " : prod.name + " "
|
31
|
+
for q in query
|
32
|
+
b = i % 2 == 0 ? "\e[0m\e[2m" : "\e[0m\e[0m"
|
33
|
+
replaces = name.scan(/#{q}/i).length
|
34
|
+
name.gsub!(/#{q}/i, "\e[32m\\0#{b}")
|
35
|
+
name += "." until name.length > w - 23
|
36
|
+
end
|
37
|
+
total_price = prod.price * Options[:rate] * Options[:tax]
|
38
|
+
out = sprintf("%-6s | %-6s | %-#{s}s %-3d | R$ %s", prod.store, prod.sid, name, prod.price, yellow(total_price.to_i))
|
39
|
+
|
40
|
+
puts i % 2 == 0 ? bold(out) : out
|
41
|
+
end
|
42
|
+
puts "_" * w
|
43
|
+
puts red("Total: #{prods.all.length} (#{Time.now - t}s)")
|
44
|
+
end
|
45
|
+
alias :search :list
|
46
|
+
|
47
|
+
def info(sid=nil)
|
48
|
+
if !sid
|
49
|
+
puts red("Use: pyradise view <ID>")
|
50
|
+
elsif !prod = Product.filter(:sid => sid.to_i).first
|
51
|
+
puts yellow("Product not found.")
|
52
|
+
else
|
53
|
+
w = terminal_size[0] - 20
|
54
|
+
prices = prod.prices
|
55
|
+
max = prices.values.max.to_f
|
56
|
+
prices.keys.sort.each do |k|
|
57
|
+
rel = "=" * (prices[k] / max * w)
|
58
|
+
puts "#{Time.at(k).strftime('%M/%d')} #{rel}| #{prices[k]}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias :show :info
|
63
|
+
|
64
|
+
#
|
65
|
+
# Wipe all data
|
66
|
+
def clear
|
67
|
+
print t(:delete)
|
68
|
+
return puts unless read_char =~ /y/
|
69
|
+
Product.dataset.delete
|
70
|
+
puts "\nDB cleared."
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def read_char
|
76
|
+
system "stty raw -echo"
|
77
|
+
out = STDIN.getc
|
78
|
+
RUBY_PLATFORM =~ /1.9/ ? out : out.chr
|
79
|
+
ensure
|
80
|
+
system "stty -raw echo"
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Create records from txt files
|
85
|
+
def create txts
|
86
|
+
products = []
|
87
|
+
for txt in txts
|
88
|
+
print "Parsing #{txt[:store]}..."
|
89
|
+
c = Product.all.length
|
90
|
+
parse(txt[:txt], txt[:delimiter]).each do |t|
|
91
|
+
next if t[:price] == 0
|
92
|
+
create_product(t, txt[:store].to_s)
|
93
|
+
end
|
94
|
+
puts "#{Product.all.length - c} #{t(:created)}."
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def create_product(t, store)
|
99
|
+
if prod = Product.filter(:name => t[:name], :store => store).first
|
100
|
+
prod.new_price!(t[:price]) if t[:price] != prod.price
|
101
|
+
else
|
102
|
+
Product.create(t.merge(:store => store))
|
103
|
+
end
|
104
|
+
rescue => e
|
105
|
+
puts "SQLITE Err => #{e}"
|
106
|
+
p t[:name]
|
107
|
+
puts t.inspect
|
108
|
+
end
|
109
|
+
|
110
|
+
def from_stores
|
111
|
+
stores = []
|
112
|
+
for store in YAML.load(File.new(File.dirname(__FILE__) + '/../stores.yml'))[:stores]
|
113
|
+
data = {}
|
114
|
+
data[:store] = store[0]
|
115
|
+
data[:delimiter] = store[1][:delimiter]
|
116
|
+
|
117
|
+
# Open URL
|
118
|
+
next unless data[:txt] = fetch_store(store)
|
119
|
+
|
120
|
+
# Open local file to write
|
121
|
+
open("#{HOME}#{store[0]}-#{Time.now.to_i}.txt", "wb") do |dump|
|
122
|
+
dump.write(data[:txt].gsub(/\t/, ""))
|
123
|
+
end
|
124
|
+
puts "Store #{store[0]} dumped."
|
125
|
+
stores << data
|
126
|
+
end
|
127
|
+
stores
|
128
|
+
end
|
129
|
+
|
130
|
+
def fetch_store(store)
|
131
|
+
open(store[1][:txt]).read
|
132
|
+
rescue => e
|
133
|
+
puts "#{store[0]} offline: #{e}.".capitalize
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
|
137
|
+
def parse(txt, del)
|
138
|
+
products = []
|
139
|
+
txt.each_line do |l|
|
140
|
+
sid, *info = l.split(del)
|
141
|
+
next if info.length < 2
|
142
|
+
price = info.delete_at(-1).strip.to_i
|
143
|
+
next if price.nil? || price.zero?
|
144
|
+
products << { :sid => sid.strip, :name => info.join("").strip.gsub(/\.{2,}/, ""), :price => price }
|
145
|
+
end
|
146
|
+
products
|
147
|
+
end
|
148
|
+
|
149
|
+
# Get terminal size to fit the table nicely.
|
150
|
+
# From highliner.
|
151
|
+
def terminal_size
|
152
|
+
`stty size`.split.map { |x| x.to_i }.reverse
|
153
|
+
end
|
154
|
+
|
155
|
+
def red(txt); "\e[31m#{txt}\e[0m"; end
|
156
|
+
def green(txt); "\e[32m#{txt}\e[0m"; end
|
157
|
+
def yellow(txt); "\e[33m#{txt}\e[0m"; end
|
158
|
+
def bold(txt); "\e[2m#{txt}\e[0m"; end
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Pyradise
|
2
|
+
module I18n
|
3
|
+
I = {
|
4
|
+
:en_us =>
|
5
|
+
{
|
6
|
+
:search => "Searching for",
|
7
|
+
:order => "Ordered by",
|
8
|
+
:delete => "About to delete all the records. Sure? [y/N] ",
|
9
|
+
:created => "products created"
|
10
|
+
},
|
11
|
+
:pt_br =>
|
12
|
+
{
|
13
|
+
:search => "Procurando por",
|
14
|
+
:order => "Ordenado por",
|
15
|
+
:delete => "Deletar todo o banco? [y/N] ",
|
16
|
+
:created => "produtos criados"
|
17
|
+
}
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
}
|
22
|
+
|
23
|
+
def t(text)
|
24
|
+
I[Options[:lang].to_sym][text.to_sym]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/pyradise/product.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
module Pyradise
|
2
|
+
class Product < Sequel::Model
|
2
3
|
|
3
4
|
def before_save
|
4
5
|
phist = { Time.now.to_i => price }
|
@@ -13,4 +14,5 @@ class Product < Sequel::Model
|
|
13
14
|
self.update(:prices => Marshal.dump(prices ? prices.merge({Time.now.to_i => np}) : np))
|
14
15
|
end
|
15
16
|
|
17
|
+
end
|
16
18
|
end
|
data/lib/pyradise.rb
CHANGED
@@ -1,145 +1,34 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'open-uri'
|
3
2
|
require 'sequel'
|
4
3
|
|
5
|
-
#TODO: def init
|
6
|
-
HOME = ENV['HOME'] + "/.pyradise"
|
7
|
-
unless File.exists? HOME
|
8
|
-
FileUtils.mkdir_p HOME
|
9
|
-
conf = open(HOME + "/conf.yml", "wb")
|
10
|
-
conf.write(":rate: 2.0\n:tax: 1.3\n")
|
11
|
-
conf.close
|
12
|
-
end
|
13
|
-
|
14
|
-
DB = Sequel.connect("sqlite://#{HOME}/py.sqlite3")
|
15
|
-
require 'pyradise/product'
|
16
|
-
# require 'pyradise/stat'
|
17
|
-
|
18
|
-
unless DB.table_exists? :products
|
19
|
-
require 'pyradise/migrate'
|
20
|
-
CreatePyradise.apply DB, :up
|
21
|
-
end
|
22
|
-
|
23
4
|
module Pyradise
|
24
5
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
def run! comm, opts
|
32
|
-
if respond_to? comm[0]
|
33
|
-
send(*comm)
|
34
|
-
else
|
35
|
-
puts "Can't do that..."
|
36
|
-
exit
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def fetch
|
41
|
-
create from_stores
|
42
|
-
end
|
43
|
-
|
44
|
-
def list(*query)
|
45
|
-
t = Time.now
|
46
|
-
w = terminal_size[0]
|
47
|
-
s = w - 35
|
48
|
-
puts "Searching #{'"' + query[0] + '"' if query[0]}... Order by: #{query[1] || 'name'}"
|
49
|
-
puts "_" * w
|
50
|
-
prods = Product.filter(:name.like("%#{query[0]}%")).order(query[1] ? query[1].to_sym : :name)
|
51
|
-
prods.each_with_index do |prod, i|
|
52
|
-
name = prod.name.length > s ? prod.name[0..s] + ".." : prod.name
|
53
|
-
out = sprintf("%-6s | %-5s | %-#{w-38}s %-3d | R$ %d", prod.store, prod.sid, name, prod.price, prod.price * RATE * TAX)
|
54
|
-
puts i % 2 == 0 ? bold(out) : out
|
55
|
-
end
|
56
|
-
puts "_" * w
|
57
|
-
puts green("Total: #{prods.all.length} (#{Time.now - t}s)")
|
58
|
-
end
|
59
|
-
alias :search :list
|
60
|
-
|
61
|
-
def info(sid=nil)
|
62
|
-
if !sid
|
63
|
-
puts red("Use: pyradise view <ID>")
|
64
|
-
elsif !prod = Product.filter(:sid => sid.to_i).first
|
65
|
-
puts yellow("Product not found.")
|
66
|
-
else
|
67
|
-
w = terminal_size[0] - 20
|
68
|
-
prices = prod.prices
|
69
|
-
max = prices.values.max.to_f
|
70
|
-
prices.keys.sort.each do |k|
|
71
|
-
rel = "=" * (prices[k] / max * w)
|
72
|
-
puts "#{Time.at(k).strftime('%M/%d')} #{rel}| #{prices[k]}"
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
alias :show :info
|
77
|
-
|
78
|
-
def clear
|
79
|
-
Product.dataset.delete
|
80
|
-
end
|
81
|
-
|
82
|
-
private
|
83
|
-
|
84
|
-
def create txts
|
85
|
-
products = []
|
86
|
-
for txt in txts
|
87
|
-
print "Parsing #{txt[:store]}..."
|
88
|
-
c = Product.all.length
|
89
|
-
parse(txt[:txt], txt[:delimiter]).each do |t|
|
90
|
-
next if t[:price] == 0
|
91
|
-
create_product(t, txt[:store].to_s)
|
92
|
-
end
|
93
|
-
puts "#{Product.all.length - c} products created."
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def create_product(t, store)
|
98
|
-
if prod = Product.filter(:name => t[:name], :store => store).first
|
99
|
-
prod.new_price!(t[:price]) if t[:price] != prod.price
|
100
|
-
else
|
101
|
-
Product.create(t.merge(:store => store))
|
102
|
-
end
|
103
|
-
rescue => e
|
104
|
-
puts "SQLITE Err => #{e}, #{t.inspect} - #{store} #{prod}"
|
105
|
-
end
|
6
|
+
HOME = ENV['HOME'] + "/.pyradise"
|
7
|
+
#TODO: def init
|
8
|
+
DB = Sequel.connect("sqlite://#{HOME}/py.sqlite3")
|
9
|
+
def locale
|
10
|
+
`locale | grep LANG`.scan(/LANG=(.*)\./)[0][0].downcase rescue "en_us"
|
11
|
+
end
|
106
12
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
dump.write(data[:txt])
|
116
|
-
dump.close
|
117
|
-
puts "Store #{store[0]} dumped... "
|
118
|
-
stores << data
|
119
|
-
end
|
120
|
-
stores
|
121
|
-
end
|
13
|
+
unless File.exists? HOME
|
14
|
+
FileUtils.mkdir_p HOME
|
15
|
+
conf = open(HOME + "/conf.yml", "wb")
|
16
|
+
conf.write(":rate: 1.8 # dollar conversion rate\n")
|
17
|
+
conf.write(":tax: 1.3 # transport tax\n")
|
18
|
+
conf.write(":lang: #{locale} # program language\n")
|
19
|
+
conf.close
|
20
|
+
end
|
122
21
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
products << { :sid => sid.strip, :name => info.join("").strip.gsub(/\.{2,}/, ""), :price => price }
|
131
|
-
end
|
132
|
-
products
|
133
|
-
end
|
22
|
+
# require 'pyradise/stat'
|
23
|
+
unless DB.table_exists? :products
|
24
|
+
require 'pyradise/migrate'
|
25
|
+
CreatePyradise.apply DB, :up
|
26
|
+
end
|
27
|
+
Defaults = {:rate => 1, :tax => 1.3, :order => :price, :lang => :en_us}
|
28
|
+
Options = Defaults.merge(YAML.load(File.new(HOME + "/conf.yml")))
|
134
29
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
end
|
30
|
+
require 'pyradise/product'
|
31
|
+
require 'pyradise/i18n'
|
32
|
+
require 'pyradise/cli'
|
139
33
|
|
140
|
-
def red(txt); "\e[31m#{txt}\e[0m"; end
|
141
|
-
def green(txt); "\e[32m#{txt}\e[0m"; end
|
142
|
-
def yellow(txt); "\e[33m#{txt}\e[0m"; end
|
143
|
-
def bold(txt); "\e[2m#{txt}\e[0m"; end
|
144
|
-
end
|
145
34
|
end
|
data/pyradise.gemspec
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{pyradise}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Marcos Piccinini"]
|
12
|
-
s.date = %q{2009-
|
12
|
+
s.date = %q{2009-12-28}
|
13
13
|
s.default_executable = %q{pyradise}
|
14
14
|
s.email = %q{x@nofxx.com}
|
15
15
|
s.executables = ["pyradise"]
|
@@ -26,6 +26,8 @@ Gem::Specification.new do |s|
|
|
26
26
|
"VERSION",
|
27
27
|
"bin/pyradise",
|
28
28
|
"lib/pyradise.rb",
|
29
|
+
"lib/pyradise/cli.rb",
|
30
|
+
"lib/pyradise/i18n.rb",
|
29
31
|
"lib/pyradise/migrate.rb",
|
30
32
|
"lib/pyradise/product.rb",
|
31
33
|
"lib/stores.yml",
|
@@ -59,3 +61,4 @@ Gem::Specification.new do |s|
|
|
59
61
|
s.add_dependency(%q<sequel>, [">= 0"])
|
60
62
|
end
|
61
63
|
end
|
64
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pyradise
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcos Piccinini
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-28 00:00:00 -02:00
|
13
13
|
default_executable: pyradise
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -40,6 +40,8 @@ files:
|
|
40
40
|
- VERSION
|
41
41
|
- bin/pyradise
|
42
42
|
- lib/pyradise.rb
|
43
|
+
- lib/pyradise/cli.rb
|
44
|
+
- lib/pyradise/i18n.rb
|
43
45
|
- lib/pyradise/migrate.rb
|
44
46
|
- lib/pyradise/product.rb
|
45
47
|
- lib/stores.yml
|