bulkrax 1.0.0 → 2.0.1

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/controllers/bulkrax/exporters_controller.rb +12 -4
  4. data/app/controllers/bulkrax/importers_controller.rb +22 -17
  5. data/app/factories/bulkrax/object_factory.rb +44 -61
  6. data/app/jobs/bulkrax/create_relationships_job.rb +187 -0
  7. data/app/jobs/bulkrax/delete_work_job.rb +6 -2
  8. data/app/jobs/bulkrax/export_work_job.rb +3 -1
  9. data/app/jobs/bulkrax/exporter_job.rb +1 -0
  10. data/app/jobs/bulkrax/{import_work_collection_job.rb → import_collection_job.rb} +2 -2
  11. data/app/jobs/bulkrax/importer_job.rb +16 -1
  12. data/app/matchers/bulkrax/application_matcher.rb +9 -6
  13. data/app/models/bulkrax/csv_collection_entry.rb +8 -6
  14. data/app/models/bulkrax/csv_entry.rb +139 -45
  15. data/app/models/bulkrax/entry.rb +19 -8
  16. data/app/models/bulkrax/exporter.rb +12 -5
  17. data/app/models/bulkrax/importer.rb +22 -5
  18. data/app/models/bulkrax/oai_entry.rb +5 -1
  19. data/app/models/bulkrax/rdf_entry.rb +16 -7
  20. data/app/models/bulkrax/xml_entry.rb +4 -0
  21. data/app/models/concerns/bulkrax/export_behavior.rb +2 -2
  22. data/app/models/concerns/bulkrax/file_factory.rb +2 -1
  23. data/app/models/concerns/bulkrax/has_matchers.rb +59 -16
  24. data/app/models/concerns/bulkrax/import_behavior.rb +35 -5
  25. data/app/models/concerns/bulkrax/importer_exporter_behavior.rb +19 -0
  26. data/app/models/concerns/bulkrax/status_info.rb +4 -4
  27. data/app/parsers/bulkrax/application_parser.rb +59 -84
  28. data/app/parsers/bulkrax/bagit_parser.rb +12 -3
  29. data/app/parsers/bulkrax/csv_parser.rb +117 -62
  30. data/app/parsers/bulkrax/oai_dc_parser.rb +5 -2
  31. data/app/parsers/bulkrax/xml_parser.rb +5 -0
  32. data/app/views/bulkrax/exporters/_form.html.erb +1 -1
  33. data/app/views/bulkrax/exporters/show.html.erb +13 -1
  34. data/app/views/bulkrax/importers/_edit_form_buttons.html.erb +45 -14
  35. data/app/views/bulkrax/importers/edit.html.erb +2 -0
  36. data/app/views/bulkrax/importers/index.html.erb +15 -17
  37. data/app/views/bulkrax/importers/show.html.erb +6 -2
  38. data/config/locales/bulkrax.en.yml +1 -0
  39. data/db/migrate/20190731114016_change_importer_and_exporter_to_polymorphic.rb +5 -1
  40. data/db/migrate/20211004170708_change_bulkrax_statuses_error_message_column_type_to_text.rb +5 -0
  41. data/db/migrate/20211203195233_rename_children_counters_to_relationships.rb +6 -0
  42. data/lib/bulkrax/engine.rb +1 -1
  43. data/lib/bulkrax/version.rb +1 -1
  44. data/lib/bulkrax.rb +9 -17
  45. data/lib/generators/bulkrax/templates/bin/importer +17 -11
  46. data/lib/generators/bulkrax/templates/config/bulkrax_api.yml +3 -1
  47. data/lib/generators/bulkrax/templates/config/initializers/bulkrax.rb +7 -12
  48. metadata +13 -7
  49. data/app/jobs/bulkrax/child_relationships_job.rb +0 -128
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9dd5afb5469da9e5d574a3a91a86a3257d8dbb04d2f8682e80a7399fe16f0e75
4
- data.tar.gz: 69033280eb68fb73f6a71ace05cb105b6545989ebfa128ae424bd30d59e59392
3
+ metadata.gz: 12d2cd4f6112d69872499fefcc5338743445c5ee922389451724591372907935
4
+ data.tar.gz: 56ca7cb3fea0d47218695ce42870f4c892f3eb622b71ef062d57ac103adcdd9c
5
5
  SHA512:
