bulkrax 4.2.1 → 4.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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">
|