demographic 0.8.6.RKM.89602.beta.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +479 -0
- data/Rakefile +22 -0
- data/app/controllers/demographic/application_controller.rb +87 -0
- data/app/controllers/demographic/custom_parameter_values_controller.rb +25 -0
- data/app/controllers/demographic/optionals_bulk_controller.rb +33 -0
- data/app/controllers/demographic/optionals_controller.rb +68 -0
- data/app/controllers/demographic/optionals_types_controller.rb +17 -0
- data/app/controllers/demographic/user_optionals_controller.rb +12 -0
- data/app/jobs/demographic/application_job.rb +4 -0
- data/app/mailers/demographic/application_mailer.rb +6 -0
- data/app/serializers/demographic/optionals/filter_list_serializer.rb +34 -0
- data/app/serializers/demographic/optionals/list_serializer.rb +17 -0
- data/app/serializers/demographic/optionals/optional_serializer.rb +20 -0
- data/app/serializers/demographic/optionals/value_serializer.rb +5 -0
- data/app/services/demographic/custom_parameter_values/index.rb +53 -0
- data/app/services/demographic/helpers/association_localizer.rb +13 -0
- data/app/services/demographic/helpers/import/bulk/build_optionals.rb +54 -0
- data/app/services/demographic/helpers/import/bulk/create_optional_values.rb +32 -0
- data/app/services/demographic/helpers/import/bulk/create_or_update_user_optionals.rb +110 -0
- data/app/services/demographic/helpers/import/create_optionals_list.rb +60 -0
- data/app/services/demographic/helpers/import/create_or_update_custom_parameter_values.rb +97 -0
- data/app/services/demographic/helpers/import/create_or_update_user_values.rb +161 -0
- data/app/services/demographic/helpers/import/create_or_update_values.rb +124 -0
- data/app/services/demographic/optional_values/create_list.rb +61 -0
- data/app/services/demographic/optional_values/update_list.rb +85 -0
- data/app/services/demographic/optionals/bulk_create.rb +115 -0
- data/app/services/demographic/optionals/bulk_create_validations.rb +122 -0
- data/app/services/demographic/optionals/create.rb +90 -0
- data/app/services/demographic/optionals/list.rb +51 -0
- data/app/services/demographic/optionals/optional_values.rb +52 -0
- data/app/services/demographic/optionals/update.rb +100 -0
- data/app/services/demographic/parse/data_serializer.rb +87 -0
- data/app/services/demographic/parse/types/values.rb +99 -0
- data/app/services/demographic/user_optionals/list.rb +60 -0
- data/app/services/import/templates/create_optionals.rb +26 -0
- data/app/services/import/templates/xlsx.rb +44 -0
- data/config/initializers/custom_classes/string.rb +11 -0
- data/config/routes.rb +38 -0
- data/db/migrate/20201207181535_create_demographic_optionals.rb +18 -0
- data/db/migrate/20201207184210_create_users.rb +12 -0
- data/db/migrate/20201211204926_create_demographic_demographic_optional_values.rb +15 -0
- data/db/migrate/20201215193056_create_demographic_user_optional_values.rb +11 -0
- data/db/migrate/20220427163444_add_token_to_optionals.rb +6 -0
- data/db/migrate/20220427163507_add_token_to_optional_values.rb +6 -0
- data/lib/demographic/engine.rb +17 -0
- data/lib/demographic/version.rb +5 -0
- data/lib/demographic.rb +78 -0
- data/lib/generators/init/USAGE +8 -0
- data/lib/generators/init/init_generator.rb +11 -0
- data/lib/generators/init/templates/init_demographic.rb +14 -0
- data/lib/generators/init/templates/policy_demographic.rb +70 -0
- data/lib/generators/init_manual_update/USAGE +8 -0
- data/lib/generators/init_manual_update/init_manual_update_generator.rb +12 -0
- data/lib/generators/init_manual_update/templates/20220427163444_add_token_to_optionals.rb +6 -0
- data/lib/generators/init_manual_update/templates/20220427163507_add_token_to_optional_values.rb +6 -0
- data/lib/generators/init_manual_update/templates/demographic_update_tokens.rake +13 -0
- data/lib/generators/init_manual_update/templates/demographic_update_tokens_by_tenants.rake +24 -0
- data/lib/generators/init_manual_update_v2/init_manual_update_generator.rb +10 -0
- data/lib/generators/init_manual_update_v2/templates/20240206120304_create_custom_parameter_values.rb +10 -0
- data/lib/generators/init_manual_update_v2/templates/custom_parameter_value.rb +5 -0
- data/lib/generators/init_model/USAGE +8 -0
- data/lib/generators/init_model/init_model_generator.rb +12 -0
- data/lib/generators/init_model/templates/optional.rb +5 -0
- data/lib/generators/init_model/templates/optional_value.rb +4 -0
- data/lib/generators/init_model/templates/user.rb +5 -0
- data/lib/generators/init_model/templates/user_optional.rb +5 -0
- data/lib/tasks/demographic_tasks.rake +4 -0
- metadata +346 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Demographic
|
|
3
|
+
module Helpers
|
|
4
|
+
module Import
|
|
5
|
+
class CreateOrUpdateUserValues
|
|
6
|
+
# optional = "{optional_id, optional_value_id, value:}"
|
|
7
|
+
|
|
8
|
+
def self.call(user:, optionals:, parent_module:, reference_id:)
|
|
9
|
+
return { success: false, message: 'error has not optionls!!!' } unless optionals.present?
|
|
10
|
+
|
|
11
|
+
adjustable = parent_model(parent_module).to_s.capitalize.classify.constantize.find_by!(id: reference_id)
|
|
12
|
+
|
|
13
|
+
optionals_array = []
|
|
14
|
+
optional_ids = optionals.map { |element| element[:optional_id] }.compact
|
|
15
|
+
|
|
16
|
+
optionals_hash = adjustable.optionals.where(id: optional_ids).select(:id, :input_type).index_by(&:id)
|
|
17
|
+
|
|
18
|
+
user_optionals_hash = user.user_optionals.where(optional_id: optional_ids).index_by(&:optional_id)
|
|
19
|
+
|
|
20
|
+
optional_value_ids = optionals.map { |element| element[:optional_value_id] }.compact
|
|
21
|
+
optional_values_hash = ::OptionalValue.where(id: optional_value_ids).select(:id, :value).index_by(&:id)
|
|
22
|
+
simple_inputs_hash = Demographic.simple_inputs.index_with { |_s| true }
|
|
23
|
+
|
|
24
|
+
optionals.each do |element|
|
|
25
|
+
next unless element[:optional_id].present?
|
|
26
|
+
optional_id = element[:optional_id].to_i
|
|
27
|
+
optional = optionals_hash[optional_id]
|
|
28
|
+
# exception for replace find_by!
|
|
29
|
+
raise StandardError, "Optional not found #{element}" unless optional.present?
|
|
30
|
+
|
|
31
|
+
user_optional = user_optionals_hash[optional_id]
|
|
32
|
+
|
|
33
|
+
if simple_inputs_hash[optional.input_type]
|
|
34
|
+
current_value = format_value(element[:value], optional)
|
|
35
|
+
optional_attrs = simple_input_v2(user.id, optional_id, user_optional, current_value)
|
|
36
|
+
else
|
|
37
|
+
optional_value = optional_values_hash[element[:optional_value_id]&.to_i]
|
|
38
|
+
optional_attrs = multiple_input_v2(user.id, optional_id, user_optional, optional_value)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
optionals_array << optional_attrs
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
{ success: true, message: 'Success!!!', optionals: optionals_array }
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
{ success: false, message: e.to_s, optionals: [] }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def self.multiple_input(user, optional, element)
|
|
52
|
+
user_optional = user.user_optionals.find_by(optional_id: optional.id)
|
|
53
|
+
|
|
54
|
+
if user_optional.present? && !element[:optional_value_id].present?
|
|
55
|
+
user_optional.destroy!
|
|
56
|
+
return {}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
return {} unless element[:optional_value_id].present?
|
|
60
|
+
|
|
61
|
+
optional_value = optional.optional_values.find_by(id: element[:optional_value_id])
|
|
62
|
+
return {} unless optional_value.present?
|
|
63
|
+
|
|
64
|
+
user_optional ||= user.user_optionals.new
|
|
65
|
+
user_optional.optional_id = optional.id
|
|
66
|
+
user_optional.optional_value_id = optional_value.id
|
|
67
|
+
user_optional.value = optional_value.value
|
|
68
|
+
user_optional.save!
|
|
69
|
+
|
|
70
|
+
{ optional_user_id: user_optional.id, optional_value_id: optional_value.id }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.multiple_input_v2(user_id, optional_id, user_optional, optional_value)
|
|
74
|
+
if optional_value.nil?
|
|
75
|
+
user_optional&.destroy!
|
|
76
|
+
return {}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
user_optional ||= UserOptional.new
|
|
80
|
+
user_optional.user_id = user_id
|
|
81
|
+
user_optional.optional_id = optional_id
|
|
82
|
+
user_optional.optional_value_id = optional_value.id
|
|
83
|
+
user_optional.value = optional_value.value
|
|
84
|
+
user_optional.save!
|
|
85
|
+
|
|
86
|
+
{ optional_id: optional_id, optional_user_id: user_optional.id, optional_value_id: optional_value.id }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.simple_input(user, optional, element)
|
|
90
|
+
current_value = format_value(element[:value], optional)
|
|
91
|
+
user_optional = user.user_optionals.find_by(optional_id: optional.id)
|
|
92
|
+
|
|
93
|
+
if user_optional.present? && !current_value.present?
|
|
94
|
+
user_optional.destroy!
|
|
95
|
+
return {}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
return {} unless current_value.present?
|
|
99
|
+
|
|
100
|
+
code = Demographic.generate_code(optional.id, current_value)
|
|
101
|
+
optional_value = optional.optional_values.find_or_create_by(slug: current_value.str_slug,
|
|
102
|
+
code: code) do |optional_value|
|
|
103
|
+
optional_value.token = Demographic.generate_token
|
|
104
|
+
optional_value.value = current_value
|
|
105
|
+
optional_value.value_translate.merge!({ I18n.locale => current_value })
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
user_optional ||= user.user_optionals.new
|
|
109
|
+
user_optional.optional_id = optional.id
|
|
110
|
+
user_optional.optional_value_id = optional_value.id
|
|
111
|
+
user_optional.value = optional_value.value
|
|
112
|
+
user_optional.save!
|
|
113
|
+
|
|
114
|
+
{ optional_user_id: user_optional.id, optional_value_id: optional_value.id }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.simple_input_v2(user_id, optional_id, user_optional, current_value)
|
|
118
|
+
unless current_value.present?
|
|
119
|
+
user_optional&.destroy!
|
|
120
|
+
return {}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
code = Demographic.generate_code(optional_id, current_value)
|
|
124
|
+
optional_value = ::OptionalValue.find_or_create_by(optional_id: optional_id,
|
|
125
|
+
slug: current_value.str_slug,
|
|
126
|
+
code: code) do |optional_value|
|
|
127
|
+
optional_value.token = Demographic.generate_token
|
|
128
|
+
optional_value.value = current_value
|
|
129
|
+
optional_value.value_translate.merge!({ I18n.locale => current_value })
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# TODO: verificar impacto de quitar creacion de user_optinal para simple_input
|
|
133
|
+
user_optional ||= UserOptional.new
|
|
134
|
+
user_optional.user_id = user_id
|
|
135
|
+
user_optional.optional_id = optional_id
|
|
136
|
+
user_optional.optional_value_id = optional_value.id
|
|
137
|
+
user_optional.value = optional_value.value
|
|
138
|
+
user_optional.save!
|
|
139
|
+
|
|
140
|
+
{ optional_id: optional_id, optional_user_id: user_optional.id, optional_value_id: optional_value.id }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def self.parent_model(parent_module)
|
|
144
|
+
parent = Demographic.parent_model[parent_module.to_sym]
|
|
145
|
+
raise 'init Demographic parent_model error param :module!!!' unless parent.present?
|
|
146
|
+
|
|
147
|
+
parent
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def self.format_value(value, optional)
|
|
151
|
+
return unless value.present?
|
|
152
|
+
|
|
153
|
+
return value.to_s.to_date.strftime if optional.input_type.eql?('date')
|
|
154
|
+
|
|
155
|
+
value.to_s
|
|
156
|
+
rescue StandardError
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Demographic
|
|
4
|
+
module Helpers
|
|
5
|
+
module Import
|
|
6
|
+
class CreateOrUpdateValues
|
|
7
|
+
def self.call(user_data:, parent_module:, reference_id:)
|
|
8
|
+
@permit_user_data = user_data.is_a?(Hash) ? user_data : user_data.to_unsafe_hash
|
|
9
|
+
return { success: false, message: 'User data not present', optionals: [] } unless @permit_user_data.present?
|
|
10
|
+
|
|
11
|
+
optionals = optional_hash
|
|
12
|
+
return { success: false, message: 'Optionals Empty!!!', optionals: [] } unless optionals.present?
|
|
13
|
+
|
|
14
|
+
optional_codes = optionals.map { |element| element[:code]&.downcase }.compact
|
|
15
|
+
optionals_query = Optional.unscoped.where('lower(code) IN (?)', optional_codes).order(id: :desc)
|
|
16
|
+
optionals_hash = optionals_query.index_by{|optional| optional.code.downcase }
|
|
17
|
+
|
|
18
|
+
records = optionals.map do |element|
|
|
19
|
+
current_value = element[:value]
|
|
20
|
+
|
|
21
|
+
optional = optionals_hash[element[:code]&.downcase]
|
|
22
|
+
return { success: false, message: "Optional not found #{element}", optionals: [] } unless optional.present?
|
|
23
|
+
|
|
24
|
+
next { optional_id: optional.id, optional_value_id: nil, value: nil } unless current_value.present?
|
|
25
|
+
|
|
26
|
+
value = value_valid(optional, current_value)
|
|
27
|
+
unless value.is_valid?
|
|
28
|
+
return { success: false, message: "The value is not valid #{current_value}", optionals: [] }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
optional_value = find_or_create_by_value(optional, value.current)
|
|
32
|
+
|
|
33
|
+
{ optional_id: optional.id, optional_value_id: optional_value.id, value: value.current }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
{ success: true, optionals: records.compact }
|
|
37
|
+
rescue StandardError => e
|
|
38
|
+
{ success: false, message: e.to_s, optionals: [] }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def self.optional_hash
|
|
44
|
+
@permit_user_data.with_indifferent_access.map do |key, value|
|
|
45
|
+
code = key.to_s
|
|
46
|
+
next unless code.include?('optional_') && !key.nil?
|
|
47
|
+
|
|
48
|
+
name_type = code.gsub('optional_', '').split('#')
|
|
49
|
+
name = name_type.first
|
|
50
|
+
|
|
51
|
+
{
|
|
52
|
+
name: name,
|
|
53
|
+
code: name.parameterize.str_slug,
|
|
54
|
+
value: value
|
|
55
|
+
}
|
|
56
|
+
end.compact
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.parent_model(parent_module)
|
|
60
|
+
parent = Demographic.parent_model[parent_module.to_sym]
|
|
61
|
+
raise 'init Demographic parent_model error param :module!!!' unless parent.present?
|
|
62
|
+
|
|
63
|
+
parent
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.datetime?(d)
|
|
67
|
+
d.methods.include? :strftime
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.value_valid(optional, value)
|
|
71
|
+
struct = Struct.new(:is_valid?, :current)
|
|
72
|
+
|
|
73
|
+
return struct.new(true, value) if optional.input_type.eql?('list') || optional.input_type.eql?('text')
|
|
74
|
+
|
|
75
|
+
return struct.new(value.to_s.is_numeric?, value) if optional.input_type.eql?('number')
|
|
76
|
+
|
|
77
|
+
return conversion_date(value, struct) if optional.input_type.eql?('date')
|
|
78
|
+
|
|
79
|
+
return conversion_bool(value, struct) if optional.input_type.eql?('bool')
|
|
80
|
+
|
|
81
|
+
struct.new(false, value.to_s)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.conversion_bool(value, struct)
|
|
85
|
+
bool_hash = { '0' => 'false', '1' => 'true' }
|
|
86
|
+
|
|
87
|
+
bool = bool_hash[value.to_i.to_s]
|
|
88
|
+
|
|
89
|
+
struct.new(bool.present?, bool)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.conversion_date(value, struct)
|
|
93
|
+
formats = %w[%Y-%m-%d %Y/%m/%d %y-%m-%d %y/%m/%d %d-%m-%Y %d/%m/%Y %d-%m-%y %d/%m/%y]
|
|
94
|
+
|
|
95
|
+
formats.each do |format|
|
|
96
|
+
date = format_date(value, format)
|
|
97
|
+
return struct.new(true, date) if date != false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
struct.new(false, value.to_s)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.format_date(value, format)
|
|
104
|
+
Date.strptime(value, format).strftime
|
|
105
|
+
rescue StandardError
|
|
106
|
+
false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def self.find_or_create_by_value(optional, current_value)
|
|
110
|
+
code = Demographic.generate_code(optional.id, current_value.to_s)
|
|
111
|
+
optional.optional_values.find_or_create_by(slug: current_value.to_s.str_slug,
|
|
112
|
+
code: code) do |optional_value|
|
|
113
|
+
optional_value.token = Demographic.generate_token
|
|
114
|
+
optional_value.value = current_value
|
|
115
|
+
optional_value.value_translate = { es: current_value }
|
|
116
|
+
end
|
|
117
|
+
rescue ActiveRecord::RecordNotUnique
|
|
118
|
+
code = Demographic.generate_code(optional.id, current_value.to_s)
|
|
119
|
+
optional.optional_values.where('code ILIKE ?', code).first
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Demographic
|
|
2
|
+
module OptionalValues
|
|
3
|
+
class CreateList
|
|
4
|
+
|
|
5
|
+
attr_accessor :adjustable, :values, :optional
|
|
6
|
+
|
|
7
|
+
def initialize(adjustable:, optional:, values:)
|
|
8
|
+
@adjustable = adjustable
|
|
9
|
+
@optional = optional
|
|
10
|
+
@values = values
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
return true unless values.present?
|
|
15
|
+
|
|
16
|
+
ActiveRecord::Base.transaction do
|
|
17
|
+
validation_values
|
|
18
|
+
create_values
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def validation_values
|
|
25
|
+
values.each do |value|
|
|
26
|
+
if !value[:value_translate].present? || !value[:value_translate].values.any?{ |v| v.present? }
|
|
27
|
+
optional.errors.add(:value_translate, 'value_translate is empty')
|
|
28
|
+
raise ActiveRecord::RecordInvalid.new(optional)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def create_values
|
|
34
|
+
|
|
35
|
+
values.each do |value|
|
|
36
|
+
if value[:is_valid].present? && value[:is_valid].to_d == 0
|
|
37
|
+
next
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
current_value = current_value(value)
|
|
41
|
+
code = Demographic.generate_code(optional.id, current_value.str_slug)
|
|
42
|
+
|
|
43
|
+
optional.optional_values.find_or_create_by!(slug: current_value.str_slug,
|
|
44
|
+
code: code) do |optional_value|
|
|
45
|
+
optional_value.token = Demographic.generate_token
|
|
46
|
+
optional_value.value = current_value
|
|
47
|
+
optional_value.value_translate = value[:value_translate]
|
|
48
|
+
optional_value.order_num = value[:order_num]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def current_value(value)
|
|
54
|
+
return value[:value_translate]['es'] if value[:value_translate]['es'].present?
|
|
55
|
+
|
|
56
|
+
value[:value_translate].values.detect{|v| v.present? }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Demographic
|
|
2
|
+
module OptionalValues
|
|
3
|
+
class UpdateList
|
|
4
|
+
|
|
5
|
+
attr_accessor :adjustable, :values, :optional
|
|
6
|
+
|
|
7
|
+
def initialize(adjustable:, optional:, values:)
|
|
8
|
+
@adjustable = adjustable
|
|
9
|
+
@optional = optional
|
|
10
|
+
@values = values
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
return true unless values.present?
|
|
15
|
+
|
|
16
|
+
ActiveRecord::Base.transaction do
|
|
17
|
+
validation_values
|
|
18
|
+
delete_values
|
|
19
|
+
update_values
|
|
20
|
+
create_values
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def validation_values
|
|
28
|
+
values.each do |value|
|
|
29
|
+
if !value[:value_translate].present? || !value[:value_translate].values.any?{ |v| v.present? }
|
|
30
|
+
optional.errors.add(:value_translate, 'value_translate is empty')
|
|
31
|
+
raise ActiveRecord::RecordInvalid.new(optional)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def delete_values
|
|
37
|
+
values_ids = values.map{|v| v[:id] }.compact
|
|
38
|
+
return unless values_ids.present?
|
|
39
|
+
|
|
40
|
+
optional.optional_values.where.not(id: values_ids).destroy_all
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def update_values
|
|
44
|
+
values_ids = values.map{|v| v[:id] }.compact
|
|
45
|
+
return unless values_ids.present?
|
|
46
|
+
|
|
47
|
+
current_values = optional.optional_values.where(id: values_ids)
|
|
48
|
+
return unless current_values.present?
|
|
49
|
+
|
|
50
|
+
current_values.each do |optional_value|
|
|
51
|
+
value = values.detect{|v| v[:id] == optional_value.id}
|
|
52
|
+
current_value = current_value(value)
|
|
53
|
+
optional_value.code = Demographic.generate_code(optional.id, current_value.str_slug)
|
|
54
|
+
optional_value.slug = current_value.str_slug
|
|
55
|
+
optional_value.value = current_value
|
|
56
|
+
optional_value.value_translate = value[:value_translate]
|
|
57
|
+
optional_value.order_num = value[:order_num]
|
|
58
|
+
optional_value.save!
|
|
59
|
+
update_user_values(optional_value, current_value)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def create_values
|
|
64
|
+
new_values = values.select{|v| !v[:id].present? }
|
|
65
|
+
return unless new_values.present?
|
|
66
|
+
|
|
67
|
+
::Demographic::OptionalValues::CreateList.new(adjustable: adjustable,
|
|
68
|
+
values: new_values,
|
|
69
|
+
optional: optional).call
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def current_value(value)
|
|
73
|
+
return value[:value_translate]['es'] if value[:value_translate]['es'].present?
|
|
74
|
+
|
|
75
|
+
value[:value_translate].values.detect{|v| v.present? }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def update_user_values(optional_value, current_value)
|
|
79
|
+
optional.user_optionals
|
|
80
|
+
.where(optional_value_id: optional_value.id)
|
|
81
|
+
.update_all(value: current_value)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
module Demographic
|
|
2
|
+
module Optionals
|
|
3
|
+
class BulkCreate
|
|
4
|
+
attr_reader :file, :adjustable, :optionals_by_code
|
|
5
|
+
|
|
6
|
+
def initialize(file:, adjustable:)
|
|
7
|
+
@file = file
|
|
8
|
+
@adjustable = adjustable
|
|
9
|
+
@optionals_by_code = adjustable.optionals.index_by(&:code)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
return response(validations.error.data, validations.error.status) if validations.error.present?
|
|
14
|
+
|
|
15
|
+
response(create_bulk_import, 200)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def validations
|
|
21
|
+
@validations ||= ::Demographic::Optionals::BulkCreateValidations.new(file: file).call
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def create_bulk_import
|
|
25
|
+
return json_response.merge!(message: "No devuelve ids del bulk import") unless bulk_import.ids.present?
|
|
26
|
+
|
|
27
|
+
optionals = Optional.where(id: bulk_import.ids)
|
|
28
|
+
return json_response.merge!(message: "No devuelve optionals aun que tenemos los ids del bulk import") unless optionals.ids.present?
|
|
29
|
+
|
|
30
|
+
return json_response.merge!(message: "El module es incorrecto") unless change_module
|
|
31
|
+
run_create_callbacks(optionals)
|
|
32
|
+
|
|
33
|
+
json_response.merge!(message: "Success")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def bulk_import
|
|
37
|
+
@bulk_import ||= bulk_import_transaction
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def bulk_import_transaction
|
|
41
|
+
current_bulk_import = Optional.bulk_import(data_hash.values,
|
|
42
|
+
batch_size: 1000,
|
|
43
|
+
on_duplicate_key_ignore: true,
|
|
44
|
+
validate: true,
|
|
45
|
+
returning: [:id, :name, :input_type])
|
|
46
|
+
|
|
47
|
+
delay_transaction
|
|
48
|
+
current_bulk_import
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def json_response
|
|
52
|
+
@json_response ||= {
|
|
53
|
+
optionals_created: bulk_import.results
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def run_create_callbacks(optionals)
|
|
58
|
+
optionals.each { |optional| optional.run_callbacks(:create) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def data_hash
|
|
62
|
+
return @data_hash if @data_hash.present?
|
|
63
|
+
|
|
64
|
+
@data_hash = {}
|
|
65
|
+
|
|
66
|
+
validations.data.each do |row|
|
|
67
|
+
serialized_row = serialize_row(row)
|
|
68
|
+
@data_hash[serialized_row.code] = serialized_row
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
@data_hash
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def serialize_row(row)
|
|
75
|
+
base_optional_hash = validations.headers.zip(row).to_h
|
|
76
|
+
code = base_optional_hash['name'].parameterize.str_slug
|
|
77
|
+
|
|
78
|
+
optional_hash = {}.tap do |hash|
|
|
79
|
+
hash[:name] = base_optional_hash['name']
|
|
80
|
+
hash[:input_type] = base_optional_hash['type']
|
|
81
|
+
hash[:code] = base_optional_hash['code'] || code
|
|
82
|
+
hash[:token] = Demographic.generate_token
|
|
83
|
+
hash[:name_translate] = { es: base_optional_hash['name'] }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
optionals_by_code[code] || adjustable.optionals.new(optional_hash)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def delay_transaction
|
|
90
|
+
# We need to do this because the .bulk_import is causing issues on production, because in prod exists a delay
|
|
91
|
+
# for the bd replicas
|
|
92
|
+
sleep 1
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def response(data, status)
|
|
96
|
+
Struct.new(:data,
|
|
97
|
+
:status).new(data,
|
|
98
|
+
status)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def change_module
|
|
102
|
+
if base_optional_hash['module'].present?
|
|
103
|
+
parent_model = Demographic.parent_model[base_optional_hash['module'].to_sym]
|
|
104
|
+
model_class = parent_model.to_s.capitalize.classify.constantize
|
|
105
|
+
if adjustable.id == "0"
|
|
106
|
+
#non-persisted object to avoid errors downstream
|
|
107
|
+
return adjustable = model_class.new(id: 0)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
adjustable = model_class.find_by!(id: adjustable.id)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
module Demographic
|
|
2
|
+
module Optionals
|
|
3
|
+
class BulkCreateValidations
|
|
4
|
+
attr_reader :file
|
|
5
|
+
attr_accessor :data_hash, :headers, :xls
|
|
6
|
+
|
|
7
|
+
def initialize(file:)
|
|
8
|
+
@file = file
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
response(validate_file)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def validate_file
|
|
18
|
+
excel_validation = valid_excel(file)
|
|
19
|
+
return excel_validation if excel_validation.respond_to?(:error)
|
|
20
|
+
|
|
21
|
+
headers_validation = validate_headers
|
|
22
|
+
return headers_validation if headers_validation.respond_to?(:error)
|
|
23
|
+
|
|
24
|
+
data_validation = validate_data
|
|
25
|
+
return data_validation if data_validation.respond_to?(:error)
|
|
26
|
+
|
|
27
|
+
data_hash
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def valid_excel(excel_file)
|
|
31
|
+
ext = file.original_filename.split('.').last
|
|
32
|
+
|
|
33
|
+
unless %w[xls xlsx].include? ext
|
|
34
|
+
return wrong_file_format(excel_file.original_filename, 'Formato de archivo no válido. Solo XLSX y XLS')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
xls = (Roo::Spreadsheet.open excel_file.tempfile.path, extension: ext) rescue nil
|
|
38
|
+
|
|
39
|
+
if xls.nil?
|
|
40
|
+
inverse_ext = ext.eql?('xls') ? 'xlsx' : 'xls'
|
|
41
|
+
xls = Roo::Spreadsheet.open excel_file.tempfile.path, extension: inverse_ext
|
|
42
|
+
end
|
|
43
|
+
# Importante: construir las filas manualmente para NO perder la primera fila (cabeceras).
|
|
44
|
+
# Algunos métodos de parseo de Roo omiten la fila de cabecera por defecto.
|
|
45
|
+
sheet = xls.sheet(0)
|
|
46
|
+
rows = []
|
|
47
|
+
(sheet.first_row..sheet.last_row).each do |row_index|
|
|
48
|
+
rows << sheet.row(row_index)
|
|
49
|
+
end
|
|
50
|
+
@xls = rows
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def validate_headers
|
|
54
|
+
@headers = xls.shift
|
|
55
|
+
required_headers = ::Import::Templates::CreateOptionals::HEADERS
|
|
56
|
+
missing_headers = required_headers - headers
|
|
57
|
+
return missing_parameters("Faltan las siguientes cabeceras en el archivo: #{missing_headers.join(', ')}") unless missing_headers.empty?
|
|
58
|
+
|
|
59
|
+
@headers
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def validate_data
|
|
63
|
+
return missing_parameters("El archivo está vacío") if xls.blank?
|
|
64
|
+
|
|
65
|
+
@data_hash = []
|
|
66
|
+
data_errors = []
|
|
67
|
+
type_index = headers.index('type')
|
|
68
|
+
|
|
69
|
+
xls.each do |row|
|
|
70
|
+
unless row.all?
|
|
71
|
+
data_errors << row
|
|
72
|
+
next
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
unless available_types.include?(row[type_index])
|
|
76
|
+
data_errors << row
|
|
77
|
+
next
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
@data_hash << row
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
return missing_parameters("El archivo contiene errores en: #{parse_row_error(data_errors)}") if data_errors.present?
|
|
84
|
+
|
|
85
|
+
@data_hash
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def parse_row_error(rows)
|
|
89
|
+
rows.map { |pair| pair.join(': ') }.join(', ')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def available_types
|
|
93
|
+
Demographic.simple_inputs + Demographic.multiples_inputs
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Start Structs methods
|
|
97
|
+
def wrong_file_format(file_name, message)
|
|
98
|
+
data = { wrong_file_format?: true, file_name: file_name, message: message }
|
|
99
|
+
Struct.new(:data, :status, :error).new(data, 400, true)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def missing_parameters(message)
|
|
103
|
+
data = { missing_parameters?: true, message: message }
|
|
104
|
+
Struct.new(:data, :status, :error).new(data, 400, true)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def response(validations)
|
|
108
|
+
error = validations.respond_to?(:error)
|
|
109
|
+
params = {}.tap do |hash|
|
|
110
|
+
hash[:data] = error ? [] : data_hash
|
|
111
|
+
hash[:headers] = error ? [] : headers
|
|
112
|
+
hash[:error] = error ? validations : false
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
Struct.new(:data, :headers, :error).new(params[:data],
|
|
116
|
+
params[:headers],
|
|
117
|
+
params[:error])
|
|
118
|
+
end
|
|
119
|
+
# Ends Structs methods
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|