Add tasks files
This commit is contained in:
parent
2133be2436
commit
1ef6964dd7
4 changed files with 156 additions and 0 deletions
2
lib/tasks/bnn_sync.rake
Normal file
2
lib/tasks/bnn_sync.rake
Normal file
|
@ -0,0 +1,2 @@
|
|||
desc "Sync files via FTP -- legacy task name. Update articles."
|
||||
task sync_bnn_files: :sync_ftp_files
|
7
lib/tasks/ftp_sync.rake
Normal file
7
lib/tasks/ftp_sync.rake
Normal file
|
@ -0,0 +1,7 @@
|
|||
desc "Sync files via FTP. Update articles."
|
||||
task sync_ftp_files: :environment do
|
||||
Supplier.ftp_sync.all.each do |supplier|
|
||||
puts "FTP-sync files for #{supplier.name}..."
|
||||
supplier.sync_ftp_files
|
||||
end
|
||||
end
|
31
lib/tasks/import.rake
Normal file
31
lib/tasks/import.rake
Normal file
|
@ -0,0 +1,31 @@
|
|||
# encoding:utf-8
|
||||
desc "Import articles from file (options: FILE=f, TYPE=t, SUPPLIER=id_or_name)"
|
||||
task 'import:articles': :environment do |t, args|
|
||||
filename = ENV['FILE']
|
||||
filename.present? or raise 'Please set FILE to the file to import'
|
||||
|
||||
type = ENV['TYPE']
|
||||
types_avail = ArticleImport.file_formats.keys
|
||||
types_avail.include?(type) or raise "Please set TYPE to one of: #{types_avail.join(', ')}"
|
||||
|
||||
supplier = Supplier.where('id = ? OR name = ?', ENV['SUPPLIER'], ENV['SUPPLIER']).first
|
||||
supplier.present? or raise 'Please set SUPPLIER to a supplier id or name'
|
||||
|
||||
outlisted_counter, new_counter, updated_counter, invalid_articles = begin
|
||||
Article.transaction do
|
||||
supplier.update_articles_from_file(File.new(filename), type: type)
|
||||
end
|
||||
end
|
||||
|
||||
# show result
|
||||
# @todo remove code duplication with mail_sync
|
||||
puts "* imported: #{new_counter} new, #{updated_counter} updated, #{outlisted_counter} outlisted, #{invalid_articles.size} invalid"
|
||||
invalid_articles.each do |article|
|
||||
puts "- invalid article '#{article.name}'"
|
||||
article.errors.each do |attr, msg|
|
||||
msg.split("\n").each do |l|
|
||||
puts " · #{attr.blank? ? '' : "#{attr}: "} #{l}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
116
lib/tasks/mail.rake
Normal file
116
lib/tasks/mail.rake
Normal file
|
@ -0,0 +1,116 @@
|
|||
#
|
||||
# Rake tasks for receiving article updates by email.
|
||||
#
|
||||
# Mail setup is heavily inspired by Foodsoft's reply-by-mail feature for messages
|
||||
# @see https://github.com/foodcoops/foodsoft/blob/master/lib/foodsoft_mail_receiver.rb
|
||||
#
|
||||
require "mail"
|
||||
require "midi-smtp-server"
|
||||
|
||||
class ReplyEmailSmtpServer < MidiSmtpServer::Smtpd
|
||||
|
||||
def start
|
||||
super
|
||||
@from = nil
|
||||
@supplier = nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def on_mail_from_event(ctx, from)
|
||||
@from = from
|
||||
end
|
||||
|
||||
def on_rcpt_to_event(ctx, rcpt_to)
|
||||
recipient = rcpt_to.gsub(/\A\s*<\s*(.*)\s*>\s*\z/, '\1')
|
||||
@supplier = find_supplier(recipient)
|
||||
rcpt_to
|
||||
rescue => error
|
||||
logger.info("Can not accept mail for '#{rcpt_to}': #{error}")
|
||||
raise MidiSmtpServer::Smtpd550Exception
|
||||
end
|
||||
|
||||
def on_message_data_event(ctx)
|
||||
handle_mail(ctx[:message][:data])
|
||||
end
|
||||
|
||||
def find_supplier(recipient)
|
||||
m = /\A#{Regexp.escape(ENV['MAILER_PREFIX'])}(?<supplier_id>\d+)\.(?<hash>\w+)(@(?<hostname>.*))?/.match(recipient)
|
||||
raise "recipient is missing or has an invalid format" if m.nil?
|
||||
|
||||
supplier = Supplier.mail_sync.find_by_id(m[:supplier_id])
|
||||
raise "supplier id #{m[:supplier_id]} could not be found" if supplier.nil?
|
||||
|
||||
hash = supplier.articles_mail_hash
|
||||
hash.casecmp(m[:hash]) == 0 or raise "hash '#{m[:hash]}' does not match expectations for supplier '#{supplier.name}'"
|
||||
|
||||
supplier
|
||||
end
|
||||
|
||||
def handle_mail(data)
|
||||
message = Mail.read_from_string(data)
|
||||
|
||||
# message checks
|
||||
if @supplier.mail_from.present?
|
||||
m, s = message.from, @supplier.mail_from
|
||||
m.any? {|n| n.include?(s) } or raise "Expected to find '#{s}' in from address '#{m.join(', ')}' for supplier #{@supplier.name}"
|
||||
end
|
||||
if @supplier.mail_subject.present?
|
||||
m, s = message.subject, @supplier.mail_subject
|
||||
m.downcase.include?(s.downcase) or raise "Expected to find '#{s}' in subject '#{m}' for supplier #{@supplier.name}"
|
||||
end
|
||||
|
||||
# get attachment
|
||||
filename = nil
|
||||
message.attachments.each do |part|
|
||||
# @todo perhaps get heuristic from article import filters?
|
||||
if part.filename.match(/\.(xls|xlsx|ods|sxc|csv|tsv|xml)$/i)
|
||||
FileUtils.mkdir_p(@supplier.mail_path)
|
||||
filename = "#{message.date.strftime '%Y%m%d'}_#{part.filename.gsub(/[^-a-z0-9_\.]+/i, '_')}"
|
||||
filename = @supplier.mail_path.join(filename)
|
||||
File.open(filename, 'w+b') { |f| f.write part.body.decoded }
|
||||
end
|
||||
end
|
||||
|
||||
raise "No spreadsheet attachment found" unless filename.present?
|
||||
|
||||
# import!
|
||||
outlisted_counter, new_counter, updated_counter, invalid_articles =
|
||||
@supplier.update_articles_from_file(File.new(filename), type: @supplier.mail_type)
|
||||
|
||||
msg = "Handled articles update email for #{@supplier.name}: "
|
||||
msg += "#{new_counter} new, #{updated_counter} updated, #{outlisted_counter} outlisted, #{invalid_articles.size} invalid"
|
||||
invalid_articles.map do |article|
|
||||
msg += "\n* invalid article '#{article.name}'"
|
||||
article.errors.each do |attr, errmsg|
|
||||
errmsg.split("\n").each do |l|
|
||||
msg += "\n - #{': ' unless attr.blank?}" + l
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
logger.info(msg)
|
||||
end
|
||||
end
|
||||
|
||||
namespace :mail do
|
||||
desc "Parse incoming email on stdin (options: RECIPIENT=1.a1b2c3d3e5)"
|
||||
task parse_reply_email: :environment do
|
||||
hande_mail(ENV['RECIPIENT'], STDIN.read)
|
||||
end
|
||||
|
||||
desc "Start STMP server for incoming email (options: SMTP_SERVER_PORT=2525, SMTP_SERVER_HOST=127.0.0.1)"
|
||||
task :smtp_server => :environment do
|
||||
port = (ENV['SMTP_SERVER_PORT'] || 2525).to_i
|
||||
host = ENV['SMTP_SERVER_HOST'] || '127.0.0.1'
|
||||
rake_say "Started SMTP server for incoming email on #{host}:#{port}."
|
||||
server = ReplyEmailSmtpServer.new(port, host)
|
||||
server.start
|
||||
server.join
|
||||
end
|
||||
end
|
||||
|
||||
# Helper
|
||||
def rake_say(message)
|
||||
puts message unless Rake.application.options.silent
|
||||
end
|
Loading…
Reference in a new issue