6
- metadata.gz: fbfb807b6aff8eba148835953075366a889fbf020a135f63cf30d16a505a7312270192cfba136f0bc5f631d5c31c171f8a1fa33f7489ef173507b932c2d8589c
7
- data.tar.gz: a00f7efef15d1a1ec402ed05f359f33c1d765a6e31cb62cdef42bc3867f43fc31f4ba277495b8ddcea456b252d45a8a3409389e99fa70def26f51b1e45ac78ab
6
+ metadata.gz: e79423222d13be800ecb7758dd0d75a87b78082b7c7d0d90a059d6bcf80a0ccd4897b0ae6301da62ebc7dd62efa6962c1a3bf64a49d0a69456d1f2e7b49ff50d
7
+ data.tar.gz: 0c65a81da9f1a6825f31fb4c4047bceb30557663a88ef3b593ede7b1fa125f950d4d675068c0f74268e18ce21caa8ceddf493febca4436ed2ff1d68486ebe8da
data/README.md CHANGED
@@ -16,7 +16,7 @@ gem 'bulkrax', git: 'https://github.com/samvera-labs/bulkrax.git'
16
16
 
17
17
  And then execute:
18
18
  ```bash
19
- $ bundle update
19
+ $ bundle install
20
20
  $ rails generate bulkrax:install
21
21
  ```
22
22
 
@@ -51,9 +51,12 @@ module Bulkrax
51
51
  if @exporter.save
52
52
  if params[:commit] == 'Create and Export'
53
53
  # Use perform now for export
54
- Bulkrax::ExporterJob.perform_now(@exporter.id)
54
+ Bulkrax::ExporterJob.perform_later(@exporter.id)
55
+ message = 'Exporter was successfully created. A download link will appear once it completes.'
56
+ else
57
+ message = 'Exporter was successfully created.'
55
58
  end
56
- redirect_to exporters_path, notice: 'Exporter was successfully created.'
59
+ redirect_to exporters_path, notice: message
57
60
  else
58
61
  render :new
59
62
  end
@@ -63,8 +66,13 @@ module Bulkrax
63
66
  def update
64
67
  field_mapping_params
65
68
  if @exporter.update(exporter_params)
66
- Bulkrax::ExporterJob.perform_now(@exporter.id) if params[:commit] == 'Update and Re-Export All Items'
67
- redirect_to exporters_path, notice: 'Exporter was successfully updated.'
69
+ if params[:commit] == 'Update and Re-Export All Items'
70
+ Bulkrax::ExporterJob.perform_later(@exporter.id)
71
+ message = 'Exporter was successfully updated. A download link will appear once it completes.'
72
+ else
73
+ 'Exporter was successfully updated.'
74
+ end
75
+ redirect_to exporters_path, notice: message
68
76
  else
69
77
  render :edit
70
78
  end
@@ -113,23 +113,9 @@ module Bulkrax
113
113
  if @importer.update(importer_params)
114
114
  files_for_import(file, cloud_files) unless file.nil? && cloud_files.nil?
115
115
  # do not perform the import
116
- if params[:commit] == 'Update Importer'
117
- # do nothing
118
- # OAI-only - selective re-harvest
119
- elsif params[:commit] == 'Update and Harvest Updated Items'
120
- Bulkrax::ImporterJob.perform_later(@importer.id, true)
121
- elsif params[:commit] == 'Update All (update metadata and update files)'
122
- @importer.parser_fields['update_files'] = true
123
- @importer.save
124
- Bulkrax::ImporterJob.perform_later(@importer.id)
125
- # Perform a full metadata and files re-import; do the same for an OAI re-harvest of all items
126
- elsif params[:commit] == ('Update and Replace (update metadata and replace files)' || 'Update and Re-Harvest All Items')
127
- @importer.parser_fields['replace_files'] = true
128
- @importer.save
129
- Bulkrax::ImporterJob.perform_later(@importer.id)
130
- # In all other cases, perform a metadata-only re-import
131
- else
132
- Bulkrax::ImporterJob.perform_later(@importer.id)
116
+ unless params[:commit] == 'Update Importer'
117
+ set_files_parser_fields
118
+ Bulkrax::ImporterJob.send(@importer.parser.perform_method, @importer.id, update_harvest)
133
119
  end
