bulkrax 5.0.0 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/bulkrax/entries_controller.rb +4 -2
- data/app/controllers/bulkrax/exporters_controller.rb +13 -9
- data/app/controllers/bulkrax/importers_controller.rb +10 -10
- data/app/helpers/bulkrax/application_helper.rb +1 -1
- data/app/helpers/bulkrax/importers_helper.rb +2 -2
- data/app/helpers/bulkrax/validation_helper.rb +4 -4
- data/app/jobs/bulkrax/create_relationships_job.rb +78 -59
- data/app/jobs/bulkrax/delete_job.rb +1 -1
- data/app/jobs/bulkrax/export_work_job.rb +2 -2
- data/app/jobs/bulkrax/import_file_set_job.rb +1 -1
- data/app/jobs/bulkrax/import_work_job.rb +20 -7
- data/app/jobs/bulkrax/importer_job.rb +2 -2
- data/app/jobs/bulkrax/schedule_relationships_job.rb +2 -1
- data/app/matchers/bulkrax/application_matcher.rb +1 -0
- data/app/models/bulkrax/csv_entry.rb +93 -24
- data/app/models/bulkrax/exporter.rb +18 -19
- data/app/models/bulkrax/importer.rb +5 -5
- data/app/models/bulkrax/importer_run.rb +6 -0
- data/app/models/bulkrax/oai_entry.rb +14 -2
- data/app/models/bulkrax/pending_relationship.rb +4 -0
- data/app/models/concerns/bulkrax/dynamic_record_lookup.rb +3 -1
- data/app/models/concerns/bulkrax/export_behavior.rb +6 -4
- data/app/models/concerns/bulkrax/has_matchers.rb +1 -0
- data/app/models/concerns/bulkrax/import_behavior.rb +6 -3
- data/app/models/concerns/bulkrax/importer_exporter_behavior.rb +9 -1
- data/app/models/concerns/bulkrax/status_info.rb +9 -4
- data/app/parsers/bulkrax/application_parser.rb +14 -16
- data/app/parsers/bulkrax/bagit_parser.rb +6 -17
- data/app/parsers/bulkrax/csv_parser.rb +43 -111
- data/app/parsers/bulkrax/oai_dc_parser.rb +2 -2
- data/app/parsers/bulkrax/parser_export_record_set.rb +281 -0
- data/app/parsers/bulkrax/xml_parser.rb +9 -5
- data/app/services/bulkrax/remove_relationships_for_importer.rb +4 -2
- data/app/views/bulkrax/entries/show.html.erb +1 -1
- data/app/views/bulkrax/exporters/_form.html.erb +60 -45
- data/app/views/bulkrax/exporters/edit.html.erb +1 -1
- data/app/views/bulkrax/exporters/index.html.erb +2 -2
- data/app/views/bulkrax/exporters/new.html.erb +1 -1
- data/app/views/bulkrax/exporters/show.html.erb +3 -3
- data/app/views/bulkrax/importers/_bagit_fields.html.erb +13 -12
- data/app/views/bulkrax/importers/_csv_fields.html.erb +13 -12
- data/app/views/bulkrax/importers/_form.html.erb +5 -5
- data/app/views/bulkrax/importers/_oai_fields.html.erb +12 -10
- data/app/views/bulkrax/importers/_xml_fields.html.erb +12 -11
- data/app/views/bulkrax/importers/show.html.erb +18 -16
- data/app/views/bulkrax/shared/_collection_entries_tab.html.erb +6 -6
- data/app/views/bulkrax/shared/_file_set_entries_tab.html.erb +6 -6
- data/app/views/bulkrax/shared/_work_entries_tab.html.erb +6 -6
- data/config/locales/bulkrax.en.yml +26 -0
- data/lib/bulkrax/entry_spec_helper.rb +190 -0
- data/lib/bulkrax/version.rb +1 -1
- data/lib/bulkrax.rb +124 -45
- data/lib/generators/bulkrax/templates/config/initializers/bulkrax.rb +1 -1
- data/lib/tasks/reset.rake +1 -1
- metadata +5 -3
@@ -13,15 +13,49 @@ module Bulkrax
|
|
13
13
|
data.headers.flatten.compact.uniq
|
14
14
|
end
|
15
15
|
|
16
|
+
class_attribute(:csv_read_data_options, default: {})
|
17
|
+
|
16
18
|
# there's a risk that this reads the whole file into memory and could cause a memory leak
|
17
19
|
def self.read_data(path)
|
18
20
|
raise StandardError, 'CSV path empty' if path.blank?
|
19
|
-
|
21
|
+
options = {
|
20
22
|
headers: true,
|
21
23
|
header_converters: ->(h) { h.to_sym },
|
22
|
-
encoding: 'utf-8'
|
24
|
+
encoding: 'utf-8'
|
25
|
+
}.merge(csv_read_data_options)
|
26
|
+
|
27
|
+
results = CSV.read(path, **options)
|
28
|
+
csv_wrapper_class.new(results)
|
23
29
|
end
|
24
30
|
|
31
|
+
# The purpose of this class is to reject empty lines. This causes lots of grief in importing.
|
32
|
+
# But why not use {CSV.read}'s `skip_lines` option? Because for some CSVs, it will never finish
|
33
|
+
# reading the file.
|
34
|
+
#
|
35
|
+
# There is a spec that demonstrates this approach works.
|
36
|
+
class CsvWrapper
|
37
|
+
include Enumerable
|
38
|
+
def initialize(original)
|
39
|
+
@original = original
|
40
|
+
end
|
41
|
+
|
42
|
+
delegate :headers, to: :@original
|
43
|
+
|
44
|
+
def each
|
45
|
+
@original.each do |row|
|
46
|
+
next if all_fields_are_empty_for(row: row)
|
47
|
+
yield(row)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def all_fields_are_empty_for(row:)
|
54
|
+
row.to_hash.values.all?(&:blank?)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
class_attribute :csv_wrapper_class, default: CsvWrapper
|
58
|
+
|
25
59
|
def self.data_for_entry(data, _source_id, parser)
|
26
60
|
# If a multi-line CSV data is passed, grab the first row
|
27
61
|
data = data.first if data.is_a?(CSV::Table)
|
@@ -35,11 +69,7 @@ module Bulkrax
|
|
35
69
|
end
|
36
70
|
|
37
71
|
def build_metadata
|
38
|
-
|
39
|
-
unless importerexporter.parser.required_elements?(keys_without_numbers(record.keys))
|
40
|
-
raise StandardError,
|
41
|
-
"Missing required elements, missing element(s) are: #{importerexporter.parser.missing_elements(keys_without_numbers(record.keys)).join(', ')}"
|
42
|
-
end
|
72
|
+
validate_record
|
43
73
|
|
44
74
|
self.parsed_metadata = {}
|
45
75
|
add_identifier
|
@@ -56,6 +86,12 @@ module Bulkrax
|
|
56
86
|
self.parsed_metadata
|
57
87
|
end
|
58
88
|
|
89
|
+
def validate_record
|
90
|
+
raise StandardError, 'Record not found' if record.nil?
|
91
|
+
raise StandardError, "Missing required elements, missing element(s) are: "\
|
92
|
+
"#{importerexporter.parser.missing_elements(record).join(', ')}" unless importerexporter.parser.required_elements?(record)
|
93
|
+
end
|
94
|
+
|
59
95
|
def add_identifier
|
60
96
|
self.parsed_metadata[work_identifier] = [record[source_identifier]]
|
61
97
|
end
|
@@ -67,9 +103,10 @@ module Bulkrax
|
|
67
103
|
end
|
68
104
|
|
69
105
|
def add_metadata_for_model
|
70
|
-
if factory_class == Collection
|
71
|
-
add_collection_type_gid
|
72
|
-
|
106
|
+
if defined?(::Collection) && factory_class == ::Collection
|
107
|
+
add_collection_type_gid if defined?(::Hyrax)
|
108
|
+
# add any additional collection metadata methods here
|
109
|
+
elsif factory_class == Bulkrax.file_model_class
|
73
110
|
validate_presence_of_filename!
|
74
111
|
add_path_to_file
|
75
112
|
validate_presence_of_parent!
|
@@ -106,7 +143,7 @@ module Bulkrax
|
|
106
143
|
self.parsed_metadata = {}
|
107
144
|
|
108
145
|
build_system_metadata
|
109
|
-
build_files_metadata
|
146
|
+
build_files_metadata if defined?(Collection) && !hyrax_record.is_a?(Collection)
|
110
147
|
build_relationship_metadata
|
111
148
|
build_mapping_metadata
|
112
149
|
self.save!
|
@@ -157,26 +194,48 @@ module Bulkrax
|
|
157
194
|
end
|
158
195
|
end
|
159
196
|
|
197
|
+
# The purpose of this helper module is to make easier the testing of the rather complex
|
198
|
+
# switching logic for determining the method we use for building the value.
|
199
|
+
module AttributeBuilderMethod
|
200
|
+
# @param key [Symbol]
|
201
|
+
# @param value [Hash<String, Object>]
|
202
|
+
# @param entry [Bulkrax::Entry]
|
203
|
+
#
|
204
|
+
# @return [NilClass] when we won't be processing this field
|
205
|
+
# @return [Symbol] (either :build_value or :build_object)
|
206
|
+
def self.for(key:, value:, entry:)
|
207
|
+
return if key == 'model'
|
208
|
+
return if key == 'file'
|
209
|
+
return if key == entry.related_parents_parsed_mapping
|
210
|
+
return if key == entry.related_children_parsed_mapping
|
211
|
+
return if value['excluded'] || value[:excluded]
|
212
|
+
return if Bulkrax.reserved_properties.include?(key) && !entry.field_supported?(key)
|
213
|
+
|
214
|
+
object_key = key if value.key?('object') || value.key?(:object)
|
215
|
+
return unless entry.hyrax_record.respond_to?(key.to_s) || object_key.present?
|
216
|
+
|
217
|
+
models_to_skip = Array.wrap(value['skip_object_for_model_names'] || value[:skip_object_for_model_names] || [])
|
218
|
+
|
219
|
+
return :build_value if models_to_skip.detect { |model| entry.factory_class.model_name.name == model }
|
220
|
+
return :build_object if object_key.present?
|
221
|
+
|
222
|
+
:build_value
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
160
226
|
def build_mapping_metadata
|
161
227
|
mapping = fetch_field_mapping
|
162
228
|
mapping.each do |key, value|
|
163
|
-
|
164
|
-
next
|
165
|
-
next if value['excluded']
|
166
|
-
next if Bulkrax.reserved_properties.include?(key) && !field_supported?(key)
|
167
|
-
|
168
|
-
object_key = key if value.key?('object')
|
169
|
-
next unless hyrax_record.respond_to?(key.to_s) || object_key.present?
|
229
|
+
method_name = AttributeBuilderMethod.for(key: key, value: value, entry: self)
|
230
|
+
next unless method_name
|
170
231
|
|
171
|
-
|
172
|
-
build_object(value)
|
173
|
-
else
|
174
|
-
build_value(key, value)
|
175
|
-
end
|
232
|
+
send(method_name, key, value)
|
176
233
|
end
|
177
234
|
end
|
178
235
|
|
179
|
-
def build_object(value)
|
236
|
+
def build_object(_key, value)
|
237
|
+
return unless hyrax_record.respond_to?(value['object'])
|
238
|
+
|
180
239
|
data = hyrax_record.send(value['object'])
|
181
240
|
return if data.empty?
|
182
241
|
|
@@ -185,6 +244,8 @@ module Bulkrax
|
|
185
244
|
end
|
186
245
|
|
187
246
|
def build_value(key, value)
|
247
|
+
return unless hyrax_record.respond_to?(key.to_s)
|
248
|
+
|
188
249
|
data = hyrax_record.send(key.to_s)
|
189
250
|
if data.is_a?(ActiveTriples::Relation)
|
190
251
|
if value['join']
|
@@ -217,6 +278,14 @@ module Bulkrax
|
|
217
278
|
end
|
218
279
|
|
219
280
|
def object_metadata(data)
|
281
|
+
# NOTE: What is `d` in this case:
|
282
|
+
#
|
283
|
+
# "[{\"single_object_first_name\"=>\"Fake\", \"single_object_last_name\"=>\"Fakerson\", \"single_object_position\"=>\"Leader, Jester, Queen\", \"single_object_language\"=>\"english\"}]"
|
284
|
+
#
|
285
|
+
# The above is a stringified version of a Ruby string. Using eval is a very bad idea as it
|
286
|
+
# will execute the value of `d` within the full Ruby interpreter context.
|
287
|
+
#
|
288
|
+
# TODO: Would it be possible to store this as a non-string? Maybe the actual Ruby Array and Hash?
|
220
289
|
data = data.map { |d| eval(d) }.flatten # rubocop:disable Security/Eval
|
221
290
|
|
222
291
|
data.each_with_index do |obj, index|
|
@@ -18,18 +18,9 @@ module Bulkrax
|
|
18
18
|
|
19
19
|
def export
|
20
20
|
current_run && setup_export_path
|
21
|
-
|
22
|
-
when 'collection'
|
23
|
-
create_from_collection
|
24
|
-
when 'importer'
|
25
|
-
create_from_importer
|
26
|
-
when 'worktype'
|
27
|
-
create_from_worktype
|
28
|
-
when 'all'
|
29
|
-
create_from_all
|
30
|
-
end
|
21
|
+
send("create_from_#{self.export_from}")
|
31
22
|
rescue StandardError => e
|
32
|
-
|
23
|
+
set_status_info(e)
|
33
24
|
end
|
34
25
|
|
35
26
|
# #export_source accessors
|
@@ -69,7 +60,7 @@ module Bulkrax
|
|
69
60
|
end
|
70
61
|
|
71
62
|
def workflow_status_list
|
72
|
-
Sipity::WorkflowState.all.map { |s| [s.name&.titleize, s.name] }.uniq
|
63
|
+
Sipity::WorkflowState.all.map { |s| [s.name&.titleize, s.name] }.uniq if defined?(::Hyrax)
|
73
64
|
end
|
74
65
|
|
75
66
|
# If field_mapping is empty, setup a default based on the export_properties
|
@@ -84,12 +75,20 @@ module Bulkrax
|
|
84
75
|
end
|
85
76
|
|
86
77
|
def export_from_list
|
87
|
-
|
88
|
-
[
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
78
|
+
if defined?(::Hyrax)
|
79
|
+
[
|
80
|
+
[I18n.t('bulkrax.exporter.labels.importer'), 'importer'],
|
81
|
+
[I18n.t('bulkrax.exporter.labels.collection'), 'collection'],
|
82
|
+
[I18n.t('bulkrax.exporter.labels.worktype'), 'worktype'],
|
83
|
+
[I18n.t('bulkrax.exporter.labels.all'), 'all']
|
84
|
+
]
|
85
|
+
else
|
86
|
+
[
|
87
|
+
[I18n.t('bulkrax.exporter.labels.importer'), 'importer'],
|
88
|
+
[I18n.t('bulkrax.exporter.labels.collection'), 'collection'],
|
89
|
+
[I18n.t('bulkrax.exporter.labels.all'), 'all']
|
90
|
+
]
|
91
|
+
end
|
93
92
|
end
|
94
93
|
|
95
94
|
def export_type_list
|
@@ -131,7 +130,7 @@ module Bulkrax
|
|
131
130
|
end
|
132
131
|
|
133
132
|
def export_properties
|
134
|
-
properties =
|
133
|
+
properties = Bulkrax.curation_concerns.map { |work| work.properties.keys }.flatten.uniq.sort
|
135
134
|
properties.reject { |prop| Bulkrax.reserved_properties.include?(prop) }
|
136
135
|
end
|
137
136
|
|
@@ -15,7 +15,7 @@ module Bulkrax
|
|
15
15
|
has_many :entries, as: :importerexporter, dependent: :destroy
|
16
16
|
|
17
17
|
validates :name, presence: true
|
18
|
-
validates :admin_set_id, presence: true
|
18
|
+
validates :admin_set_id, presence: true if defined?(::Hyrax)
|
19
19
|
validates :parser_klass, presence: true
|
20
20
|
|
21
21
|
delegate :valid_import?, :write_errored_entries_file, :visibility, to: :parser
|
@@ -47,12 +47,12 @@ module Bulkrax
|
|
47
47
|
if importer_run.failed_records.positive?
|
48
48
|
if importer_run.invalid_records.present?
|
49
49
|
e = Bulkrax::ImportFailed.new('Failed with Invalid Records', importer_run.invalid_records.split("\n"))
|
50
|
-
importer_run.importer.
|
50
|
+
importer_run.importer.set_status_info(e)
|
51
51
|
else
|
52
|
-
importer_run.importer.
|
52
|
+
importer_run.importer.set_status_info('Complete (with failures)')
|
53
53
|
end
|
54
54
|
else
|
55
|
-
importer_run.importer.
|
55
|
+
importer_run.importer.set_status_info('Complete')
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
@@ -160,7 +160,7 @@ module Bulkrax
|
|
160
160
|
types = types_array || DEFAULT_OBJECT_TYPES
|
161
161
|
parser.create_objects(types)
|
162
162
|
rescue StandardError => e
|
163
|
-
|
163
|
+
set_status_info(e)
|
164
164
|
end
|
165
165
|
|
166
166
|
# Prepend the base_url to ensure unique set identifiers
|
@@ -9,5 +9,11 @@ module Bulkrax
|
|
9
9
|
def parents
|
10
10
|
pending_relationships.pluck(:parent_id).uniq
|
11
11
|
end
|
12
|
+
|
13
|
+
def user
|
14
|
+
# An importer might not have a user, the CLI ingest need not assign a user. As such, we
|
15
|
+
# fallback to the configured user.
|
16
|
+
importer.user || Bulkrax.fallback_user_for_importer_exporter_processing
|
17
|
+
end
|
12
18
|
end
|
13
19
|
end
|
@@ -9,6 +9,15 @@ module Bulkrax
|
|
9
9
|
|
10
10
|
delegate :record, to: :raw_record
|
11
11
|
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
# Included to assist in testing; namely so that you can copy down an OAI entry, store it locally,
|
15
|
+
# and then manually construct an {OAI::GetRecordResponse}.
|
16
|
+
#
|
17
|
+
# @see Bulkrax::EntrySpecHelper.oai_entry_for
|
18
|
+
attr_writer :raw_record
|
19
|
+
|
20
|
+
# @return [OAI::GetRecordResponse]
|
12
21
|
def raw_record
|
13
22
|
@raw_record ||= client.get_record(identifier: identifier, metadata_prefix: parser.parser_fields['metadata_prefix'])
|
14
23
|
end
|
@@ -28,7 +37,7 @@ module Bulkrax
|
|
28
37
|
def build_metadata
|
29
38
|
self.parsed_metadata = {}
|
30
39
|
self.parsed_metadata[work_identifier] = [record.header.identifier]
|
31
|
-
self.raw_metadata = {
|
40
|
+
self.raw_metadata = { record: record.metadata.to_s, header: record.header.to_s }
|
32
41
|
|
33
42
|
# We need to establish the #factory_class before we proceed with the metadata. See
|
34
43
|
# https://github.com/samvera-labs/bulkrax/issues/702 for further details.
|
@@ -91,7 +100,8 @@ module Bulkrax
|
|
91
100
|
# If OAI-PMH doesn't return setSpec in the headers for GetRecord, use parser.collection_name
|
92
101
|
# in this case, if 'All' is selected, records will not be added to a collection.
|
93
102
|
def find_collection_ids
|
94
|
-
return self.collection_ids if
|
103
|
+
return self.collection_ids if defined?(@called_find_collection_ids)
|
104
|
+
|
95
105
|
if sets.blank? || parser.collection_name != 'all'
|
96
106
|
collection = find_collection(importerexporter.unique_collection_identifier(parser.collection_name))
|
97
107
|
self.collection_ids << collection.id if collection.present? && !self.collection_ids.include?(collection.id)
|
@@ -101,6 +111,8 @@ module Bulkrax
|
|
101
111
|
self.collection_ids << c.id if c.present? && !self.collection_ids.include?(c.id)
|
102
112
|
end
|
103
113
|
end
|
114
|
+
|
115
|
+
@called_find_collection_ids = true
|
104
116
|
return self.collection_ids
|
105
117
|
end
|
106
118
|
end
|
@@ -3,5 +3,9 @@
|
|
3
3
|
module Bulkrax
|
4
4
|
class PendingRelationship < ApplicationRecord
|
5
5
|
belongs_to :importer_run
|
6
|
+
|
7
|
+
# Ideally we wouldn't have a column named "order", as it is a reserved SQL term. However, if we
|
8
|
+
# quote the column, all is well...for the application.
|
9
|
+
scope :ordered, -> { order(Arel.sql("#{quoted_table_name}.#{connection.quote_column_name('order')}")) }
|
6
10
|
end
|
7
11
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Bulkrax
|
4
|
+
# TODO: Extract methods to class methods; there's no reason for these methods to be a mixin.
|
5
|
+
# TODO: Add specs to test in isolation
|
4
6
|
module DynamicRecordLookup
|
5
7
|
# Search entries, collections, and every available work type for a record that
|
6
8
|
# has the provided identifier.
|
@@ -40,7 +42,7 @@ module Bulkrax
|
|
40
42
|
@available_work_types ||= if defined?(::Hyku)
|
41
43
|
::Site.instance.available_works.map(&:constantize)
|
42
44
|
else
|
43
|
-
|
45
|
+
Bulkrax.curation_concerns
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'marcel'
|
3
|
+
|
2
4
|
module Bulkrax
|
3
5
|
module ExportBehavior
|
4
6
|
extend ActiveSupport::Concern
|
@@ -10,9 +12,9 @@ module Bulkrax
|
|
10
12
|
rescue RSolr::Error::Http, CollectionsCreatedError => e
|
11
13
|
raise e
|
12
14
|
rescue StandardError => e
|
13
|
-
|
15
|
+
set_status_info(e)
|
14
16
|
else
|
15
|
-
|
17
|
+
set_status_info
|
16
18
|
end
|
17
19
|
|
18
20
|
def build_export_metadata
|
@@ -27,8 +29,8 @@ module Bulkrax
|
|
27
29
|
def filename(file_set)
|
28
30
|
return if file_set.original_file.blank?
|
29
31
|
fn = file_set.original_file.file_name.first
|
30
|
-
mime =
|
31
|
-
ext_mime =
|
32
|
+
mime = ::Marcel::MimeType.for(file_set.original_file.mime_type)
|
33
|
+
ext_mime = ::Marcel::MimeType.for(file_set.original_file.file_name)
|
32
34
|
if fn.include?(file_set.id) || importerexporter.metadata_only?
|
33
35
|
filename = "#{fn}.#{mime.to_sym}"
|
34
36
|
filename = fn if mime.to_s == ext_mime.to_s
|
@@ -11,16 +11,16 @@ module Bulkrax
|
|
11
11
|
unless self.importerexporter.validate_only
|
12
12
|
raise CollectionsCreatedError unless collections_created?
|
13
13
|
@item = factory.run!
|
14
|
-
add_user_to_permission_templates! if self.class.to_s.include?("Collection")
|
14
|
+
add_user_to_permission_templates! if self.class.to_s.include?("Collection") && defined?(::Hyrax)
|
15
15
|
parent_jobs if self.parsed_metadata[related_parents_parsed_mapping]&.join.present?
|
16
16
|
child_jobs if self.parsed_metadata[related_children_parsed_mapping]&.join.present?
|
17
17
|
end
|
18
18
|
rescue RSolr::Error::Http, CollectionsCreatedError => e
|
19
19
|
raise e
|
20
20
|
rescue StandardError => e
|
21
|
-
|
21
|
+
set_status_info(e)
|
22
22
|
else
|
23
|
-
|
23
|
+
set_status_info
|
24
24
|
ensure
|
25
25
|
self.save!
|
26
26
|
end
|
@@ -93,6 +93,8 @@ module Bulkrax
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def add_admin_set_id
|
96
|
+
return unless defined?(::Hyrax)
|
97
|
+
|
96
98
|
self.parsed_metadata['admin_set_id'] = importerexporter.admin_set_id if self.parsed_metadata['admin_set_id'].blank?
|
97
99
|
end
|
98
100
|
|
@@ -165,6 +167,7 @@ module Bulkrax
|
|
165
167
|
# @param field [String] name of the controlled property
|
166
168
|
# @return [Boolean] provided value is a present, active authority ID for the provided field
|
167
169
|
def active_id_for_authority?(value, field)
|
170
|
+
return false unless defined?(::Hyrax)
|
168
171
|
field_service = ('Hyrax::' + "#{field}_service".camelcase).constantize
|
169
172
|
active_authority_ids = field_service.new.active_elements.map { |ae| ae['id'] }
|
170
173
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'marcel'
|
2
3
|
|
3
4
|
module Bulkrax
|
4
5
|
module ImporterExporterBehavior
|
@@ -50,7 +51,14 @@ module Bulkrax
|
|
50
51
|
|
51
52
|
# Is this a zip file?
|
52
53
|
def zip?
|
53
|
-
parser_fields&.[]('import_file_path')
|
54
|
+
filename = parser_fields&.[]('import_file_path')
|
55
|
+
return false unless filename
|
56
|
+
return false unless File.file?(filename)
|
57
|
+
returning_value = false
|
58
|
+
File.open(filename) do |file|
|
59
|
+
returning_value = ::Marcel::MimeType.for(file).include?('application/zip')
|
60
|
+
end
|
61
|
+
returning_value
|
54
62
|
end
|
55
63
|
end
|
56
64
|
end
|
@@ -33,16 +33,21 @@ module Bulkrax
|
|
33
33
|
current_status&.created_at
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
36
|
+
def set_status_info(e = nil, current_run = nil)
|
37
|
+
runnable = current_run || last_run
|
37
38
|
if e.nil?
|
38
|
-
self.statuses.create!(status_message: 'Complete', runnable:
|
39
|
+
self.statuses.create!(status_message: 'Complete', runnable: runnable)
|
39
40
|
elsif e.is_a?(String)
|
40
|
-
self.statuses.create!(status_message: e, runnable:
|
41
|
+
self.statuses.create!(status_message: e, runnable: runnable)
|
41
42
|
else
|
42
|
-
self.statuses.create!(status_message: 'Failed', runnable:
|
43
|
+
self.statuses.create!(status_message: 'Failed', runnable: runnable, error_class: e.class.to_s, error_message: e.message, error_backtrace: e.backtrace)
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
47
|
+
alias status_info set_status_info
|
48
|
+
|
49
|
+
deprecation_deprecate status_info: "Favor Bulkrax::StatusInfo.set_status_info. We will be removing .status_info in Bulkrax v6.0.0"
|
50
|
+
|
46
51
|
# api compatible with previous error structure
|
47
52
|
def last_error
|
48
53
|
return unless current_status && current_status.error_class.present?
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'zip'
|
3
|
+
require 'marcel'
|
3
4
|
|
4
5
|
module Bulkrax
|
5
6
|
# An abstract class that establishes the API for Bulkrax's import and export parsing.
|
@@ -10,10 +11,11 @@ module Bulkrax
|
|
10
11
|
alias importer importerexporter
|
11
12
|
alias exporter importerexporter
|
12
13
|
delegate :only_updates, :limit, :current_run, :errors, :mapping,
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
:seen, :increment_counters, :parser_fields, :user, :keys_without_numbers,
|
15
|
+
:key_without_numbers, :status, :set_status_info, :status_info, :status_at,
|
16
|
+
:exporter_export_path, :exporter_export_zip_path, :importer_unzip_path, :validate_only,
|
17
|
+
:zip?, :file?,
|
18
|
+
to: :importerexporter
|
17
19
|
|
18
20
|
# @todo Convert to `class_attribute :parser_fiels, default: {}`
|
19
21
|
def self.parser_fields
|
@@ -275,10 +277,16 @@ module Bulkrax
|
|
275
277
|
|
276
278
|
# @return [Array<String>]
|
277
279
|
def required_elements
|
280
|
+
matched_elements = ((importerexporter.mapping.keys || []) & (Bulkrax.required_elements || []))
|
281
|
+
unless matched_elements.count == Bulkrax.required_elements.count
|
282
|
+
missing_elements = Bulkrax.required_elements - matched_elements
|
283
|
+
error_alert = "Missing mapping for at least one required element, missing mappings are: #{missing_elements.join(', ')}"
|
284
|
+
raise StandardError, error_alert
|
285
|
+
end
|
278
286
|
if Bulkrax.fill_in_blank_source_identifiers
|
279
|
-
|
287
|
+
Bulkrax.required_elements
|
280
288
|
else
|
281
|
-
[
|
289
|
+
Bulkrax.required_elements + [source_identifier]
|
282
290
|
end
|
283
291
|
end
|
284
292
|
|
@@ -351,16 +359,6 @@ module Bulkrax
|
|
351
359
|
end
|
352
360
|
end
|
353
361
|
|
354
|
-
# Is this a file?
|
355
|
-
def file?
|
356
|
-
parser_fields&.[]('import_file_path') && File.file?(parser_fields['import_file_path'])
|
357
|
-
end
|
358
|
-
|
359
|
-
# Is this a zip file?
|
360
|
-
def zip?
|
361
|
-
parser_fields&.[]('import_file_path') && MIME::Types.type_for(parser_fields['import_file_path']).include?('application/zip')
|
362
|
-
end
|
363
|
-
|
364
362
|
# Path for the import
|
365
363
|
# @return [String]
|
366
364
|
def import_file_path
|
@@ -11,7 +11,7 @@ module Bulkrax
|
|
11
11
|
def valid_import?
|
12
12
|
return true if import_fields.present?
|
13
13
|
rescue => e
|
14
|
-
|
14
|
+
set_status_info(e)
|
15
15
|
false
|
16
16
|
end
|
17
17
|
|
@@ -51,7 +51,7 @@ module Bulkrax
|
|
51
51
|
record_data = entry_class.data_for_entry(data_row, source_identifier, self)
|
52
52
|
next record_data if importerexporter.metadata_only?
|
53
53
|
|
54
|
-
record_data[:file] = bag.bag_files.join('|') if
|
54
|
+
record_data[:file] = bag.bag_files.join('|') if Bulkrax.curation_concerns.include? record_data[:model]&.constantize
|
55
55
|
record_data
|
56
56
|
end
|
57
57
|
else
|
@@ -82,19 +82,7 @@ module Bulkrax
|
|
82
82
|
end
|
83
83
|
importer.record_status
|
84
84
|
rescue StandardError => e
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
def total
|
89
|
-
@total = importer.parser_fields['total'] || 0 if importer?
|
90
|
-
|
91
|
-
@total = if exporter?
|
92
|
-
limit.nil? || limit.zero? ? current_record_ids.count : limit
|
93
|
-
end
|
94
|
-
|
95
|
-
return @total || 0
|
96
|
-
rescue StandardError
|
97
|
-
@total = 0
|
85
|
+
set_status_info(e)
|
98
86
|
end
|
99
87
|
|
100
88
|
# export methods
|
@@ -143,8 +131,8 @@ module Bulkrax
|
|
143
131
|
begin
|
144
132
|
bag.add_file(file_name, file.path) if bag.bag_files.select { |b| b.include?(file_name) }.blank?
|
145
133
|
rescue => e
|
146
|
-
entry.
|
147
|
-
|
134
|
+
entry.set_status_info(e)
|
135
|
+
set_status_info(e)
|
148
136
|
end
|
149
137
|
end
|
150
138
|
|
@@ -185,6 +173,7 @@ module Bulkrax
|
|
185
173
|
File.join(path, id)
|
186
174
|
end
|
187
175
|
|
176
|
+
# @todo(bjustice) - remove hyrax reference
|
188
177
|
def write_triples(folder_count, e)
|
189
178
|
sd = SolrDocument.find(e.identifier)
|
190
179
|
return if sd.nil?
|