bulkrax 4.2.1 → 4.4.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 +4 -4
- data/app/assets/javascripts/bulkrax/exporters.js +12 -0
- data/app/assets/javascripts/bulkrax/importers.js.erb +27 -1
- data/app/controllers/bulkrax/exporters_controller.rb +3 -1
- data/app/controllers/bulkrax/importers_controller.rb +1 -1
- data/app/factories/bulkrax/object_factory.rb +35 -8
- data/app/jobs/bulkrax/create_relationships_job.rb +1 -1
- data/app/jobs/bulkrax/import_work_job.rb +12 -10
- data/app/matchers/bulkrax/application_matcher.rb +1 -1
- data/app/models/bulkrax/csv_entry.rb +14 -10
- data/app/models/bulkrax/importer.rb +20 -15
- data/app/models/bulkrax/oai_entry.rb +1 -2
- data/app/models/concerns/bulkrax/file_set_entry_behavior.rb +8 -1
- data/app/models/concerns/bulkrax/import_behavior.rb +10 -9
- data/app/parsers/bulkrax/application_parser.rb +87 -14
- data/app/parsers/bulkrax/bagit_parser.rb +2 -1
- data/app/parsers/bulkrax/csv_parser.rb +11 -10
- data/app/parsers/bulkrax/oai_dc_parser.rb +2 -2
- data/app/services/bulkrax/remove_relationships_for_importer.rb +107 -0
- data/app/views/bulkrax/exporters/_form.html.erb +3 -3
- data/app/views/bulkrax/exporters/show.html.erb +17 -41
- data/app/views/bulkrax/importers/edit.html.erb +1 -1
- data/app/views/bulkrax/importers/new.html.erb +1 -1
- data/app/views/bulkrax/importers/show.html.erb +3 -114
- data/app/views/bulkrax/shared/_collection_entries_tab.html.erb +39 -0
- data/app/views/bulkrax/shared/_file_set_entries_tab.html.erb +39 -0
- data/app/views/bulkrax/shared/_work_entries_tab.html.erb +39 -0
- data/app/views/hyrax/dashboard/sidebar/_bulkrax_sidebar_additions.html.erb +7 -5
- data/app/views/hyrax/dashboard/sidebar/_repository_content.html.erb +23 -15
- data/db/migrate/20211203195233_rename_children_counters_to_relationships.rb +1 -1
- data/db/migrate/20211220195027_add_file_set_counters_to_importer_runs.rb +1 -1
- data/db/migrate/20220118001339_add_import_attempts_to_entries.rb +1 -1
- data/db/migrate/20220119213325_add_work_counters_to_importer_runs.rb +1 -1
- data/db/migrate/20220301001839_create_bulkrax_pending_relationships.rb +1 -1
- data/db/migrate/20220303212810_add_order_to_bulkrax_pending_relationships.rb +1 -1
- data/db/migrate/20220412233954_add_include_thumbnails_to_bulkrax_exporters.rb +1 -1
- data/db/migrate/20220413180915_add_generated_metadata_to_bulkrax_exporters.rb +1 -1
- data/db/migrate/20220609001128_rename_bulkrax_importer_run_to_importer_run.rb +1 -1
- data/lib/bulkrax/version.rb +1 -1
- data/lib/bulkrax.rb +38 -11
- data/lib/generators/bulkrax/templates/config/initializers/bulkrax.rb +10 -0
- data/lib/tasks/bulkrax_tasks.rake +10 -11
- data/lib/tasks/reset.rake +65 -0
- metadata +7 -2
@@ -2,6 +2,9 @@
|
|
2
2
|
require 'zip'
|
3
3
|
|
4
4
|
module Bulkrax
|
5
|
+
# An abstract class that establishes the API for Bulkrax's import and export parsing.
|
6
|
+
#
|
7
|
+
# @abstract Subclass the Bulkrax::ApplicationParser to create a parser that handles a specific format (e.g. CSV, Bagit, XML, etc).
|
5
8
|
class ApplicationParser # rubocop:disable Metrics/ClassLength
|
6
9
|
attr_accessor :importerexporter, :headers
|
7
10
|
alias importer importerexporter
|
@@ -12,14 +15,21 @@ module Bulkrax
|
|
12
15
|
:exporter_export_path, :exporter_export_zip_path, :importer_unzip_path, :validate_only,
|
13
16
|
to: :importerexporter
|
14
17
|
|
18
|
+
# @todo Convert to `class_attribute :parser_fiels, default: {}`
|
15
19
|
def self.parser_fields
|
16
20
|
{}
|
17
21
|
end
|
18
22
|
|
23
|
+
# @return [TrueClass,FalseClass] this parser does or does not support exports.
|
24
|
+
#
|
25
|
+
# @todo Convert to `class_attribute :export_supported, default: false, instance_predicate: true` and `self << class; alias export_supported? export_supported; end`
|
19
26
|
def self.export_supported?
|
20
27
|
false
|
21
28
|
end
|
22
29
|
|
30
|
+
# @return [TrueClass,FalseClass] this parser does or does not support imports.
|
31
|
+
#
|
32
|
+
# @todo Convert to `class_attribute :import_supported, default: false, instance_predicate: true` and `self << class; alias import_supported? import_supported; end`
|
23
33
|
def self.import_supported?
|
24
34
|
true
|
25
35
|
end
|
@@ -29,49 +39,70 @@ module Bulkrax
|
|
29
39
|
@headers = []
|
30
40
|
end
|
31
41
|
|
32
|
-
# @api
|
42
|
+
# @api public
|
43
|
+
# @abstract Subclass and override {#entry_class} to implement behavior for the parser.
|
33
44
|
def entry_class
|
34
|
-
raise
|
45
|
+
raise NotImplementedError, 'must be defined'
|
35
46
|
end
|
36
47
|
|
37
|
-
# @api
|
48
|
+
# @api public
|
49
|
+
# @abstract Subclass and override {#collection_entry_class} to implement behavior for the parser.
|
38
50
|
def collection_entry_class
|
39
|
-
raise
|
51
|
+
raise NotImplementedError, 'must be defined'
|
40
52
|
end
|
41
53
|
|
42
|
-
# @api
|
54
|
+
# @api public
|
55
|
+
# @abstract Subclass and override {#records} to implement behavior for the parser.
|
43
56
|
def records(_opts = {})
|
44
|
-
raise
|
57
|
+
raise NotImplementedError, 'must be defined'
|
45
58
|
end
|
46
59
|
|
60
|
+
# @return [Symbol] the name of the identifying property in the source system from which we're
|
61
|
+
# importing (e.g. is *not* this application that mounts *this* Bulkrax engine).
|
62
|
+
#
|
63
|
+
# @see #work_identifier
|
64
|
+
# @see https://github.com/samvera-labs/bulkrax/wiki/CSV-Importer#source-identifier Bulkrax Wiki regarding source identifier
|
47
65
|
def source_identifier
|
48
66
|
@source_identifier ||= get_field_mapping_hash_for('source_identifier')&.values&.first&.[]('from')&.first&.to_sym || :source_identifier
|
49
67
|
end
|
50
68
|
|
69
|
+
# @return [Symbol] the name of the identifying property for the system which we're importing
|
70
|
+
# into (e.g. the application that mounts *this* Bulkrax engine)
|
71
|
+
# @see #source_identifier
|
51
72
|
def work_identifier
|
52
73
|
@work_identifier ||= get_field_mapping_hash_for('source_identifier')&.keys&.first&.to_sym || :source
|
53
74
|
end
|
54
75
|
|
76
|
+
# @return [String]
|
55
77
|
def generated_metadata_mapping
|
56
78
|
@generated_metadata_mapping ||= 'generated'
|
57
79
|
end
|
58
80
|
|
81
|
+
# @return [String, NilClass]
|
82
|
+
# @see #related_parents_raw_mapping
|
59
83
|
def related_parents_raw_mapping
|
60
84
|
@related_parents_raw_mapping ||= get_field_mapping_hash_for('related_parents_field_mapping')&.values&.first&.[]('from')&.first
|
61
85
|
end
|
62
86
|
|
87
|
+
# @return [String]
|
88
|
+
# @see #related_parents_field_mapping
|
63
89
|
def related_parents_parsed_mapping
|
64
90
|
@related_parents_parsed_mapping ||= (get_field_mapping_hash_for('related_parents_field_mapping')&.keys&.first || 'parents')
|
65
91
|
end
|
66
92
|
|
93
|
+
# @return [String, NilClass]
|
94
|
+
# @see #related_children_parsed_mapping
|
67
95
|
def related_children_raw_mapping
|
68
96
|
@related_children_raw_mapping ||= get_field_mapping_hash_for('related_children_field_mapping')&.values&.first&.[]('from')&.first
|
69
97
|
end
|
70
98
|
|
99
|
+
# @return [String]
|
100
|
+
# @see #related_children_raw_mapping
|
71
101
|
def related_children_parsed_mapping
|
72
102
|
@related_children_parsed_mapping ||= (get_field_mapping_hash_for('related_children_field_mapping')&.keys&.first || 'children')
|
73
103
|
end
|
74
104
|
|
105
|
+
# @api private
|
75
106
|
def get_field_mapping_hash_for(key)
|
76
107
|
return instance_variable_get("@#{key}_hash") if instance_variable_get("@#{key}_hash").present?
|
77
108
|
|
@@ -85,6 +116,7 @@ module Bulkrax
|
|
85
116
|
instance_variable_get("@#{key}_hash")
|
86
117
|
end
|
87
118
|
|
119
|
+
# @return [Array<String>]
|
88
120
|
def model_field_mappings
|
89
121
|
model_mappings = Bulkrax.field_mappings[self.class.to_s]&.dig('model', :from) || []
|
90
122
|
model_mappings |= ['model']
|
@@ -92,6 +124,7 @@ module Bulkrax
|
|
92
124
|
model_mappings
|
93
125
|
end
|
94
126
|
|
127
|
+
# @return [String]
|
95
128
|
def perform_method
|
96
129
|
if self.validate_only
|
97
130
|
'perform_now'
|
@@ -100,29 +133,55 @@ module Bulkrax
|
|
100
133
|
end
|
101
134
|
end
|
102
135
|
|
136
|
+
# The visibility of the record. Acceptable values are: "open", "embaro", "lease", "authenticated", "restricted". The default is "open"
|
137
|
+
#
|
138
|
+
# @return [String]
|
139
|
+
# @see https://github.com/samvera/hydra-head/blob/main/hydra-access-controls/app/models/concerns/hydra/access_controls/access_right.rb Hydra::AccessControls::AccessRight for details on the range of values.
|
140
|
+
# @see https://github.com/samvera/hyrax/blob/bd2bcffc33e183904be2c175367648815f25bc2b/app/services/hyrax/visibility_intention.rb Hyrax::VisibilityIntention for how we process the visibility.
|
103
141
|
def visibility
|
104
142
|
@visibility ||= self.parser_fields['visibility'] || 'open'
|
105
143
|
end
|
106
144
|
|
145
|
+
# @api public
|
146
|
+
#
|
147
|
+
# @param types [Array<Symbol>] the types of objects that we'll create.
|
148
|
+
#
|
149
|
+
# @see Bulkrax::Importer::DEFAULT_OBJECT_TYPES
|
150
|
+
# @see #create_collections
|
151
|
+
# @see #create_works
|
152
|
+
# @see #create_file_sets
|
153
|
+
# @see #create_relationships
|
154
|
+
def create_objects(types = [])
|
155
|
+
types.each do |object_type|
|
156
|
+
parser.send("create_#{object_type.pluralize}")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# @abstract Subclass and override {#create_collections} to implement behavior for the parser.
|
107
161
|
def create_collections
|
108
|
-
raise
|
162
|
+
raise NotImplementedError, 'must be defined' if importer?
|
109
163
|
end
|
110
164
|
|
165
|
+
# @abstract Subclass and override {#create_works} to implement behavior for the parser.
|
111
166
|
def create_works
|
112
|
-
raise
|
167
|
+
raise NotImplementedError, 'must be defined' if importer?
|
113
168
|
end
|
114
169
|
|
170
|
+
# @abstract Subclass and override {#create_file_sets} to implement behavior for the parser.
|
115
171
|
def create_file_sets
|
116
|
-
raise
|
172
|
+
raise NotImplementedError, 'must be defined' if importer?
|
117
173
|
end
|
118
174
|
|
175
|
+
# @abstract Subclass and override {#create_relationships} to implement behavior for the parser.
|
119
176
|
def create_relationships
|
120
|
-
raise
|
177
|
+
raise NotImplementedError, 'must be defined' if importer?
|
121
178
|
end
|
122
179
|
|
123
180
|
# Optional, define if using browse everything for file upload
|
124
181
|
def retrieve_cloud_files(files); end
|
125
182
|
|
183
|
+
# @param file [#path, #original_filename] the file object that with the relevant data for the
|
184
|
+
# import.
|
126
185
|
def write_import_file(file)
|
127
186
|
path = File.join(path_for_import, file.original_filename)
|
128
187
|
FileUtils.mv(
|
@@ -133,47 +192,58 @@ module Bulkrax
|
|
133
192
|
end
|
134
193
|
|
135
194
|
# Base path for imported and exported files
|
195
|
+
# @param [String]
|
196
|
+
# @return [String] the base path for files that this parser will "parse"
|
136
197
|
def base_path(type = 'import')
|
137
|
-
|
198
|
+
# account for multiple versions of hyku
|
199
|
+
is_multitenant = ENV['HYKU_MULTITENANT'] == 'true' || ENV['SETTINGS__MULTITENANCY__ENABLED'] == 'true'
|
200
|
+
is_multitenant ? File.join(Bulkrax.send("#{type}_path"), ::Site.instance.account.name) : Bulkrax.send("#{type}_path")
|
138
201
|
end
|
139
202
|
|
140
203
|
# Path where we'll store the import metadata and files
|
141
204
|
# this is used for uploaded and cloud files
|
205
|
+
# @return [String]
|
142
206
|
def path_for_import
|
143
207
|
@path_for_import = File.join(base_path, importerexporter.path_string)
|
144
208
|
FileUtils.mkdir_p(@path_for_import) unless File.exist?(@path_for_import)
|
145
209
|
@path_for_import
|
146
210
|
end
|
147
211
|
|
212
|
+
# @abstract Subclass and override {#setup_export_file} to implement behavior for the parser.
|
148
213
|
def setup_export_file
|
149
|
-
raise
|
214
|
+
raise NotImplementedError, 'must be defined' if exporter?
|
150
215
|
end
|
151
216
|
|
217
|
+
# @abstract Subclass and override {#write_files} to implement behavior for the parser.
|
152
218
|
def write_files
|
153
|
-
raise
|
219
|
+
raise NotImplementedError, 'must be defined' if exporter?
|
154
220
|
end
|
155
221
|
|
222
|
+
# @return [TrueClass,FalseClass]
|
156
223
|
def importer?
|
157
224
|
importerexporter.is_a?(Bulkrax::Importer)
|
158
225
|
end
|
159
226
|
|
227
|
+
# @return [TrueClass,FalseClass]
|
160
228
|
def exporter?
|
161
229
|
importerexporter.is_a?(Bulkrax::Exporter)
|
162
230
|
end
|
163
231
|
|
164
232
|
# @param limit [Integer] limit set on the importerexporter
|
165
233
|
# @param index [Integer] index of current iteration
|
166
|
-
# @return [
|
234
|
+
# @return [TrueClass,FalseClass]
|
167
235
|
def limit_reached?(limit, index)
|
168
236
|
return false if limit.nil? || limit.zero? # no limit
|
169
237
|
index >= limit
|
170
238
|
end
|
171
239
|
|
172
240
|
# Override to add specific validations
|
241
|
+
# @return [TrueClass,FalseClass]
|
173
242
|
def valid_import?
|
174
243
|
true
|
175
244
|
end
|
176
245
|
|
246
|
+
# @return [TrueClass,FalseClass]
|
177
247
|
def record_has_source_identifier(record, index)
|
178
248
|
if record[source_identifier].blank?
|
179
249
|
if Bulkrax.fill_in_blank_source_identifiers.present?
|
@@ -197,6 +267,7 @@ module Bulkrax
|
|
197
267
|
end
|
198
268
|
# rubocop:enable Rails/SkipsModelValidations
|
199
269
|
|
270
|
+
# @return [Array<String>]
|
200
271
|
def required_elements
|
201
272
|
if Bulkrax.fill_in_blank_source_identifiers
|
202
273
|
['title']
|
@@ -285,12 +356,14 @@ module Bulkrax
|
|
285
356
|
end
|
286
357
|
|
287
358
|
# Path for the import
|
359
|
+
# @return [String]
|
288
360
|
def import_file_path
|
289
361
|
@import_file_path ||= real_import_file_path
|
290
362
|
end
|
291
363
|
|
292
364
|
private
|
293
365
|
|
366
|
+
# @return [String]
|
294
367
|
def real_import_file_path
|
295
368
|
return importer_unzip_path if file? && zip?
|
296
369
|
parser_fields['import_file_path']
|
@@ -134,7 +134,8 @@ module Bulkrax
|
|
134
134
|
|
135
135
|
record.file_sets.each do |fs|
|
136
136
|
file_name = filename(fs)
|
137
|
-
next if file_name.blank?
|
137
|
+
next if file_name.blank? || fs.original_file.blank?
|
138
|
+
|
138
139
|
io = open(fs.original_file.uri)
|
139
140
|
file = Tempfile.new([file_name, File.extname(file_name)], binmode: true)
|
140
141
|
file.write(io.read)
|
@@ -180,7 +180,7 @@ module Bulkrax
|
|
180
180
|
end
|
181
181
|
|
182
182
|
def current_work_ids
|
183
|
-
ActiveSupport::
|
183
|
+
ActiveSupport::Deprecation.warn('Bulkrax::CsvParser#current_work_ids will be replaced with #current_record_ids in version 3.0')
|
184
184
|
current_record_ids
|
185
185
|
end
|
186
186
|
|
@@ -200,14 +200,14 @@ module Bulkrax
|
|
200
200
|
# get the parent collection and child collections
|
201
201
|
@collection_ids = ActiveFedora::SolrService.query("id:#{importerexporter.export_source} #{extra_filters}", method: :post, rows: 2_147_483_647).map(&:id)
|
202
202
|
@collection_ids += ActiveFedora::SolrService.query("has_model_ssim:Collection AND member_of_collection_ids_ssim:#{importerexporter.export_source}", method: :post, rows: 2_147_483_647).map(&:id)
|
203
|
+
find_child_file_sets(@work_ids)
|
203
204
|
when 'worktype'
|
204
205
|
@work_ids = ActiveFedora::SolrService.query("has_model_ssim:#{importerexporter.export_source + extra_filters}", method: :post, rows: 2_000_000_000).map(&:id)
|
206
|
+
find_child_file_sets(@work_ids)
|
205
207
|
when 'importer'
|
206
208
|
set_ids_for_exporting_from_importer
|
207
209
|
end
|
208
210
|
|
209
|
-
find_child_file_sets(@work_ids) if importerexporter.export_from == 'collection'
|
210
|
-
|
211
211
|
@work_ids + @collection_ids + @file_set_ids
|
212
212
|
end
|
213
213
|
# rubocop:enable Metrics/AbcSize
|
@@ -355,9 +355,9 @@ module Bulkrax
|
|
355
355
|
path = File.join(exporter_export_path, folder_count, 'files')
|
356
356
|
FileUtils.mkdir_p(path) unless File.exist? path
|
357
357
|
file = filename(fs)
|
358
|
-
|
359
|
-
next if file.blank?
|
358
|
+
next if file.blank? || fs.original_file.blank?
|
360
359
|
|
360
|
+
io = open(fs.original_file.uri)
|
361
361
|
File.open(File.join(path, file), 'wb') do |f|
|
362
362
|
f.write(io.read)
|
363
363
|
f.close
|
@@ -440,7 +440,7 @@ module Bulkrax
|
|
440
440
|
file_mapping = Bulkrax.field_mappings.dig(self.class.to_s, 'file', :from)&.first&.to_sym || :file
|
441
441
|
next if r[file_mapping].blank?
|
442
442
|
|
443
|
-
r[file_mapping].split(
|
443
|
+
r[file_mapping].split(Bulkrax.multi_value_element_split_on).map do |f|
|
444
444
|
file = File.join(path_to_files, f.tr(' ', '_'))
|
445
445
|
if File.exist?(file) # rubocop:disable Style/GuardClause
|
446
446
|
file
|
@@ -455,9 +455,10 @@ module Bulkrax
|
|
455
455
|
def path_to_files(**args)
|
456
456
|
filename = args.fetch(:filename, '')
|
457
457
|
|
458
|
-
@path_to_files
|
459
|
-
|
460
|
-
|
458
|
+
return @path_to_files if @path_to_files.present? && filename.blank?
|
459
|
+
@path_to_files = File.join(
|
460
|
+
zip? ? importer_unzip_path : File.dirname(import_file_path), 'files', filename
|
461
|
+
)
|
461
462
|
end
|
462
463
|
|
463
464
|
private
|
@@ -467,7 +468,7 @@ module Bulkrax
|
|
467
468
|
entry_uid ||= if Bulkrax.fill_in_blank_source_identifiers.present?
|
468
469
|
Bulkrax.fill_in_blank_source_identifiers.call(self, records.find_index(collection_hash))
|
469
470
|
else
|
470
|
-
collection_hash[:title].split(
|
471
|
+
collection_hash[:title].split(Bulkrax.multi_value_element_split_on).first
|
471
472
|
end
|
472
473
|
|
473
474
|
entry_uid
|
@@ -13,8 +13,7 @@ module Bulkrax
|
|
13
13
|
def client
|
14
14
|
@client ||= OAI::Client.new(importerexporter.parser_fields['base_url'],
|
15
15
|
headers: headers,
|
16
|
-
parser: 'libxml'
|
17
|
-
metadata_prefix: importerexporter.parser_fields['metadata_prefix'])
|
16
|
+
parser: 'libxml')
|
18
17
|
rescue StandardError
|
19
18
|
raise OAIError
|
20
19
|
end
|
@@ -32,6 +31,7 @@ module Bulkrax
|
|
32
31
|
end
|
33
32
|
|
34
33
|
def records(opts = {})
|
34
|
+
opts[:metadata_prefix] ||= importerexporter.parser_fields['metadata_prefix']
|
35
35
|
opts[:set] = collection_name unless collection_name == 'all'
|
36
36
|
|
37
37
|
opts[:from] = importerexporter&.last_imported_at&.strftime("%Y-%m-%d") if importerexporter.last_imported_at && only_updates
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Bulkrax
|
3
|
+
# This module is rather destructive; it will break relationships between the works, file sets, and
|
4
|
+
# collections that were imported via an importer. You probably don't want to run this on your
|
5
|
+
# data, except in the case where you have been testing a Bulkrax::Importer, the parsers and
|
6
|
+
# mappings. Then, you might have relationships that you want to remove.
|
7
|
+
#
|
8
|
+
# tl;dr - Caution this will break things!
|
9
|
+
class RemoveRelationshipsForImporter
|
10
|
+
# @api public
|
11
|
+
#
|
12
|
+
# Remove the relationships of the works and collections for all of the Bulkrax::Entry records
|
13
|
+
# associated with the given Bulkrax::Importer.
|
14
|
+
#
|
15
|
+
# @param importer [Bulkrax::Importer]
|
16
|
+
# @param with_progress_bar [Boolean]
|
17
|
+
def self.break_relationships_for!(importer:, with_progress_bar: false)
|
18
|
+
entries = importer.entries.select(&:succeeded?)
|
19
|
+
progress_bar = build_progress_bar_for(with_progress_bar: with_progress_bar, entries: entries)
|
20
|
+
new(progress_bar: progress_bar, entries: entries).break_relationships!
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
# A null object that conforms to this class's use of a progress bar.
|
26
|
+
module NullProgressBar
|
27
|
+
def self.increment; end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
#
|
32
|
+
# @return [#increment]
|
33
|
+
def self.build_progress_bar_for(with_progress_bar:, entries:)
|
34
|
+
return NullProgressBar unless with_progress_bar
|
35
|
+
|
36
|
+
begin
|
37
|
+
require 'ruby-progressbar'
|
38
|
+
ProgessBar.create(total: entries.count)
|
39
|
+
rescue LoadError
|
40
|
+
Rails.logger.info("Using NullProgressBar because ProgressBar is not available due to a LoadError.")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param entries [#each]
|
45
|
+
# @param progress_bar [#increment]
|
46
|
+
def initialize(entries:, progress_bar:)
|
47
|
+
@progress_bar = progress_bar
|
48
|
+
@entries = entries
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :entries, :progress_bar
|
52
|
+
|
53
|
+
def break_relationships!
|
54
|
+
entries.each do |entry|
|
55
|
+
progress_bar.increment
|
56
|
+
|
57
|
+
obj = entry.factory.find
|
58
|
+
next if obj.is_a?(FileSet) # FileSets must be attached to a Work
|
59
|
+
|
60
|
+
if obj.is_a?(Collection)
|
61
|
+
remove_relationships_from_collection(obj)
|
62
|
+
else
|
63
|
+
remove_relationships_from_work(obj)
|
64
|
+
end
|
65
|
+
|
66
|
+
obj.try(:reindex_extent=, Hyrax::Adapters::NestingIndexAdapter::LIMITED_REINDEX)
|
67
|
+
obj.save!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def remove_relationships_from_collection(collection)
|
72
|
+
# Remove child work relationships
|
73
|
+
collection.member_works.each do |work|
|
74
|
+
change = work.member_of_collections.delete(collection)
|
75
|
+
work.save! if change.present?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Remove parent collection relationships
|
79
|
+
collection.member_of_collections.each do |parent_col|
|
80
|
+
Hyrax::Collections::NestedCollectionPersistenceService
|
81
|
+
.remove_nested_relationship_for(parent: parent_col, child: collection)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Remove child collection relationships
|
85
|
+
collection.member_collections.each do |child_col|
|
86
|
+
Hyrax::Collections::NestedCollectionPersistenceService
|
87
|
+
.remove_nested_relationship_for(parent: collection, child: child_col)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def remove_relationships_from_work(work)
|
92
|
+
# Remove parent collection relationships
|
93
|
+
work.member_of_collections = []
|
94
|
+
|
95
|
+
# Remove parent work relationships
|
96
|
+
work.member_of_works.each do |parent_work|
|
97
|
+
parent_work.members.delete(work)
|
98
|
+
parent_work.save!
|
99
|
+
end
|
100
|
+
|
101
|
+
# Remove child work relationships
|
102
|
+
work.member_works.each do |child_work|
|
103
|
+
work.member_works.delete(child_work)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -29,7 +29,7 @@
|
|
29
29
|
|
30
30
|
<%= form.input :export_source_importer,
|
31
31
|
label: t('bulkrax.exporter.labels.importer'),
|
32
|
-
|
32
|
+
required: true,
|
33
33
|
prompt: 'Select from the list',
|
34
34
|
label_html: { class: 'importer export-source-option hidden' },
|
35
35
|
input_html: { class: 'importer export-source-option hidden' },
|
@@ -38,7 +38,7 @@
|
|
38
38
|
<%= form.input :export_source_collection,
|
39
39
|
prompt: 'Start typing ...',
|
40
40
|
label: t('bulkrax.exporter.labels.collection'),
|
41
|
-
|
41
|
+
required: true,
|
42
42
|
placeholder: @collection&.title&.first,
|
43
43
|
label_html: { class: 'collection export-source-option hidden' },
|
44
44
|
input_html: {
|
@@ -52,7 +52,7 @@
|
|
52
52
|
|
53
53
|
<%= form.input :export_source_worktype,
|
54
54
|
label: t('bulkrax.exporter.labels.worktype'),
|
55
|
-
|
55
|
+
required: true,
|
56
56
|
prompt: 'Select from the list',
|
57
57
|
label_html: { class: 'worktype export-source-option hidden' },
|
58
58
|
input_html: { class: 'worktype export-source-option hidden' },
|
@@ -87,49 +87,25 @@
|
|
87
87
|
|
88
88
|
<p class='bulkrax-p-align'><strong><%= t('bulkrax.exporter.labels.field_mapping') %>:</strong></p>
|
89
89
|
|
90
|
-
<p class=
|
91
|
-
<strong
|
92
|
-
<%= @exporter.
|
90
|
+
<p class="bulkrax-p-align" title="<%= @exporter.last_run&.total_work_entries %> processed, <%= @exporter.last_run&.failed_records %> failed">
|
91
|
+
<strong>Total Entries:</strong>
|
92
|
+
<%= @exporter.last_run&.total_work_entries %>
|
93
93
|
</p>
|
94
94
|
<br>
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
<% @work_entries.each do |e| %>
|
110
|
-
<tr>
|
111
|
-
<td><%= link_to e.identifier, bulkrax.exporter_entry_path(@exporter.id, e.id) %></td>
|
112
|
-
<td><%= e.id %></td>
|
113
|
-
<% if e.status == 'Complete' %>
|
114
|
-
<td><span class='glyphicon glyphicon-ok' style='color: green;'></span> <%= e.status %></td>
|
115
|
-
<% elsif e.status == 'Pending' %>
|
116
|
-
<td><span class='glyphicon glyphicon-option-horizontal' style='color: blue;'></span> <%= e.status %></td>
|
117
|
-
<% else %>
|
118
|
-
<td><span class='glyphicon glyphicon-remove' style='color: red;'></span> <%= e.status %></td>
|
119
|
-
<% end %>
|
120
|
-
<% if e.last_error.present? %>
|
121
|
-
<td><%= link_to e.last_error.dig('error_class'), bulkrax.exporter_entry_path(@exporter.id, e.id) %></td>
|
122
|
-
<% else %>
|
123
|
-
<td></td>
|
124
|
-
<% end %>
|
125
|
-
<td><%= e.status_at %></td>
|
126
|
-
<td><%= link_to raw("<span class='glyphicon glyphicon-info-sign'></span>"), bulkrax.exporter_entry_path(@exporter.id, e.id) %></td>
|
127
|
-
</tr>
|
128
|
-
<% end %>
|
129
|
-
</tbody>
|
130
|
-
</table>
|
131
|
-
<%= page_entries_info(@work_entries) %><br>
|
132
|
-
<%= paginate(@work_entries, param_name: :work_entries_page) %>
|
95
|
+
|
96
|
+
<div class="bulkrax-nav-tab-bottom-margin">
|
97
|
+
<!-- Nav tabs -->
|
98
|
+
<ul class="bulkrax-nav-tab-top-margin tab-nav nav nav-tabs" role="tablist">
|
99
|
+
<li role="presentation" class='active'><a href="#work-entries" aria-controls="work-entries" role="tab" data-toggle="tab">Work Entries</a></li>
|
100
|
+
<li role="presentation"><a href="#collection-entries" aria-controls="collection-entries" role="tab" data-toggle="tab">Collection Entries</a></li>
|
101
|
+
<li role="presentation"><a href="#file-set-entries" aria-controls="file-set-entries" role="tab" data-toggle="tab">File Set Entries</a></li>
|
102
|
+
</ul>
|
103
|
+
<!-- Tab panes -->
|
104
|
+
<div class="tab-content outline">
|
105
|
+
<%= render partial: 'bulkrax/shared/work_entries_tab', locals: { item: @exporter, entries: @work_entries } %>
|
106
|
+
<%= render partial: 'bulkrax/shared/collection_entries_tab', locals: { item: @exporter, entries: @collection_entries } %>
|
107
|
+
<%= render partial: 'bulkrax/shared/file_set_entries_tab', locals: { item: @exporter, entries: @file_set_entries } %>
|
108
|
+
</div>
|
133
109
|
<br>
|
134
110
|
<%= link_to 'Edit', edit_exporter_path(@exporter) %>
|
135
111
|
|
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
<div class="row">
|
6
6
|
<div class="col-md-12">
|
7
|
-
<div class="panel panel-default tabs">
|
7
|
+
<div class="panel panel-default tabs importer-form">
|
8
8
|
|
9
9
|
<%= simple_form_for @importer, html: { multipart: true } do |form| %>
|
10
10
|
<%= render 'form', importer: @importer, form: form %>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
<div class="row">
|
6
6
|
<div class="col-md-12">
|
7
|
-
<div class="panel panel-default tabs">
|
7
|
+
<div class="panel panel-default tabs importer-form">
|
8
8
|
<%= simple_form_for @importer, html: { multipart: true } do |form| %>
|
9
9
|
<%= render 'form', importer: @importer, form: form %>
|
10
10
|
<div class="panel-footer">
|