134
120
  if api_request?
135
121
  json_response('updated', :ok, 'Importer was successfully updated.')
@@ -310,6 +296,25 @@ module Bulkrax
310
296
  redirect_to path, notice: message
311
297
  end
312
298
  end
299
+
300
+ # update methods (for commit deciphering)
301
+ def update_harvest
302
+ # OAI-only - selective re-harvest
303
+ params[:commit] == 'Update and Harvest Updated Items'
304
+ end
305
+
306
+ def set_files_parser_fields
307
+ if params[:commit] == 'Update Metadata and Files'
308
+ @importer.parser_fields['update_files'] = true
309
+ elsif params[:commit] == ('Update and Replace Files' || 'Update and Re-Harvest All Items')
310
+ @importer.parser_fields['replace_files'] = true
311
+ elsif params[:commit] == 'Update and Harvest Updated Items'
312
+ return
313
+ else
314
+ @importer.parser_fields['metadata_only'] = true
315
+ end
316
+ @importer.save
317
+ end
313
318
  end
314
319
  # rubocop:enable Metrics/ClassLength
315
320
  end
@@ -5,20 +5,30 @@ module Bulkrax
5
5
  extend ActiveModel::Callbacks
6
6
  include Bulkrax::FileFactory
7
7
  define_model_callbacks :save, :create
8
- attr_reader :attributes, :object, :source_identifier_value, :klass, :replace_files, :update_files, :work_identifier
8
+ attr_reader :attributes, :object, :source_identifier_value, :klass, :replace_files, :update_files, :work_identifier, :collection_field_mapping
9
9
 
10
10
  # rubocop:disable Metrics/ParameterLists
11
- def initialize(attributes:, source_identifier_value:, work_identifier:, replace_files: false, user: nil, klass: nil, update_files: false)
11
+ def initialize(attributes:, source_identifier_value:, work_identifier:, collection_field_mapping:, replace_files: false, user: nil, klass: nil, update_files: false)
12
+ ActiveSupport::Deprecation.warn(
13
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
14
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
15
+ )
12
16
  @attributes = ActiveSupport::HashWithIndifferentAccess.new(attributes)
13
17
  @replace_files = replace_files
14
18
  @update_files = update_files
15
19
  @user = user || User.batch_user
16
20
  @work_identifier = work_identifier
21
+ @collection_field_mapping = collection_field_mapping
17
22
  @source_identifier_value = source_identifier_value
18
23
  @klass = klass || Bulkrax.default_work_type.constantize
19
24
  end
20
25
  # rubocop:enable Metrics/ParameterLists
21
26
 
27
+ # update files is set, replace files is set or this is a create
28
+ def with_files
29
+ update_files || replace_files || !object
30
+ end
31
+
22
32
  def run
23
33
  arg_hash = { id: attributes[:id], name: 'UPDATE', klass: klass }
24
34
  @object = find
@@ -36,6 +46,7 @@ module Bulkrax
36
46
  self.run
37
47
  # Create the error exception if the object is not validly saved for some reason
38
48
  raise ActiveFedora::RecordInvalid, object if !object.persisted? || object.changed?
49
+ object
39
50
  end
40
51
 
41
52
  def update
@@ -49,7 +60,7 @@ module Bulkrax
49
60
  end
50
61
 
51
62
  def find
52
- return find_by_id if attributes[:id]
63
+ return find_by_id if attributes[:id].present?
53
64
  return search_by_identifier if attributes[work_identifier].present?
54
65
  end
55
66
 
@@ -116,52 +127,33 @@ module Bulkrax
116
127
  end
