rawbotz 0.1.2
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.
- 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
|