rawbotz 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +9 -0
- data/README.md +185 -0
- data/Rakefile +29 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config.ru +9 -0
- data/exe/debug/test_mail +15 -0
- data/exe/rawbotz +39 -0
- data/exe/rawbotz_import_links +74 -0
- data/exe/rawbotz_maintenance.sh +57 -0
- data/exe/rawbotz_process_order +86 -0
- data/exe/rawbotz_process_order_queue +80 -0
- data/exe/rawbotz_stock_update +84 -0
- data/exe/rawbotz_update_local_products +48 -0
- data/exe/rawbotz_update_remote_products +160 -0
- data/lib/rawbotz/app.rb +84 -0
- data/lib/rawbotz/cli/order_result_table.rb +57 -0
- data/lib/rawbotz/datapolate.rb +42 -0
- data/lib/rawbotz/helpers/flash_helper.rb +13 -0
- data/lib/rawbotz/helpers/icon_helper.rb +40 -0
- data/lib/rawbotz/helpers/resource_link_helper.rb +45 -0
- data/lib/rawbotz/local_shop.rb +12 -0
- data/lib/rawbotz/mail_template.rb +29 -0
- data/lib/rawbotz/option_parser.rb +37 -0
- data/lib/rawbotz/order_processor.rb +92 -0
- data/lib/rawbotz/product_updater.rb +89 -0
- data/lib/rawbotz/public/Chart.min.js +14 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/HELP-US-OUT.txt +7 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/css/font-awesome.css +2086 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/css/font-awesome.min.css +4 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/fonts/FontAwesome.otf +0 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/fonts/fontawesome-webfont.eot +0 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/fonts/fontawesome-webfont.svg +655 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/fonts/fontawesome-webfont.ttf +0 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/fonts/fontawesome-webfont.woff +0 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/fonts/fontawesome-webfont.woff2 +0 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/animated.less +34 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/bordered-pulled.less +25 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/core.less +12 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/fixed-width.less +6 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/font-awesome.less +17 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/icons.less +697 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/larger.less +13 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/list.less +19 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/mixins.less +26 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/path.less +15 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/rotated-flipped.less +20 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/stacked.less +20 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/less/variables.less +708 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_animated.scss +34 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_bordered-pulled.scss +25 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_core.scss +12 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_fixed-width.scss +6 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_icons.scss +697 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_larger.scss +13 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_list.scss +19 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_mixins.scss +26 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_path.scss +15 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_rotated-flipped.scss +20 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_stacked.scss +20 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/_variables.scss +708 -0
- data/lib/rawbotz/public/font-awesome-4.5.0/scss/font-awesome.scss +17 -0
- data/lib/rawbotz/public/jquery-2.2.0.min.js +4 -0
- data/lib/rawbotz/public/jui/external/jquery/jquery.js +9789 -0
- data/lib/rawbotz/public/jui/images/ui-icons_444444_256x240.png +0 -0
- data/lib/rawbotz/public/jui/images/ui-icons_555555_256x240.png +0 -0
- data/lib/rawbotz/public/jui/images/ui-icons_777620_256x240.png +0 -0
- data/lib/rawbotz/public/jui/images/ui-icons_777777_256x240.png +0 -0
- data/lib/rawbotz/public/jui/images/ui-icons_cc0000_256x240.png +0 -0
- data/lib/rawbotz/public/jui/images/ui-icons_ffffff_256x240.png +0 -0
- data/lib/rawbotz/public/jui/index.html +513 -0
- data/lib/rawbotz/public/jui/jquery-ui.css +1225 -0
- data/lib/rawbotz/public/jui/jquery-ui.js +16617 -0
- data/lib/rawbotz/public/jui/jquery-ui.min.css +7 -0
- data/lib/rawbotz/public/jui/jquery-ui.min.js +13 -0
- data/lib/rawbotz/public/jui/jquery-ui.structure.css +833 -0
- data/lib/rawbotz/public/jui/jquery-ui.structure.min.css +5 -0
- data/lib/rawbotz/public/jui/jquery-ui.theme.css +410 -0
- data/lib/rawbotz/public/jui/jquery-ui.theme.min.css +5 -0
- data/lib/rawbotz/public/pure-min.css +11 -0
- data/lib/rawbotz/public/rawbotz.css +60 -0
- data/lib/rawbotz/public/rawbotz.js +96 -0
- data/lib/rawbotz/public/rawbotz_ajax_product_link.js +64 -0
- data/lib/rawbotz/remote_shop.rb +21 -0
- data/lib/rawbotz/routes/non_remote_orders.rb +45 -0
- data/lib/rawbotz/routes/orders.rb +76 -0
- data/lib/rawbotz/routes/product_links.rb +68 -0
- data/lib/rawbotz/routes/products.rb +104 -0
- data/lib/rawbotz/routes/remote_shop.rb +37 -0
- data/lib/rawbotz/routes/suppliers.rb +36 -0
- data/lib/rawbotz/routes.rb +9 -0
- data/lib/rawbotz/version.rb +3 -0
- data/lib/rawbotz/views/_hide_unhide_button.haml +11 -0
- data/lib/rawbotz/views/_menu.haml +20 -0
- data/lib/rawbotz/views/index.haml +1 -0
- data/lib/rawbotz/views/layout.haml +55 -0
- data/lib/rawbotz/views/maintenance/menu.haml +25 -0
- data/lib/rawbotz/views/maintenance.haml +150 -0
- data/lib/rawbotz/views/order/_head.haml +24 -0
- data/lib/rawbotz/views/order/_item_table.haml +44 -0
- data/lib/rawbotz/views/order/new.haml +22 -0
- data/lib/rawbotz/views/order/non_remote.haml +42 -0
- data/lib/rawbotz/views/order/packlist.haml +14 -0
- data/lib/rawbotz/views/order/view.haml +79 -0
- data/lib/rawbotz/views/orders/index.haml +40 -0
- data/lib/rawbotz/views/orders/menu.haml +24 -0
- data/lib/rawbotz/views/orders/non_remotes.haml +8 -0
- data/lib/rawbotz/views/product/_stock_sales_plot.haml +60 -0
- data/lib/rawbotz/views/product/link_to.haml +29 -0
- data/lib/rawbotz/views/product/view.haml +64 -0
- data/lib/rawbotz/views/products/_link_table.haml +38 -0
- data/lib/rawbotz/views/products/index.haml +36 -0
- data/lib/rawbotz/views/products/link_wizard.haml +48 -0
- data/lib/rawbotz/views/products/links.haml +65 -0
- data/lib/rawbotz/views/products/menu.haml +27 -0
- data/lib/rawbotz/views/products/table.haml +20 -0
- data/lib/rawbotz/views/products/table_with_linking.haml +30 -0
- data/lib/rawbotz/views/remote_cart/index.haml +30 -0
- data/lib/rawbotz/views/remote_order/view.haml +35 -0
- data/lib/rawbotz/views/remote_orders/index.haml +20 -0
- data/lib/rawbotz/views/remote_product/view.haml +90 -0
- data/lib/rawbotz/views/remote_products/_table.haml +15 -0
- data/lib/rawbotz/views/remote_products/index.haml +30 -0
- data/lib/rawbotz/views/supplier/view.haml +63 -0
- data/lib/rawbotz/views/suppliers/index.haml +13 -0
- data/lib/rawbotz/views/thin_layout.haml +32 -0
- data/lib/rawbotz/views/widgets/_ajax_link.haml +5 -0
- data/lib/rawbotz/views/widgets/_link.haml +6 -0
- data/lib/rawbotz/views/widgets/_link_line.haml +15 -0
- data/lib/rawbotz.rb +77 -0
- data/rawbotz.gemspec +33 -0
- metadata +327 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
require "rawbotz"
|
6
|
+
|
7
|
+
require 'optparse'
|
8
|
+
|
9
|
+
options = {config: RawgentoModels::CONF_FILE}
|
10
|
+
|
11
|
+
optparse = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [OPTIONS]"
|
13
|
+
opts.separator ""
|
14
|
+
|
15
|
+
opts.separator "RawgentoDB configuration file (MySQL options)"
|
16
|
+
opts.on("-c", "--config FILE", 'file path to rawgento-db YAML config file.') do |c|
|
17
|
+
if !File.exist? c
|
18
|
+
STDERR.puts "Cannot load conf file #{c}"
|
19
|
+
exit 2
|
20
|
+
end
|
21
|
+
|
22
|
+
options[:config] = c
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.separator ""
|
26
|
+
opts.separator "Output options"
|
27
|
+
opts.on("-v", "--verbose", 'print debug output (WARNING: including PASSWORD)') do |v|
|
28
|
+
$stdout.sync = true
|
29
|
+
options[:verbose] = true
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.separator ""
|
33
|
+
opts.separator "General"
|
34
|
+
opts.on_tail('--version', 'Show version.') do
|
35
|
+
puts "#{$PROGRAM_NAME} #{Rawbotz::VERSION}"
|
36
|
+
exit 0
|
37
|
+
end
|
38
|
+
opts.on('-h', '--help', 'Show this help.') do
|
39
|
+
puts opts
|
40
|
+
exit 0
|
41
|
+
end
|
42
|
+
end
|
43
|
+
optparse.parse!
|
44
|
+
|
45
|
+
include RawgentoModels
|
46
|
+
|
47
|
+
def main options
|
48
|
+
logger = Logger.new(STDOUT)
|
49
|
+
logger.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
|
50
|
+
|
51
|
+
logger.debug "#{$PROGRAM_NAME} #{Rawbotz::VERSION}"
|
52
|
+
|
53
|
+
if !File.exist? options[:config]
|
54
|
+
logger.warn "Cannot load conf file #{options[:config]}"
|
55
|
+
exit 2
|
56
|
+
end
|
57
|
+
|
58
|
+
RawgentoModels.establish_connection options[:config]
|
59
|
+
|
60
|
+
begin
|
61
|
+
result = RawgentoDB::Query.stock(RawgentoDB.settings(options[:config]))
|
62
|
+
rescue Mysql2::Error => e
|
63
|
+
logger.error "Problems accessing MySQL database #{e.inspect}"
|
64
|
+
exit 2
|
65
|
+
end
|
66
|
+
|
67
|
+
now = DateTime.now
|
68
|
+
|
69
|
+
logger.info("Starting stock update")
|
70
|
+
|
71
|
+
result.each do |s|
|
72
|
+
product = RawgentoModels::LocalProduct.find_by(product_id: s.product_id)
|
73
|
+
if product.nil?
|
74
|
+
logger.info("No such product #{s.product_id}")
|
75
|
+
else
|
76
|
+
product.stock_items.create(qty: s.qty, date: now)
|
77
|
+
logger.info("Updated stock for #{s.product_id}")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
logger.info("Finished with stock update")
|
82
|
+
end
|
83
|
+
|
84
|
+
main options
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rawbotz"
|
4
|
+
require "rawbotz/option_parser"
|
5
|
+
|
6
|
+
optparse = Rawbotz::OptionParser.new do |opts, options|
|
7
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [OPTIONS]"
|
8
|
+
opts.separator ""
|
9
|
+
|
10
|
+
opts.on("-m", "--[no-]mail",
|
11
|
+
"send mail with result") do |v|
|
12
|
+
options[:mail] = v
|
13
|
+
end
|
14
|
+
end
|
15
|
+
optparse.parse!
|
16
|
+
options = optparse.options
|
17
|
+
|
18
|
+
logger = Logger.new(STDOUT)
|
19
|
+
logger.level = optparse.options[:verbose] ? Logger::DEBUG : Logger::INFO
|
20
|
+
|
21
|
+
logger.debug "#{$PROGRAM_NAME} #{Rawbotz::VERSION}"
|
22
|
+
|
23
|
+
logger.info("Fetching and updating local products from MySQL database")
|
24
|
+
|
25
|
+
begin
|
26
|
+
products = RawgentoDB::Query.products
|
27
|
+
rescue
|
28
|
+
logger.error "Could not connect to MySQL database or other error"
|
29
|
+
#logger.error "$!.inspect" $@ -> backtrace
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
product_count = RawgentoModels::LocalProduct.unscoped.count
|
34
|
+
|
35
|
+
updater = Rawbotz::ProductUpdater.new logger
|
36
|
+
updater.sync
|
37
|
+
|
38
|
+
logger.info("Finished updating local products.")
|
39
|
+
|
40
|
+
logger.info("Found #{RawgentoModels::LocalProduct.unscoped.count - product_count} new products")
|
41
|
+
|
42
|
+
if options[:mail]
|
43
|
+
logger.debug("Sending mail")
|
44
|
+
Rawbotz::mail("rawbotz local product update",
|
45
|
+
"Found #{RawgentoModels::LocalProduct.unscoped.count - product_count} new local products")
|
46
|
+
end
|
47
|
+
|
48
|
+
exit 0
|
@@ -0,0 +1,160 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rawbotz"
|
4
|
+
|
5
|
+
require 'magento_remote'
|
6
|
+
|
7
|
+
require 'optparse'
|
8
|
+
|
9
|
+
options = { sleep_time: 1,
|
10
|
+
start_pid: 0,
|
11
|
+
limit: nil,
|
12
|
+
check_new: false,
|
13
|
+
max_dead: 100,
|
14
|
+
conf_file: 'remote-shop.yml'}
|
15
|
+
def options.print_default(sym); "(default: #{self[sym]})" end
|
16
|
+
|
17
|
+
program_name = File.basename __FILE__
|
18
|
+
$program_name = program_name
|
19
|
+
|
20
|
+
optparse = OptionParser.new do |opts|
|
21
|
+
opts.banner = "Usage: #{program_name} [OPTIONS]"
|
22
|
+
opts.separator ""
|
23
|
+
opts.separator "Scrapes remote shop and 'imports' products from it."
|
24
|
+
opts.separator ""
|
25
|
+
|
26
|
+
opts.separator "Will read remote shop conf from config file."
|
27
|
+
|
28
|
+
opts.separator ""
|
29
|
+
opts.separator "Configuration file (remote shop info, local db info)"
|
30
|
+
opts.on("-c", "--config FILE", 'file path to YAML config file.') do |c|
|
31
|
+
options[:config] = c
|
32
|
+
end
|
33
|
+
opts.separator ""
|
34
|
+
opts.separator "Parsing/scraping options"
|
35
|
+
opts.on("-s", "--start-pid PRODUCT_ID", Integer,
|
36
|
+
"start at product_id PRODUCT_ID #{options.print_default(:start_pid)}") do |v|
|
37
|
+
options[:start_pid] = v
|
38
|
+
end
|
39
|
+
opts.on("-w", "--wait SECONDS", Float,
|
40
|
+
"sleep SECONDS between two requests #{options.print_default(:sleep_time)}") do |v|
|
41
|
+
options[:sleep_time] = v
|
42
|
+
end
|
43
|
+
opts.on("-b", "--batches NUMBER", Integer,
|
44
|
+
"try NUMBER batches before assuming no further hits #{options.print_default(:max_dead)}") do |v|
|
45
|
+
options[:max_dead] = v
|
46
|
+
end
|
47
|
+
opts.on("-m", "--[no-]mail",
|
48
|
+
"send mail with result") do |v|
|
49
|
+
options[:mail] = v
|
50
|
+
end
|
51
|
+
opts.on("-l", "--limit LIMIT", Integer,
|
52
|
+
"try LIMIT products #{options.print_default(:limit)}") do |v|
|
53
|
+
options[:limit] = v
|
54
|
+
end
|
55
|
+
opts.on("-n", "--check-new", Integer,
|
56
|
+
"check only for new products") do |v|
|
57
|
+
options[:check_new] = true
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.separator ""
|
61
|
+
opts.separator "Output options"
|
62
|
+
opts.on("-v", "--verbose", 'print debug output (WARNING: including PASSWORD)') do |v|
|
63
|
+
$stdout.sync = true
|
64
|
+
options[:verbose] = true
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.separator ""
|
68
|
+
opts.separator "General"
|
69
|
+
opts.on_tail('--version', 'Show version.') do
|
70
|
+
puts "#{program_name} #{Rawbotz::VERSION}"
|
71
|
+
exit 0
|
72
|
+
end
|
73
|
+
opts.on_tail('-h', '--help', 'Show this help.') do
|
74
|
+
puts opts
|
75
|
+
exit 0
|
76
|
+
end
|
77
|
+
end
|
78
|
+
optparse.parse!
|
79
|
+
|
80
|
+
if options[:check_new] && options[:start_pid] != 0
|
81
|
+
STDERR.puts "Cannot define both check-new and start-pid options"
|
82
|
+
exit 1
|
83
|
+
end
|
84
|
+
|
85
|
+
def main options
|
86
|
+
STDOUT.sync = true
|
87
|
+
logger = Logger.new(STDOUT)
|
88
|
+
logger.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
|
89
|
+
|
90
|
+
logger.debug "#{$program_name} #{Rawbotz::VERSION}"
|
91
|
+
|
92
|
+
mech_config = (YAML.load_file options[:config])["remote_shop"]
|
93
|
+
logger.debug(mech_config)
|
94
|
+
mech = MagentoMech.from_config(mech_config)
|
95
|
+
mech.log_to! logger
|
96
|
+
|
97
|
+
RawgentoModels.establish_connection options[:config]
|
98
|
+
logger.debug("Established connection to rawbotz database.")
|
99
|
+
|
100
|
+
supplier_name = mech_config["supplier_name"]
|
101
|
+
supplier = RawgentoModels::Supplier.find_or_create_by(name: supplier_name)
|
102
|
+
logger.debug("Supplier is #{supplier.name}")
|
103
|
+
|
104
|
+
number_dead = 0
|
105
|
+
product_count = RawgentoModels::RemoteProduct.count
|
106
|
+
|
107
|
+
|
108
|
+
current_pid = options[:start_pid]
|
109
|
+
if options[:check_new]
|
110
|
+
|
111
|
+
# max (product_id)
|
112
|
+
last_product_id = RawgentoModels::RemoteProduct.unscoped.where(supplier: supplier).order(product_id: :desc).first.product_id rescue 0
|
113
|
+
logger.debug "Nr Remote Products: #{RawgentoModels::RemoteProduct.where(supplier: supplier).count}"
|
114
|
+
current_pid = last_product_id
|
115
|
+
options[:start_pid] = last_product_id
|
116
|
+
logger.info "Check for new products (last found product_id was #{last_product_id})"
|
117
|
+
end
|
118
|
+
|
119
|
+
while true do
|
120
|
+
p = mech.scrape_products(current_pid, 1, options[:sleep_time])
|
121
|
+
if !p.empty?
|
122
|
+
logger.info "Found #{p[0].inspect}"
|
123
|
+
product_db = RawgentoModels::RemoteProduct.find_or_initialize_by(product_id: p[0][1].to_i, supplier: supplier)
|
124
|
+
product_db.update!(name: p[0][0], product_id: p[0][1].to_i, supplier: supplier)
|
125
|
+
product_db.save!
|
126
|
+
number_dead = 0
|
127
|
+
else
|
128
|
+
extra = options[:limit] ? "#{current_pid - (options[:start_pid] + options[:limit])}" : ""
|
129
|
+
extra += " #{options[:max_dead] - number_dead}"
|
130
|
+
logger.info "None at #{current_pid} - try #{extra} more"
|
131
|
+
number_dead += 1
|
132
|
+
end
|
133
|
+
current_pid += 1
|
134
|
+
if options[:limit] && current_pid > options[:start_pid] + options[:limit]
|
135
|
+
logger.info "reached limit"
|
136
|
+
break
|
137
|
+
end
|
138
|
+
if number_dead > options[:max_dead]
|
139
|
+
logger.info "reached max dead"
|
140
|
+
break
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
if RawgentoModels::RemoteProduct.any?
|
145
|
+
logger.info "Last at #{RawgentoModels::RemoteProduct.unscoped.where(supplier: supplier).order(product_id: :desc).first.product_id}"
|
146
|
+
logger.info "Found #{RawgentoModels::RemoteProduct.count - product_count} remote products"
|
147
|
+
else
|
148
|
+
logger.warn "No remote products found!"
|
149
|
+
end
|
150
|
+
|
151
|
+
if options[:mail]
|
152
|
+
logger.debug("Sending mail")
|
153
|
+
Rawbotz::mail("rawbotz remote product update",
|
154
|
+
"Found #{RawgentoModels::RemoteProduct.count - product_count} remote products")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
main options
|
160
|
+
exit 0
|
data/lib/rawbotz/app.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'action_view' # Workaround https://github.com/haml/haml/issues/695
|
3
|
+
require 'haml'
|
4
|
+
require 'rawbotz/helpers/icon_helper'
|
5
|
+
require 'rawbotz/helpers/flash_helper'
|
6
|
+
require 'rawbotz/helpers/resource_link_helper'
|
7
|
+
require 'tilt/haml'
|
8
|
+
|
9
|
+
class RawbotzApp < Sinatra::Base
|
10
|
+
include RawgentoModels
|
11
|
+
|
12
|
+
enable :sessions
|
13
|
+
|
14
|
+
configure do
|
15
|
+
# Setup local rawbotz database
|
16
|
+
if Rawbotz.conf_file_path
|
17
|
+
RawgentoModels.establish_connection Rawbotz.conf_file_path
|
18
|
+
else
|
19
|
+
RawgentoModels.establish_connection
|
20
|
+
end
|
21
|
+
|
22
|
+
# Configure local mysql database
|
23
|
+
RawgentoDB.settings(Rawbotz.conf_file_path)
|
24
|
+
|
25
|
+
# And RemoteShop
|
26
|
+
conf = YAML.load_file(Rawbotz.conf_file_path)
|
27
|
+
set :supplier_name, conf["supplier_name"]
|
28
|
+
set :supplier, Supplier.find_by(name:
|
29
|
+
conf["supplier_name"])
|
30
|
+
set :conf, conf
|
31
|
+
end
|
32
|
+
|
33
|
+
helpers do ; end
|
34
|
+
helpers Rawbotz::Helpers::IconHelper
|
35
|
+
helpers Rawbotz::Helpers::FlashHelper
|
36
|
+
helpers Rawbotz::Helpers::ResourceLinkHelper
|
37
|
+
|
38
|
+
get '/' do
|
39
|
+
haml :index
|
40
|
+
end
|
41
|
+
|
42
|
+
# get '/orders'
|
43
|
+
# get '/order/new'
|
44
|
+
# get '/order/:id'
|
45
|
+
# post '/order/:id/'
|
46
|
+
# get '/order/:id/packlist'
|
47
|
+
register Rawbotz::RawbotzApp::Routing::Orders
|
48
|
+
|
49
|
+
# get '/products'
|
50
|
+
# post '/products/search'
|
51
|
+
# get '/product/:id'
|
52
|
+
# get '/product/:id/stock_sales_plot'
|
53
|
+
# post '/product/:id/hide'
|
54
|
+
# post '/product/:id/unhide'
|
55
|
+
# get '/remote_products'
|
56
|
+
# post '/remote_products/search'
|
57
|
+
# get '/remote_product/id'
|
58
|
+
register Rawbotz::RawbotzApp::Routing::Products
|
59
|
+
|
60
|
+
# get '/products/links'
|
61
|
+
# get '/products/link_wizard'
|
62
|
+
# get '/products/link_wizard/:idx'
|
63
|
+
# get '/product/:id/link'
|
64
|
+
# post '/product/:id/link'
|
65
|
+
register Rawbotz::RawbotzApp::Routing::ProductLinks
|
66
|
+
|
67
|
+
# get '/remote_cart'
|
68
|
+
# get '/remote_orders'
|
69
|
+
# get '/remote_order/:id'
|
70
|
+
register Rawbotz::RawbotzApp::Routing::RemoteShop
|
71
|
+
|
72
|
+
# get '/supplier/:id'
|
73
|
+
# post '/supplier/:id'
|
74
|
+
register Rawbotz::RawbotzApp::Routing::Suppliers
|
75
|
+
|
76
|
+
# get '/orders/non_remote'
|
77
|
+
# get '/order/non_remote/:supplier_id'
|
78
|
+
# post '/order/non_remote/:supplier_id'
|
79
|
+
register Rawbotz::RawbotzApp::Routing::NonRemoteOrders
|
80
|
+
|
81
|
+
get '/maintenance' do
|
82
|
+
haml :maintenance
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Rawbotz::CLI
|
2
|
+
module OrderResultTable
|
3
|
+
def self.tables diffs
|
4
|
+
out = ""
|
5
|
+
if !diffs[:perfect].empty?
|
6
|
+
perfect_items = diffs[:perfect].map do |p, q|
|
7
|
+
[p.local_product.remote_product.name[0..35], q]
|
8
|
+
end
|
9
|
+
out << Terminal::Table.new(title: "Perfect",
|
10
|
+
headings: ['Product', 'In Cart'],
|
11
|
+
rows: perfect_items,
|
12
|
+
style: {width: 60}).to_s
|
13
|
+
out << "\n\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
if !diffs[:modified].empty?
|
17
|
+
modified_items = diffs[:modified].map do |p,q|
|
18
|
+
[p.local_product.remote_product.name[0..35], p.num_wished, q]
|
19
|
+
end
|
20
|
+
out << Terminal::Table.new(title: "Modified",
|
21
|
+
headings: ['Product', 'Wished', 'In Cart'],
|
22
|
+
rows: modified_items,
|
23
|
+
style: {width:60}).to_s
|
24
|
+
out << "\n\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
if !diffs[:miss].empty?
|
28
|
+
missing_items = diffs[:miss].map do |p,q|
|
29
|
+
[p.local_product.remote_product.name[0..35], q]
|
30
|
+
end
|
31
|
+
out << Terminal::Table.new(title: "Missing (?)",
|
32
|
+
headings: ['Product', 'In Cart'],
|
33
|
+
rows: missing_items,
|
34
|
+
style: {width:60}).to_s
|
35
|
+
out << "\n\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
if !diffs[:under_avail].empty?
|
39
|
+
under_avail_items = diffs[:under_avail].map do |p,q|
|
40
|
+
[p.local_product.remote_product.name[0..35], p.num_wished, q]
|
41
|
+
end
|
42
|
+
out << Terminal::Table.new(title: "Not available as such",
|
43
|
+
headings: ['Product', 'Wanted', 'In Cart'],
|
44
|
+
rows: under_avail_items,
|
45
|
+
style: {width:60}).to_s
|
46
|
+
out << "\n\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
if !diffs[:extra].empty?
|
50
|
+
out << Terminal::Table.new(title: "Extra (not in rawbotz)",
|
51
|
+
headings: ['Product', 'In Cart'],
|
52
|
+
rows: diffs[:extra],
|
53
|
+
style: {width:60}).to_s
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rawbotz
|
2
|
+
module Datapolate
|
3
|
+
def self.date_minmax sales, stock
|
4
|
+
#sales.each{|sale| sale[0] = Date.strptime(sale[0])}
|
5
|
+
sales_first, sales_last = sales.minmax_by{|s| s[0]}
|
6
|
+
stock_first, stock_last = stock.minmax_by{|s| s.date}
|
7
|
+
first_dates = []
|
8
|
+
first_dates << sales_first[0] if !sales_first.nil?
|
9
|
+
first_dates << stock_first.date.to_date if !stock_first.nil?
|
10
|
+
|
11
|
+
last_dates = []
|
12
|
+
last_dates << sales_last[0] if !sales_last.nil?
|
13
|
+
last_dates << stock_last.date.to_date if !stock_last.nil?
|
14
|
+
|
15
|
+
[first_dates.min, last_dates.max]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.explode_days min_date, max_date
|
19
|
+
(min_date.to_date..max_date.to_date).to_a
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.create_data sales, stock
|
23
|
+
# from [date, sales] and [date, stock] create realistic list
|
24
|
+
from_date, to_date = date_minmax sales, stock
|
25
|
+
days = explode_days from_date, to_date
|
26
|
+
|
27
|
+
stock_data = Hash.new(nil)
|
28
|
+
stock.each{|s| stock_data[s.date.to_date] = s.qty}
|
29
|
+
sales_data = Hash.new(nil)
|
30
|
+
sales.each{|s| sales_data[s[0].to_date] = s.fetch(1)}
|
31
|
+
|
32
|
+
data = {}
|
33
|
+
days.each do |day|
|
34
|
+
data[day] = {label: day.to_date,
|
35
|
+
stock: stock_data[day],
|
36
|
+
sales: sales_data[day]
|
37
|
+
}
|
38
|
+
end
|
39
|
+
data
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rawbotz
|
2
|
+
module Helpers
|
3
|
+
module IconHelper
|
4
|
+
def error_icon
|
5
|
+
'<i class="fa fa-flash"></i>'
|
6
|
+
end
|
7
|
+
def index_icon
|
8
|
+
'<i class="fa fa-th-list"></i>'
|
9
|
+
end
|
10
|
+
def info_icon
|
11
|
+
'<i class="fa fa-info-circle"></i>'
|
12
|
+
end
|
13
|
+
def link_icon
|
14
|
+
'<i class="fa fa-link"></i>'
|
15
|
+
end
|
16
|
+
def order_icon
|
17
|
+
'<i class="fa fa-shopping-cart"></i>'
|
18
|
+
end
|
19
|
+
def product_icon
|
20
|
+
'<i class="fa fa-cube"></i>'
|
21
|
+
end
|
22
|
+
def products_icon
|
23
|
+
'<i class="fa fa-cubes"></i>'
|
24
|
+
end
|
25
|
+
def remote_icon
|
26
|
+
'<i class="fa fa-globe"></i>'
|
27
|
+
end
|
28
|
+
def settings_icon
|
29
|
+
'<i class="fa fa-wrench"></i>'
|
30
|
+
end
|
31
|
+
def success_icon
|
32
|
+
'<i class="fa fa-smile-o"></i>'
|
33
|
+
end
|
34
|
+
def warning_icon
|
35
|
+
'<i class="fa fa-warning"></i>'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
# briefcase clone gift shopping-bag truck
|
40
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Rawbotz
|
2
|
+
module Helpers
|
3
|
+
module ResourceLinkHelper
|
4
|
+
include RawgentoModels
|
5
|
+
|
6
|
+
def local_product_link product
|
7
|
+
if product.present?
|
8
|
+
if product.name.empty?
|
9
|
+
"<a href=\"/product/#{product.id}\">[no product name!]</a>"
|
10
|
+
else
|
11
|
+
"<a href=\"/product/#{product.id}\">#{product.name}</a>"
|
12
|
+
end
|
13
|
+
else
|
14
|
+
"Product not in database"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
def remote_product_link product
|
18
|
+
if product.is_a? LocalProduct
|
19
|
+
remote_product_link product.remote_product
|
20
|
+
elsif product.try(:id)
|
21
|
+
"<a href=\"/remote_product/#{product.id}\">"\
|
22
|
+
"<i class=\"fa fa-globe\"></i>#{product.name}</a>"
|
23
|
+
elsif product.name
|
24
|
+
# Used in RemoteOrder view.
|
25
|
+
"#{product.name}"
|
26
|
+
else
|
27
|
+
"not linked"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
def product_link product
|
31
|
+
return local_product_link(product) if product.is_a?(LocalProduct)
|
32
|
+
return remote_product_link(product) if product.is_a?(RemoteProduct)
|
33
|
+
"no product"
|
34
|
+
end
|
35
|
+
def supplier_link supplier
|
36
|
+
if supplier.to_s != ""
|
37
|
+
"<a href=\"/supplier/#{supplier.id}\">#{supplier.name}</a>"
|
38
|
+
else
|
39
|
+
"[no supplier]"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Rawbotz
|
2
|
+
module LocalShop
|
3
|
+
def self.product_page_frontend(product, settings)
|
4
|
+
# TODO settings could be memoized
|
5
|
+
"#{settings['local_shop']['base_uri']}catalog/product/view/id/#{product.product_id}"
|
6
|
+
end
|
7
|
+
def self.product_page_backend(product, settings)
|
8
|
+
# TODO settings could be memoized
|
9
|
+
"#{settings['local_shop']['base_uri']}index.php/rawadmin/catalog_product/edit/id/#{product.product_id}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rawbotz
|
2
|
+
module MailTemplate
|
3
|
+
# Substitute certain patterns in template
|
4
|
+
def self.consume template, order
|
5
|
+
result = ""
|
6
|
+
result = template.gsub(/SUPPLIERNAME/, order[:supplier][:name])
|
7
|
+
|
8
|
+
lines = result.split("\n")
|
9
|
+
subject, lines = lines.partition{|l| l.start_with?("SUBJECT=")}
|
10
|
+
product_line = lines.detect{|l| l.start_with?("* ")}
|
11
|
+
order_lines = order[:order_items].map do |oi|
|
12
|
+
next if product_line.nil?
|
13
|
+
order_item_line = product_line[2..-1]
|
14
|
+
order_item_line.gsub!(/PRODUCTCODE/, '')
|
15
|
+
order_item_line.gsub!(/QTY/, oi[:num_wished].to_s)
|
16
|
+
if oi[:local_product][:packsize].to_s != ""
|
17
|
+
order_item_line.gsub!(/NUM_PACKS/, (oi[:num_wished] / oi[:local_product][:packsize].to_f).to_s)
|
18
|
+
else
|
19
|
+
order_item_line.gsub!(/NUM_PACKS/, '')
|
20
|
+
end
|
21
|
+
order_item_line.gsub!(/PACKSIZE/, oi[:local_product][:packsize].to_s)
|
22
|
+
order_item_line.gsub!(/PRODUCTNAME/, oi[:local_product][:name].to_s)
|
23
|
+
order_item_line
|
24
|
+
end
|
25
|
+
lines[lines.find_index(product_line)] = order_lines
|
26
|
+
lines.flatten.join("\n")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Rawbotz
|
4
|
+
class OptionParser < ::OptionParser
|
5
|
+
attr_accessor :options
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@options = {}
|
9
|
+
super *args
|
10
|
+
default_options!
|
11
|
+
yield(self, @options) if block_given?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def default_options!
|
17
|
+
separator "Output Options"
|
18
|
+
on("-v", "--[no-]verbose", 'Run verbosely') do |c|
|
19
|
+
@options[:verbose] = c
|
20
|
+
end
|
21
|
+
separator "Configuration file"
|
22
|
+
on("-c", "--config FILE", 'File path to YAML config file.') do |c|
|
23
|
+
Rawbotz.conf_file_path = c
|
24
|
+
Rawbotz.configure!
|
25
|
+
end
|
26
|
+
separator "General"
|
27
|
+
on_tail('--version', 'Show version and exit.') do
|
28
|
+
puts "part of rawbotz #{Rawbotz::VERSION}"
|
29
|
+
exit 0
|
30
|
+
end
|
31
|
+
on('-h', '--help', 'Show this help and exit.') do
|
32
|
+
puts self
|
33
|
+
exit 0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|