117
128
 
118
129
  def create_collection(attrs)
130
+ ActiveSupport::Deprecation.warn(
131
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
132
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
133
+ )
119
134
  attrs = collection_type(attrs)
120
- object.members = members
121
- object.member_of_collections = member_of_collections
135
+ persist_collection_memberships(parent: object, child: find_collection(attributes[:child_collection_id])) if attributes[:child_collection_id].present?
136
+ persist_collection_memberships(parent: find_collection(attributes[collection_field_mapping]), child: object) if attributes[collection_field_mapping].present?
122
137
  object.attributes = attrs
123
138
  object.apply_depositor_metadata(@user)
124
139
  object.save!
125
140
  end
126
141
 
127
142
  def update_collection(attrs)
128
- object.members = members
129
- object.member_of_collections = member_of_collections
143
+ ActiveSupport::Deprecation.warn(
144
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
145
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
146
+ )
147
+ persist_collection_memberships(parent: object, child: find_collection(attributes[:child_collection_id])) if attributes[:child_collection_id].present?
148
+ persist_collection_memberships(parent: find_collection(attributes[collection_field_mapping]), child: object) if attributes[collection_field_mapping].present?
130
149
  object.attributes = attrs
131
150
  object.save!
132
151
  end
133
152
 
134
- # Collections don't respond to member_of_collections_attributes or member_of_collection_ids=
135
- # or member_ids=
136
- # Add them directly with members / member_of_collections
137
- # collection should be in the form { id: collection_id }
138
- # and collections [{ id: collection_id }]
139
- # member_ids comes from
140
- # @todo - consider performance implications although we wouldn't expect a Collection to be a member of many Collections
141
- def members
142
- ms = object.members.to_a
143
- [:children].each do |atat|
144
- next if attributes[atat].blank?
145
- ms.concat(
146
- Array.wrap(
147
- find_collection(attributes[atat])
148
- )
149
- )
150
- end
151
- ms.flatten.compact.uniq
152
- end
153
-
154
- def member_of_collections
155
- ms = object.member_of_collection_ids.to_a.map { |id| find_collection(id) }
156
- [:collection, :collections].each do |atat|
157
- next if attributes[atat].blank?
158
- ms.concat(
159
- Array.wrap(
160
- find_collection(attributes[atat])
161
- )
162
- )
163
- end
164
- ms.flatten.compact.uniq
153
+ # Add child to parent's #member_collections
154
+ # Add parent to child's #member_of_collections
155
+ def persist_collection_memberships(parent:, child:)
156
+ ::Hyrax::Collections::NestedCollectionPersistenceService.persist_nested_collection_for(parent: parent, child: child)
165
157
  end
166
158
 
167
159
  def find_collection(id)
@@ -187,44 +179,35 @@ module Bulkrax
187
179
  # which is used by Hyrax::Actors::AddAsMemberOfCollectionsActor
188
180
  def create_attributes
189
181
  return transform_attributes if klass == Collection
190
- if attributes[:collection].present?
191
- transform_attributes.except(:collection).merge(member_of_collections_attributes: { 0 => { id: collection.id } })
192
- elsif attributes[:collections].present?
193
- collection_ids = attributes[:collections].each.with_index.each_with_object({}) do |(element, index), ids|
194
- ids[index] = { id: element }
195
- end
196
- transform_attributes.except(:collections).merge(member_of_collections_attributes: collection_ids)
197
- else
198
- transform_attributes
199
- end
182
+ ActiveSupport::Deprecation.warn(
183
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
184
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
185
+ )
186
+ transform_attributes.except(:collections, :collection, collection_field_mapping)
200
187
  end
201
188
 
202
189
  # Strip out the :collection key, and add the member_of_collection_ids,
203
190
  # which is used by Hyrax::Actors::AddAsMemberOfCollectionsActor
204
191
  def attribute_update
205
192
  return transform_attributes.except(:id) if klass == Collection
