Skip to content

Instantly share code, notes, and snippets.

@Mozart2234
Last active September 21, 2023 15:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mozart2234/f51394b345a16cc392c59bcf2891ab5a to your computer and use it in GitHub Desktop.
Save Mozart2234/f51394b345a16cc392c59bcf2891ab5a to your computer and use it in GitHub Desktop.
migrator library
require_relative "./row"
class Migrator::Base
def initialize(configs:)
@configs = configs
@headers = configs.map { |config| config[:name] }
end
def valid_row(values)
row = Migrator::Row.new(configs: @configs, values: values)
row.validate
row
end
def valid_headers?(headers)
headers.size == @headers.size && headers.to_set == @headers.to_set
end
end
module MigrationsService::V2::FuelCards::Template::Config
BASE = [
{
name: I18n.t('services.migrations.fuel_cards.headers.movement_type'),
example: 'RECARGA / DECREMENTO',
validations: { presence: true, inclusion: { in: %w{RECARGA DECREMENTO}, case_sensitive: false } }
},
{
name: I18n.t('services.migrations.fuel_cards.headers.client_id'),
example: '5290',
validations: { presence: true }
},
{
name: I18n.t('services.migrations.fuel_cards.headers.proxy_number'),
example: '21788347423',
validations: { presence: true }
},
{
name: I18n.t('services.migrations.fuel_cards.headers.product'),
example: 'PRE-PAGO',
validations: { presence: true }
},
{
name: I18n.t('services.migrations.fuel_cards.headers.amount'),
example: '200',
validations: { presence: true }
}
].freeze
INFO = [
{
name: I18n.t("services.migrations.fuel_cards.info.column_disclaimer"),
style: { b: true }
},
{
name: I18n.t("services.migrations.fuel_cards.info.example_disclaimer"),
style: { i: true }
}
].freeze
end
class MigrationsService::V2::FuelCards::Migrate < MigrationsService::V2::Main
EXTRA_HEADERS = [
I18n.t('migrations_service.main.headers.state'),
I18n.t('migrations_service.main.headers.information')
].freeze
@client_id = nil
@api_token = nil
def call
url_download = download_file
configs = MigrationsService::V2::FuelCards::Template::Config::BASE
# Base code
migrator = Migrator::Base.new(configs: configs)
xlsx = Roo::Spreadsheet.open(url_download.to_s, extension: :xlsx)
headers = xlsx.row(1)
@status[:total] = xlsx.last_row - 1
@migration_service.update(total: @status[:total], status: 'migrating')
unless valid_headers?(migrator, headers)
handle_error
return nil
end
response_xlsx = migrate_data(xlsx, migrator)
route_file = MigrationsService::V2::GenerateXlsx.new(
response_xlsx, headers.concat(EXTRA_HEADERS), @migration_service.file_identifier
).call
@migration_service.response_file = Pathname.new(route_file).open
@migration_service.status = 'completed'
@migration_service.completed = @status[:completed]
@migration_service.warnings = @status[:warnings]
@migration_service.total_errors = @status[:errors]
@migration_service.total = response_xlsx.size
@migration_service.save
delete_complement_file
File.delete(url_download)
File.delete(route_file) if route_file.present?
flag = @migration_service.status == 'completed' ? 'green' : 'red'
create_notification('fuel_cards', 'migrations', flag)
@migration_service
end
private
def handle_error
@migration_service.total = 0
@migration_service.status = 'failed'
@migration_service.type_fault = 'layout'
@migration_service.completed = @status[:completed]
@migration_service.warnings = @status[:warnings]
@migration_service.total_errors = @status[:errors]
@migration_service.save
end
def valid_headers?(migrator, headers)
migrator.valid_headers?(headers)
end
def migrate_data(xlsx, migrator)
render_xlsx_data = []
xlsx.parse(headers: true, clean: true).drop(1).each do |row|
# Initialize Variables
feedback_messages = []
res_row = migrator.valid_row(row)
unless res_row.valid?
feedback_messages << res_row.errors.join(',')
render_xlsx_data << format_response_row(row, feedback_messages, 'error')
next
end
# Logic to migrate data from the XLSX -- Hidden because of data privacy
unless response.success?
feedback_messages << response.message
if response["errors"].present?
response["errors"].values.each do |m|
feedback_messages << m
end
end
render_xlsx_data << format_response_row(row, feedback_messages, 'error')
next
end
render_xlsx_data.push(format_response_row(row, feedback_messages, 'ok'))
end
render_xlsx_data
end
def format_response_row(row, feedback_messages, status)
global_status = case status
when 'ok' then :completed
when 'warning' then :warnings
when 'error' then :errors
end
@status[global_status] += 1
{
status: status,
row_data: row.values.concat([I18n.t("migrations_service.statuses.#{status}"), feedback_messages.join(', ')])
}
end
end
class Migrator::Row
attr_reader :errors
def initialize(configs: {}, values: {})
@configs = configs
@errors = []
@values = values
end
def validate
@configs.each do |config|
do_actions(config)
do_validations(config)
end
end
def valid?
@errors.empty?
end
private
def do_actions(config)
return if config[:actions].nil?
actions = config[:actions]
actions.each do |action|
do_action(action, config[:name])
end
end
def do_validations(config)
return if config[:validations].nil? || !config[:validations][:presence]
config[:validations].keys.each do |validation|
do_validate(validation, config, config[:name])
end
end
def do_action(action, key)
value = @values[key]
if action == :strip
@values[key] = value.to_s.strip
end
end
def do_validate(action, config, key)
value = @values[key]
case action
when :presence
if value.blank?
@errors.push(I18n.t("migrator.row.validations.errors.#{action}", name: key))
end
when :is_email?
if value.present? && !value.to_s.match?(URI::MailTo::EMAIL_REGEXP)
@errors.push(I18n.t("migrator.row.validations.errors.#{action}", name: key))
end
when :is_date?
if value.present? && !value.is_a?(Date)
@errors.push(I18n.t("migrator.row.validations.errors.#{action}", name: key))
end
when :is_time?
if value.present? && !value.is_a?(Time)
@errors.push(I18n.t("migrator.row.validations.errors.#{action}", name: key))
end
when :is_datetime?
if value.present? && !value.is_a?(DateTime)
@errors.push(I18n.t("migrator.row.validations.errors.#{action}", name: key))
end
when :is_a_pk?
if value.present? && value.to_i <= 0
@errors.push(I18n.t("migrator.row.validations.errors.#{action}", name: key))
end
when :inclusion
permitted_values = config[:validations][action][:in]
unless config[:validations][action][:case_sensitive]
value = value.to_s.downcase
permitted_values = permitted_values.map(&:downcase)
end
if value.present? && permitted_values.exclude?(value.to_s)
message = I18n.t("migrator.row.validations.errors.#{action}",
name: key,
values: permitted_values.join('/'))
@errors.push(message)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment