bulkrax 5.0.0 → 5.2.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/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?
|