206
- if attributes[:collection].present?
207
- transform_attributes.except(:id).except(:collection).merge(member_of_collections_attributes: { 0 => { id: collection.id } })
208
- elsif attributes[:collections].present?
209
- collection_ids = attributes[:collections].each.with_index.each_with_object({}) do |(element, index), ids|
210
- ids[index] = element
211
- end
212
- transform_attributes.except(:id).except(:collections).merge(member_of_collections_attributes: collection_ids)
213
- else
214
- transform_attributes.except(:id)
215
- end
193
+ ActiveSupport::Deprecation.warn(
194
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
195
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
196
+ )
197
+ transform_attributes.except(:id, :collections, :collection, collection_field_mapping)
216
198
  end
217
199
 
218
200
  # Override if we need to map the attributes from the parser in
219
201
  # a way that is compatible with how the factory needs them.
220
202
  def transform_attributes
221
- attributes.slice(*permitted_attributes)
222
- .merge(file_attributes(update_files))
203
+ @transform_attributes = attributes.slice(*permitted_attributes)
204
+ @transform_attributes.merge!(file_attributes(update_files)) if with_files
205
+ @transform_attributes
223
206
  end
224
207
 
225
208
  # Regardless of what the Parser gives us, these are the properties we are prepared to accept.
226
209
  def permitted_attributes
227
- klass.properties.keys.map(&:to_sym) + %i[id edit_users edit_groups read_groups visibility work_members_attributes admin_set_id]
210
+ klass.properties.keys.map(&:to_sym) + %i[id edit_users edit_groups read_groups visibility work_members_attributes admin_set_id member_of_collections_attributes]
228
211
  end
229
212
  end
230
213
  end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bulkrax
