Class: Spree::Import
- Inherits:
-
Object
- Object
- Spree::Import
- Includes:
- NumberIdentifier
- Defined in:
- app/models/spree/import.rb
Direct Known Subclasses
Spree::Imports::Customers, Spree::Imports::ProductTranslations, Spree::Imports::Products
Class Method Summary collapse
-
.available_models ⇒ Array<Class>
Returns the available models for the import.
-
.available_types ⇒ Array<Class>
Returns the available types for the import.
-
.model_class ⇒ Object
eg.
-
.required_scope ⇒ Symbol?
Admin API scope family gating this import type — an import is a bulk write, so an API key needs
write_<required_scope>to create and manage it. -
.type_for_model(model) ⇒ Class
Returns the type for the model.
Instance Method Summary collapse
-
#attachment_file_content ⇒ String
Returns the content of the attachment file.
-
#can_be_deleted? ⇒ Boolean
Imports are deletable only while no processing jobs can be in flight — CreateRowsJob/ProcessGroupJob re-load the record mid-run.
-
#complete? ⇒ Boolean
Returns true if the import is complete.
-
#create_mappings ⇒ Object
Creates mappings from the schema fields TODO: get mappings from the previous import if it exists, so user won't have to map the same columns again.
-
#create_rows_async ⇒ void
Creates rows asynchronously.
-
#csv_headers ⇒ Array<String>
Returns the headers of the csv file.
-
#current_ability ⇒ Spree::Ability
Returns the current ability for the import.
-
#display_name ⇒ String
Returns the display name for the import.
- #event_serializer_class ⇒ Object
-
#group_column ⇒ String?
Returns the schema field name used to group rows for parallel processing.
-
#import_schema ⇒ Spree::ImportSchema
Returns the import schema for the import.
-
#large_import? ⇒ Boolean
Returns true if the import has more rows than the large import threshold.
-
#mapped_fields ⇒ Array<String>
Returns the mapped fields for the import schema.
-
#mapping? ⇒ Boolean
Returns true if the import is in mapping state.
-
#mapping_done? ⇒ Boolean
Returns true if the mapping is done.
-
#model_class ⇒ Class
Returns the model class for the import.
-
#process_rows_async ⇒ void
Processes rows asynchronously.
-
#processing? ⇒ Boolean
Returns true if the import is processing or completed mapping.
- #publish_import_completed_event ⇒ Object
-
#required_fields ⇒ Array<String>
Returns the required fields for the import schema.
-
#results_page_url ⇒ String?
URL of the wizard/results view for this import — the caller-provided
results_urlwith the wizard's?import=param appended (a no-op for the legacy admin's show URL, whose path already identifies the import). -
#results_url ⇒ Object
Public API name for the
results_urlpreference (read/write symmetry). - #results_url=(value) ⇒ Object
-
#row_lookup_cache ⇒ Hash
Per-instance cache shared by row processors within a single processing job.
-
#row_processor_class ⇒ Class
Returns the row processor class for the import.
-
#rows_status_counts ⇒ Hash{String => Integer}
Per-status row counts for API status payloads, memoized so one request issues a single grouped COUNT instead of one per status.
-
#sample_row ⇒ Hash
First data row as { header => value }, reading a single line of the attached CSV — used by the mapping UI to show example values.
-
#schema_fields ⇒ Array<Hash>
Returns the fields for the import schema If model supports metafields, it will include the metafield definitions for this model.
-
#store ⇒ Spree::Store
Returns the store for the import.
- #touch_store ⇒ Object
-
#unmapped_file_columns ⇒ Array<String>
Returns the file columns that are not mapped.
Class Method Details
.available_models ⇒ Array<Class>
Returns the available models for the import
339 340 341 |
# File 'app/models/spree/import.rb', line 339 def available_models available_types.map(&:model_class) end |
.available_types ⇒ Array<Class>
Returns the available types for the import
333 334 335 |
# File 'app/models/spree/import.rb', line 333 def available_types Spree.import_types end |
.model_class ⇒ Object
eg. Spree::Imports::Orders => Spree::Order
364 365 366 367 368 369 370 371 372 |
# File 'app/models/spree/import.rb', line 364 def model_class return Spree.user_class if to_s == 'Spree::Imports::Customers' klass = "Spree::#{to_s.demodulize.singularize}".safe_constantize raise NameError, "Missing model class for #{self}" unless klass klass end |
.required_scope ⇒ Symbol?
Admin API scope family gating this import type — an import is a bulk
write, so an API key needs write_<required_scope> to create and
manage it. Derived from the class name (Spree::Imports::Customers =>
:customers); override in subclasses gated by a different scope.
Returns nil on the base class, so unmapped types are only accessible
to write_all keys.
357 358 359 360 361 |
# File 'app/models/spree/import.rb', line 357 def required_scope return nil if self == Spree::Import to_s.demodulize.underscore.to_sym end |
.type_for_model(model) ⇒ Class
Returns the type for the model
345 346 347 |
# File 'app/models/spree/import.rb', line 345 def type_for_model(model) available_types.find { |type| type.model_class.to_s == model.to_s } end |
Instance Method Details
#attachment_file_content ⇒ String
Returns the content of the attachment file
275 276 277 |
# File 'app/models/spree/import.rb', line 275 def @attachment_file_content ||= .attached? ? .blob.download&.force_encoding('UTF-8') : nil end |
#can_be_deleted? ⇒ Boolean
Imports are deletable only while no processing jobs can be in flight — CreateRowsJob/ProcessGroupJob re-load the record mid-run.
132 133 134 |
# File 'app/models/spree/import.rb', line 132 def can_be_deleted? %w[pending mapping completed failed].include?(status) end |
#complete? ⇒ Boolean
Returns true if the import is complete
125 126 127 |
# File 'app/models/spree/import.rb', line 125 def complete? status == 'completed' end |
#create_mappings ⇒ Object
Creates mappings from the schema fields TODO: get mappings from the previous import if it exists, so user won't have to map the same columns again
281 282 283 284 285 286 287 |
# File 'app/models/spree/import.rb', line 281 def create_mappings schema_fields.each do |schema_field| mapping = mappings.find_or_create_by!(schema_field: schema_field[:name]) mapping.try_to_auto_assign_file_column(csv_headers) mapping.save! end end |
#create_rows_async ⇒ void
This method returns an undefined value.
Creates rows asynchronously
291 292 293 |
# File 'app/models/spree/import.rb', line 291 def create_rows_async Spree::Imports::CreateRowsJob.set(wait: 2.seconds).perform_later(id) end |
#csv_headers ⇒ Array<String>
Returns the headers of the csv file
264 265 266 267 268 269 270 271 |
# File 'app/models/spree/import.rb', line 264 def csv_headers return [] if .blank? @csv_headers ||= ::CSV.parse_line( , col_sep: preferred_delimiter ) end |
#current_ability ⇒ Spree::Ability
Returns the current ability for the import
313 314 315 |
# File 'app/models/spree/import.rb', line 313 def current_ability @current_ability ||= Spree.ability_class.new(user, { store: store }) end |
#display_name ⇒ String
Returns the display name for the import
250 251 252 |
# File 'app/models/spree/import.rb', line 250 def display_name "#{Spree.t(type.demodulize.pluralize.downcase)} #{number}" end |
#event_serializer_class ⇒ Object
326 327 328 |
# File 'app/models/spree/import.rb', line 326 def event_serializer_class 'Spree::Api::V3::ImportSerializer'.safe_constantize end |
#group_column ⇒ String?
Returns the schema field name used to group rows for parallel processing. Rows sharing the same value in this field are processed together in one job. Returns nil for imports where rows are independent (default — batched in chunks).
107 108 109 |
# File 'app/models/spree/import.rb', line 107 def group_column nil end |
#import_schema ⇒ Spree::ImportSchema
Returns the import schema for the import
194 195 196 |
# File 'app/models/spree/import.rb', line 194 def import_schema "Spree::ImportSchemas::#{type.demodulize}".safe_constantize.new end |
#large_import? ⇒ Boolean
Returns true if the import has more rows than the large import threshold. Large imports skip per-row UI broadcasts and use bulk processing.
99 100 101 |
# File 'app/models/spree/import.rb', line 99 def large_import? rows_count >= Spree::Config[:large_import_threshold] end |
#mapped_fields ⇒ Array<String>
Returns the mapped fields for the import schema
238 239 240 |
# File 'app/models/spree/import.rb', line 238 def mapped_fields @mapped_fields ||= mappings.mapped.where(schema_field: required_fields) end |
#mapping? ⇒ Boolean
Returns true if the import is in mapping state
113 114 115 |
# File 'app/models/spree/import.rb', line 113 def mapping? status == 'mapping' end |
#mapping_done? ⇒ Boolean
Returns true if the mapping is done
244 245 246 |
# File 'app/models/spree/import.rb', line 244 def mapping_done? mapped_fields.count == required_fields.count end |
#model_class ⇒ Class
Returns the model class for the import
184 185 186 187 188 189 190 |
# File 'app/models/spree/import.rb', line 184 def model_class if type == 'Spree::Imports::Customers' Spree.user_class else "Spree::#{type.demodulize.singularize}".safe_constantize end end |
#process_rows_async ⇒ void
This method returns an undefined value.
Processes rows asynchronously
297 298 299 |
# File 'app/models/spree/import.rb', line 297 def process_rows_async Spree::Imports::ProcessRowsJob.perform_later(id) end |
#processing? ⇒ Boolean
Returns true if the import is processing or completed mapping
119 120 121 |
# File 'app/models/spree/import.rb', line 119 def processing? ['processing', 'completed_mapping'].include?(status) end |
#publish_import_completed_event ⇒ Object
258 259 260 |
# File 'app/models/spree/import.rb', line 258 def publish_import_completed_event publish_event('import.completed') end |
#required_fields ⇒ Array<String>
Returns the required fields for the import schema
232 233 234 |
# File 'app/models/spree/import.rb', line 232 def required_fields import_schema.required_fields end |
#results_page_url ⇒ String?
URL of the wizard/results view for this import — the caller-provided
results_url with the wizard's ?import= param appended (a no-op for
the legacy admin's show URL, whose path already identifies the import).
Nil when the preference is absent; the mailer then renders no link.
158 159 160 161 162 163 164 165 166 167 168 |
# File 'app/models/spree/import.rb', line 158 def results_page_url return if preferred_results_url.blank? uri = URI.parse(preferred_results_url) params = URI.decode_www_form(uri.query.to_s) params << ['import', prefixed_id] uri.query = URI.encode_www_form(params) uri.to_s rescue URI::InvalidURIError nil end |
#results_url ⇒ Object
Public API name for the results_url preference (read/write symmetry).
String preferences round-trip nil as "" — normalize blank to nil.
145 146 147 |
# File 'app/models/spree/import.rb', line 145 def results_url preferred_results_url.presence end |
#results_url=(value) ⇒ Object
149 150 151 |
# File 'app/models/spree/import.rb', line 149 def results_url=(value) self.preferred_results_url = value end |
#row_lookup_cache ⇒ Hash
Per-instance cache shared by row processors within a single processing job. Group jobs funnel every row through the same Import instance, so lookups of shared records (tax/shipping categories, option types, metafield definitions) resolve once per job instead of once per row.
322 323 324 |
# File 'app/models/spree/import.rb', line 322 def row_lookup_cache @row_lookup_cache ||= {} end |
#row_processor_class ⇒ Class
Returns the row processor class for the import
200 201 202 |
# File 'app/models/spree/import.rb', line 200 def row_processor_class "Spree::ImportRowProcessors::#{type.demodulize.singularize}".safe_constantize end |
#rows_status_counts ⇒ Hash{String => Integer}
Per-status row counts for API status payloads, memoized so one request issues a single grouped COUNT instead of one per status.
139 140 141 |
# File 'app/models/spree/import.rb', line 139 def rows_status_counts @rows_status_counts ||= rows.group(:status).count end |
#sample_row ⇒ Hash
First data row as { header => value }, reading a single line of the attached CSV — used by the mapping UI to show example values.
173 174 175 176 177 178 179 180 |
# File 'app/models/spree/import.rb', line 173 def sample_row return {} if .blank? row = ::CSV.foreach(StringIO.new(), headers: true, col_sep: preferred_delimiter).first row&.to_h || {} rescue ::CSV::MalformedCSVError, EncodingError {} end |
#schema_fields ⇒ Array<Hash>
Returns the fields for the import schema If model supports metafields, it will include the metafield definitions for this model
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'app/models/spree/import.rb', line 207 def schema_fields base_fields = import_schema.fields # Dynamically add metafield definitions if the model supports metafields if = .map do |definition| { name: definition.csv_header_name, label: definition.name } end base_fields + else base_fields end end |
#store ⇒ Spree::Store
Returns the store for the import
303 304 305 306 307 308 309 |
# File 'app/models/spree/import.rb', line 303 def store if owner.is_a?(Spree::Store) owner else owner.respond_to?(:store) ? owner.store : Spree::Store.default end end |
#touch_store ⇒ Object
254 255 256 |
# File 'app/models/spree/import.rb', line 254 def touch_store store.touch end |
#unmapped_file_columns ⇒ Array<String>
Returns the file columns that are not mapped
226 227 228 |
# File 'app/models/spree/import.rb', line 226 def unmapped_file_columns csv_headers.reject { |header| mappings.mapped.exists?(file_column: header) } end |