4
+ # Responsible for creating parent-child relationships between Works and Collections.
5
+ #
6
+ # Handles three kinds of relationships:
7
+ # - Work to Collection
8
+ # - Collection to Collection
9
+ # - Work to Work
10
+ #
11
+ # These can be established from either side of the relationship (i.e. from parent to child or from child to parent).
12
+ # This job only creates one relationship at a time. If a record needs multiple parents or children or both, individual
13
+ # jobs should be run for each of those relationships.
14
+ #
15
+ # NOTE: In the context of this job, "record" is used to generically refer
16
+ # to either an instance of a Work or an instance of a Collection.
17
+ # NOTE: In the context of this job, "identifier" is used to generically refer
18
+ # to either a record's ID or an Bulkrax::Entry's source_identifier.
19
+ class CreateRelationshipsJob < ApplicationJob
20
+ queue_as :import
21
+
22
+ attr_accessor :base_entry, :child_record, :parent_record, :importer_run
23
+
24
+ # @param entry_identifier [String] source_identifier of the base Bulkrax::Entry the job was triggered from (see #build_for_importer)
25
+ # @param parent_identifier [String] Work/Collection ID or Bulkrax::Entry source_identifier
26
+ # @param child_identifier [String] Work/Collection ID or Bulkrax::Entry source_identifier
27
+ # @param importer_run [Bulkrax::ImporterRun] current importer run (needed to properly update counters)
28
+ #
29
+ # The entry_identifier is used to lookup the @base_entry for the job (a.k.a. the entry the job was called from).
30
+ # The @base_entry defines the context of the relationship (e.g. "this entry (@base_entry) should have a parent").
31
+ # Whether the @base_entry is the parent or the child in the relationship is determined by the presence of a
32
+ # parent_identifier or child_identifier param. For example, if a parent_identifier is passed, we know @base_entry
33
+ # is the child in the relationship, and vice versa if a child_identifier is passed.
34
+ def perform(entry_identifier:, parent_identifier: nil, child_identifier: nil, importer_run:)
35
+ @base_entry = Entry.find_by(identifier: entry_identifier)
36
+ @importer_run = importer_run
37
+ if parent_identifier.present?
38
+ @child_record = find_record(entry_identifier)
39
+ @parent_record = find_record(parent_identifier)
40
+ elsif child_identifier.present?
41
+ @parent_record = find_record(entry_identifier)
42
+ @child_record = find_record(child_identifier)
43
+ else
44
+ raise ::StandardError, %("#{entry_identifier}" needs either a child or a parent to create a relationship)
45
+ end
46
+
47
+ if @child_record.blank? || @parent_record.blank?
48
+ reschedule(
49
+ entry_identifier: entry_identifier,
50
+ parent_identifier: parent_identifier,
51
+ child_identifier: child_identifier,
52
+ importer_run: importer_run
53
+ )
54
+ return false # stop current job from continuing to run after rescheduling
55
+ end
56
+
57
+ create_relationship
58
+ rescue ::StandardError => e
59
+ base_entry.status_info(e)
60
+ importer_run.increment!(:failed_relationships) # rubocop:disable Rails/SkipsModelValidations
61
+ end
62
+
63
+ private
64
+
65
+ def create_relationship
66
+ if parent_record.is_a?(::Collection) && child_record.is_a?(::Collection)
67
+ collection_parent_collection_child
68
+ elsif parent_record.is_a?(::Collection) && curation_concern?(child_record)
69
+ collection_parent_work_child
70
+ elsif curation_concern?(parent_record) && child_record.is_a?(::Collection)
71
+ raise ::StandardError, 'a Collection may not be assigned as a child of a Work'
72
+ else
73
+ work_parent_work_child
74
+ end
75
+ end
76
+
77
+ # This method allows us to create relationships with preexisting records (by their ID) OR
78
+ # with records that are concurrently being imported (by their Bulkrax::Entry source_identifier).
79
+ #
80
+ # @param identifier [String] Work/Collection ID or Bulkrax::Entry source_identifier
81
+ # @return [Work, Collection, nil] Work or Collection if found, otherwise nil
82
+ def find_record(identifier)
83
+ record = Entry.find_by(identifier: identifier)
84
+ record ||= ::Collection.where(id: identifier).first
85
+ if record.blank?
86
+ available_work_types.each do |work_type|
87
+ record ||= work_type.where(id: identifier).first
88
+ end
89
+ end
90
+ record = record.factory.find if record.is_a?(Entry)
91
+
92
+ record
93
+ end
94
+
95
+ # Check if the record is a Work
96
+ def curation_concern?(record)
97
+ available_work_types.include?(record.class)
98
+ end
99
+
100
+ # @return [Array<Class>] list of work type classes
101
+ def available_work_types
102
+ # If running in a Hyku app, do not reference disabled work types
103
+ @available_work_types ||= if defined?(::Hyku)
104
+ ::Site.instance.available_works.map(&:constantize)
105
+ else
106
+ ::Hyrax.config.curation_concerns
107
+ end
108
+ end
109
+
110
+ def user
111
+ @user ||= importer_run.importer.user
112
+ end
113
+
114
+ # Work-Collection membership is added to the child as member_of_collection_ids
115
+ # This is adding the reverse relationship, from the child to the parent
116
+ def collection_parent_work_child
117
+ ActiveSupport::Deprecation.warn(
118
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
119
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
120
+ )
121
+ attrs = { id: child_record.id, member_of_collections_attributes: { 0 => { id: parent_record.id } } }
122
+ ObjectFactory.new(
123
+ attributes: attrs,
124
+ source_identifier_value: nil, # sending the :id in the attrs means the factory doesn't need a :source_identifier_value
125
+ work_identifier: base_entry.parser.work_identifier,
126
+ collection_field_mapping: base_entry.parser.collection_field_mapping,
127
+ replace_files: false,
128
+ user: user,
129
+ klass: child_record.class
130
+ ).run
131
+ # TODO: add counters for :processed_parents and :failed_parents
132
+ importer_run.increment!(:processed_relationships) # rubocop:disable Rails/SkipsModelValidations
133
+ end
134
+
135
+ # Collection-Collection membership is added to the as member_ids
136
+ def collection_parent_collection_child
137
+ ActiveSupport::Deprecation.warn(
138
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
139
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
140
+ )
141
+ attrs = { id: parent_record.id, child_collection_id: child_record.id }
142
+ ObjectFactory.new(
143
+ attributes: attrs,
144
+ source_identifier_value: nil, # sending the :id in the attrs means the factory doesn't need a :source_identifier_value
145
+ work_identifier: base_entry.parser.work_identifier,
146
+ collection_field_mapping: base_entry.parser.collection_field_mapping,
147
+ replace_files: false,
148
+ user: user,
149
+ klass: parent_record.class
150
+ ).run
151
+ # TODO: add counters for :processed_parents and :failed_parents
152
+ importer_run.increment!(:processed_relationships) # rubocop:disable Rails/SkipsModelValidations
153
+ end
154
+
155
+ # Work-Work membership is added to the parent as member_ids
156
+ def work_parent_work_child
157
+ ActiveSupport::Deprecation.warn(
158
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
159
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
160
+ )
161
+ attrs = {
162
+ id: parent_record.id,
163
+ work_members_attributes: { 0 => { id: child_record.id } }
164
+ }
165
+ ObjectFactory.new(
166
+ attributes: attrs,
167
+ source_identifier_value: nil, # sending the :id in the attrs means the factory doesn't need a :source_identifier_value
168
+ work_identifier: base_entry.parser.work_identifier,
169
+ collection_field_mapping: base_entry.parser.collection_field_mapping,
170
+ replace_files: false,
171
+ user: user,
172
+ klass: parent_record.class
173
+ ).run
174
+ # TODO: add counters for :processed_parents and :failed_parents
175
+ importer_run.increment!(:processed_relationships) # rubocop:disable Rails/SkipsModelValidations
176
+ end
177
+
178
+ def reschedule(entry_identifier:, parent_identifier:, child_identifier:, importer_run:)
179
+ CreateRelationshipsJob.set(wait: 10.minutes).perform_later(
180
+ entry_identifier: entry_identifier,
181
+ parent_identifier: parent_identifier,
182
+ child_identifier: child_identifier,
183
+ importer_run: importer_run
184
+ )
185
+ end
186
+ end
187
+ end
@@ -8,8 +8,12 @@ module Bulkrax
8
8
  def perform(entry, importer_run)
9
9
  work = entry.factory.find
10
10
  work&.delete
11
- importer_run.increment!(:deleted_records)
12
- importer_run.decrement!(:enqueued_records)
11
+ ImporterRun.find(importer_run.id).increment!(:deleted_records)
12
+ ImporterRun.find(importer_run.id).decrement!(:enqueued_records)
13
+ entry.save!
14
+ entry.importer.current_run = ImporterRun.find(importer_run.id)
15
+ entry.importer.record_status
16
+ entry.status_info("Deleted", ImporterRun.find(importer_run.id))
13
17
  end
14
18
  # rubocop:enable Rails/SkipsModelValidations
15
19
  end
@@ -26,12 +26,14 @@ module Bulkrax
26
26
  # rubocop:enable Rails/SkipsModelValidations
27
27
  end
28
28
  exporter_run = ExporterRun.find(args[1])
29
- return if exporter_run.enqueued_records.positive?
29
+ return entry if exporter_run.enqueued_records.positive?
30
30
  if exporter_run.failed_records.positive?
31
31
  exporter_run.exporter.status_info('Complete (with failures)')
32
32
  else
33
33
  exporter_run.exporter.status_info('Complete')
34
34
  end
35
+
36
+ return entry
35
37
  end
36
38
  end
37
39
  end
@@ -8,6 +8,7 @@ module Bulkrax
8
8
  exporter = Exporter.find(exporter_id)
9
9
  exporter.export
10
10
  exporter.write
11
+ exporter.save
11
12
  true
12
13
  end
13
14
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bulkrax
4
- class ImportWorkCollectionJob < ApplicationJob
4
+ class ImportCollectionJob < ApplicationJob
5
5
  queue_as :import
6
6
 
7
7
  # rubocop:disable Rails/SkipsModelValidations
@@ -28,7 +28,7 @@ module Bulkrax
28
28
  collection = entry.factory.find
29
29
  permission_template = Hyrax::PermissionTemplate.find_or_create_by!(source_id: collection.id)
30
30
 
31
- Hyrax::PermissionTemplateAccess.create!(
31
+ Hyrax::PermissionTemplateAccess.find_or_create_by!(
32
32
  permission_template_id: permission_template.id,
33
33
  agent_id: user.user_key,
34
34
  agent_type: 'user',
@@ -6,17 +6,32 @@ module Bulkrax
6
6
 
7
7
  def perform(importer_id, only_updates_since_last_import = false)
8
8
  importer = Importer.find(importer_id)
9
+
9
10
  importer.current_run
11
+ unzip_imported_file(importer.parser)
10
12
  import(importer, only_updates_since_last_import)
13
+ update_current_run_counters(importer)
11
14
  schedule(importer) if importer.schedulable?
12
15
  end
13
16
 
14
17
  def import(importer, only_updates_since_last_import)
15
18
  importer.only_updates = only_updates_since_last_import || false
16
19
  return unless importer.valid_import?
20
+
17
21
  importer.import_collections
18
22
  importer.import_works
19
- importer.create_parent_child_relationships unless importer.validate_only
23
+ end
24
+
25
+ def unzip_imported_file(parser)
26
+ return unless parser.file? && parser.zip?
27
+
28
+ parser.unzip(parser.parser_fields['import_file_path'])
29
+ end
30
+
31
+ def update_current_run_counters(importer)
32
+ importer.current_run.total_work_entries = importer.limit || importer.parser.works_total
33
+ importer.current_run.total_collection_entries = importer.parser.collections_total
34
+ importer.current_run.save!
20
35
  end
21
36
 
22
37
  def schedule(importer)
@@ -20,9 +20,9 @@ module Bulkrax
20
20
  return unless content.send(self.if[0], Regexp.new(self.if[1]))
21
21
  end
22
22
 
23
- @result = content.to_s.gsub(/\s/, ' ') # remove any line feeds and tabs
24
- @result.strip!
25
- process_split
23
+ # @result will evaluate to an empty string for nil content values
24
+ @result = content.to_s.gsub(/\s/, ' ').strip # remove any line feeds and tabs
25
+ process_split if @result.present?
26
26
  @result = @result[0] if @result.is_a?(Array) && @result.size == 1
27
27
  process_parse
28
28
  return @result
@@ -54,7 +54,10 @@ module Bulkrax
54
54
  end
55
55
 
56
56
  def parse_remote_files(src)
57
- { url: src.strip } if src.present?
57
+ return if src.blank?
58
+ src.strip!
59
+ name = Bulkrax::Importer.safe_uri_filename(src)
60
+ { url: src, file_name: name }
58
61
  end
59
62
 
60
63
  def parse_language(src)
@@ -63,14 +66,14 @@ module Bulkrax
63
66
  end
64
67
 
65
68
  def parse_subject(src)
66
- string = src.to_s.strip.downcase
69
+ string = src.strip.downcase
67
70
  return if string.blank?
68
71
 
69
72
  string.slice(0, 1).capitalize + string.slice(1..-1)
70
73
  end
71
74
 
72
75
  def parse_types(src)
73
- src.to_s.strip.titleize
76
+ src.strip.titleize
74
77
  end
75
78
 
76
79
  # Allow for mapping a model field to the work type or collection
@@ -6,14 +6,16 @@ module Bulkrax
6
6
  Collection
7
7
  end
8
8
 
9
- def build_metadata
10
- self.parsed_metadata = self.raw_metadata
11
- add_local
12
- return self.parsed_metadata
9
+ # Use identifier set by CsvParser#unique_collection_identifier, which falls back
10
+ # on the Collection's first title if record[source_identifier] is not present
11
+ def add_identifier
12
+ self.parsed_metadata[work_identifier] = self.identifier
13
13
  end
14
14
 
15
- def collections_created?
16
- true
15
+ def add_collection_type_gid
16
+ return if self.parsed_metadata['collection_type_gid'].present?
17
+
18
+ self.parsed_metadata['collection_type_gid'] = ::Hyrax::CollectionType.find_or_create_default_collection_type.gid
17
19
  end
18
20
  end
19
21
  end