bulkrax 9.0.2 → 9.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/README.md +26 -0
- data/app/assets/javascripts/bulkrax/datatables.js +12 -0
- data/app/assets/javascripts/bulkrax/importers.js.erb +4 -1
- data/app/factories/bulkrax/object_factory.rb +36 -2
- data/app/factories/bulkrax/object_factory_interface.rb +26 -0
- data/app/factories/bulkrax/valkyrie_object_factory.rb +109 -27
- data/app/jobs/bulkrax/create_relationships_job.rb +123 -76
- data/app/jobs/bulkrax/delete_job.rb +11 -0
- data/app/jobs/bulkrax/importer_job.rb +1 -0
- data/app/matchers/bulkrax/application_matcher.rb +2 -1
- data/app/models/bulkrax/csv_entry.rb +41 -10
- data/app/models/bulkrax/importer.rb +9 -1
- data/app/models/bulkrax/status.rb +1 -1
- data/app/models/concerns/bulkrax/export_behavior.rb +28 -15
- data/app/models/concerns/bulkrax/file_set_entry_behavior.rb +13 -4
- data/app/models/concerns/bulkrax/importer_exporter_behavior.rb +1 -1
- data/app/parsers/bulkrax/application_parser.rb +22 -4
- data/app/parsers/bulkrax/csv_parser.rb +36 -6
- data/app/parsers/bulkrax/oai_dc_parser.rb +0 -2
- data/app/parsers/bulkrax/xml_parser.rb +1 -1
- data/app/services/bulkrax/factory_class_finder.rb +56 -15
- data/app/services/hyrax/custom_queries/find_by_source_identifier.rb +6 -11
- data/app/services/wings/custom_queries/find_by_source_identifier.rb +15 -6
- data/app/views/bulkrax/entries/show.html.erb +15 -9
- data/app/views/bulkrax/importers/_bagit_fields.html.erb +1 -1
- data/app/views/bulkrax/importers/_csv_fields.html.erb +1 -1
- data/app/views/bulkrax/importers/_oai_fields.html.erb +1 -1
- data/app/views/bulkrax/importers/_xml_fields.html.erb +1 -1
- data/app/views/bulkrax/importers/show.html.erb +4 -4
- data/app/views/bulkrax/shared/_entries_tab.html.erb +1 -1
- data/config/locales/bulkrax.en.yml +5 -3
- data/lib/bulkrax/engine.rb +1 -1
- data/lib/bulkrax/version.rb +1 -1
- data/lib/bulkrax.rb +6 -11
- data/lib/generators/bulkrax/templates/bin/importer +1 -5
- metadata +8 -3
- data/app/factories/bulkrax/valkyrize-hyku.code-workspace +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb04fb1689c90c0cb7b96d0fbab95f371e4e4718509638812e62b0bffa1cc9e0
|
4
|
+
data.tar.gz: b3df0b413a151f7c49c6a39e07a705ec0e8c87d181a66de2a0ae75d567297f08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89a766b0116729946c1ffba1252362fff3f29fd57ab774aebab39fa5b6f2b207a511de3da801b47bce7352a8ef2ca2e3811f306ca0f701ef592acd2a66103f34
|
7
|
+
data.tar.gz: b559f0441c55c23a64568d95d1bcb533dc5b11cf12c9ad8aaed3ab852a0f9ea88b1e9140968b6272af54d05d2edae3ff3dadb5433e07fa6c55d90e615f42447d
|
data/README.md
CHANGED
@@ -208,6 +208,32 @@ We encourage everyone to help improve this project. Bug reports and pull reques
|
|
208
208
|
|
209
209
|
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://contributor-covenant.org) code of conduct.
|
210
210
|
|
211
|
+
### Running tests
|
212
|
+
- The tests use sqlite as the database, so no extra dependencies are required.
|
213
|
+
- Ensure you are using a supported version of Ruby (`ruby -v` should be greater or equal to 2.7)
|
214
|
+
- If you are using a ruby version earlier than 3.0.0, install bundler explicitly (newer versions of ruby will automatically install an appropriate bundler version)
|
215
|
+
```bash
|
216
|
+
/path/to/ruby/install/bin/gem install bundler -v '~> 2.4.0'
|
217
|
+
```
|
218
|
+
- Decide on your version of Hyrax to test against and export it to your environment, then bundle install. The Hyrax version should be greater or equal to 2.3.
|
219
|
+
```bash
|
220
|
+
export HYRAX_VERSION="~> 4.0.0"
|
221
|
+
bundle install
|
222
|
+
```
|
223
|
+
- Run the test migrations
|
224
|
+
```bash
|
225
|
+
bundle exec bin/rails db:migrate RAILS_ENV=test
|
226
|
+
```
|
227
|
+
- Run the tests
|
228
|
+
```bash
|
229
|
+
bundle exec rspec
|
230
|
+
```
|
231
|
+
|
232
|
+
- Run the style checker / linter
|
233
|
+
```bash
|
234
|
+
bundle exec rubocop
|
235
|
+
```
|
236
|
+
|
211
237
|
## Questions
|
212
238
|
Questions can be sent to support@notch8.com. Please make sure to include "Bulkrax" in the subject line of your email.
|
213
239
|
|
@@ -3,6 +3,10 @@ Blacklight.onLoad(function() {
|
|
3
3
|
$('#importer-show-table').DataTable( {
|
4
4
|
'processing': true,
|
5
5
|
'serverSide': true,
|
6
|
+
'width': '100%',
|
7
|
+
'autoWidth': false,
|
8
|
+
'scrollX': true,
|
9
|
+
'scrollCollapse': true,
|
6
10
|
"ajax": window.location.href.replace(/(\/(importers|exporters)\/\d+)/, "$1/entry_table.json"),
|
7
11
|
"pageLength": 30,
|
8
12
|
"lengthMenu": [[30, 100, 200], [30, 100, 200]],
|
@@ -15,6 +19,14 @@ Blacklight.onLoad(function() {
|
|
15
19
|
{ "data": "errors", "orderable": false },
|
16
20
|
{ "data": "actions", "orderable": false }
|
17
21
|
],
|
22
|
+
drawCallback: function() {
|
23
|
+
// Remove the inline styles that DataTables adds to the scrollHeadInner and table elements
|
24
|
+
// it's not perfect but better than the style being applied
|
25
|
+
setTimeout(function() {
|
26
|
+
$('.dataTables_scrollHeadInner').removeAttr('style');
|
27
|
+
$('.table.table-striped.dataTable.no-footer').removeAttr('style');
|
28
|
+
}, 100);
|
29
|
+
},
|
18
30
|
initComplete: function () {
|
19
31
|
// Add entry class filter
|
20
32
|
entrySelect.bind(this)()
|
@@ -15,7 +15,10 @@ function prepBulkrax(event) {
|
|
15
15
|
// Initialize the uploader only if hyraxUploader is defined
|
16
16
|
if (typeof $.fn.hyraxUploader === 'function') {
|
17
17
|
// Initialize the uploader
|
18
|
-
$('.fileupload-bulkrax').hyraxUploader({
|
18
|
+
$('.fileupload-bulkrax').hyraxUploader({
|
19
|
+
maxNumberOfFiles: 1,
|
20
|
+
maxFileSize: <%= (defined?(Hyrax) && Hyrax.config.uploader[:maxFileSize]) || 524288000 %>
|
21
|
+
});
|
19
22
|
|
20
23
|
// Function to toggle 'required' attribute based on uploaded files
|
21
24
|
function toggleRequiredAttribute() {
|
@@ -28,6 +28,40 @@ module Bulkrax
|
|
28
28
|
resource.file_sets.each(&:update_index) if resource.respond_to?(:file_sets)
|
29
29
|
end
|
30
30
|
|
31
|
+
##
|
32
|
+
# @return [String] the name of the model class for the given resource/object.
|
33
|
+
def self.model_name(resource:)
|
34
|
+
resource.has_model.first
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# A thumbnail is linked to a work rather than the file set itself.
|
39
|
+
# @return [File or FileMetadata] the thumbnail file for the given resource
|
40
|
+
def self.thumbnail_for(resource:)
|
41
|
+
return nil unless resource.respond_to?(:thumbnail)
|
42
|
+
return resource.thumbnail if resource.thumbnail.present?
|
43
|
+
return nil unless resource.respond_to?(:parent) && resource.parent.present?
|
44
|
+
return nil unless resource.parent.respond_to?(:thumbnail)
|
45
|
+
resource.parent.thumbnail
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# @input [Fileset]
|
50
|
+
# @return [File] the original file.
|
51
|
+
def self.original_file(fileset:)
|
52
|
+
fileset.try(:original_file)
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# #input [Fileset or FileMetadata]
|
57
|
+
# @return [String] the file name for the given fileset
|
58
|
+
def self.filename_for(fileset:)
|
59
|
+
file = original_file(fileset: fileset)
|
60
|
+
file.file_name.first
|
61
|
+
rescue NoMethodError
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
31
65
|
##
|
32
66
|
# @see Bulkrax::ObjectFactoryInterface
|
33
67
|
def self.export_properties
|
@@ -91,7 +125,7 @@ module Bulkrax
|
|
91
125
|
# @note HEY WE'RE USING THIS FOR A WINGS CUSTOM QUERY. BE CAREFUL WITH
|
92
126
|
# REMOVING IT.
|
93
127
|
#
|
94
|
-
# @see # {Wings::CustomQueries::FindBySourceIdentifier#
|
128
|
+
# @see # {Wings::CustomQueries::FindBySourceIdentifier#find_by_property_value}
|
95
129
|
def self.search_by_property(value:, klass:, field: nil, search_field: nil, name_field: nil, verify_property: false)
|
96
130
|
return nil unless klass.respond_to?(:where)
|
97
131
|
# We're not going to try to match nil nor "".
|
@@ -166,7 +200,7 @@ module Bulkrax
|
|
166
200
|
|
167
201
|
def delete(_user)
|
168
202
|
obj = find
|
169
|
-
|
203
|
+
raise ObjectFactoryInterface::ObjectNotFoundError, "Object not found to delete" unless obj
|
170
204
|
|
171
205
|
obj.delete(eradicate: true)
|
172
206
|
end
|
@@ -209,6 +209,32 @@ module Bulkrax
|
|
209
209
|
end
|
210
210
|
# rubocop:enable Metrics/ParameterLists
|
211
211
|
|
212
|
+
##
|
213
|
+
# @return [String] the name of the model class for the given resource/object.
|
214
|
+
def self.model_name(resource:)
|
215
|
+
raise NotImplementedError, "#{self}.#{__method__}"
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# @return [File or FileMetadata] the thumbnail file for the given resource
|
220
|
+
def self.thumbnail_for(resource:)
|
221
|
+
raise NotImplementedError, "#{self}.#{__method__}"
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# @input [Fileset or FileMetadata]
|
226
|
+
# @return [File or FileMetadata] the original file
|
227
|
+
def self.original_file(fileset:)
|
228
|
+
raise NotImplementedError, "#{self}.#{__method__}"
|
229
|
+
end
|
230
|
+
|
231
|
+
##
|
232
|
+
# #input [Fileset or FileMetadata]
|
233
|
+
# @return [String] the file name for the given fileset
|
234
|
+
def self.filename_for(fileset:)
|
235
|
+
raise NotImplementedError, "#{self}.#{__method__}"
|
236
|
+
end
|
237
|
+
|
212
238
|
##
|
213
239
|
# @api private
|
214
240
|
#
|
@@ -38,12 +38,54 @@ module Bulkrax
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
# Customized create method for Valkyrie so that @object gets set
|
42
|
+
def create
|
43
|
+
attrs = transform_attributes
|
44
|
+
@object = klass.new
|
45
|
+
conditionally_set_reindex_extent
|
46
|
+
run_callbacks :save do
|
47
|
+
run_callbacks :create do
|
48
|
+
@object = if klass == Bulkrax.collection_model_class
|
49
|
+
create_collection(attrs)
|
50
|
+
elsif klass == Bulkrax.file_model_class
|
51
|
+
create_file_set(attrs)
|
52
|
+
else
|
53
|
+
create_work(attrs)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
apply_depositor_metadata
|
59
|
+
log_created(@object)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Customized update method for Valkyrie so that @object gets set
|
63
|
+
def update
|
64
|
+
raise "Object doesn't exist" unless object
|
65
|
+
conditionally_destroy_existing_files
|
66
|
+
|
67
|
+
attrs = transform_attributes(update: true)
|
68
|
+
run_callbacks :save do
|
69
|
+
@object = if klass == Bulkrax.collection_model_class
|
70
|
+
update_collection(attrs)
|
71
|
+
elsif klass == Bulkrax.file_model_class
|
72
|
+
update_file_set(attrs)
|
73
|
+
else
|
74
|
+
update_work(attrs)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
apply_depositor_metadata
|
78
|
+
log_updated(@object)
|
79
|
+
end
|
80
|
+
|
41
81
|
# TODO: the following module needs revisiting for Valkyrie work.
|
42
82
|
# proposal is to create Bulkrax::ValkyrieFileFactory.
|
43
83
|
include Bulkrax::FileFactory
|
44
84
|
|
45
85
|
self.file_set_factory_inner_workings_class = Bulkrax::ValkyrieObjectFactory::FileFactoryInnerWorkings
|
46
86
|
|
87
|
+
delegate :transactions, to: :class
|
88
|
+
|
47
89
|
##
|
48
90
|
# When you want a different set of transactions you can change the
|
49
91
|
# container.
|
@@ -55,24 +97,25 @@ module Bulkrax
|
|
55
97
|
@transactions || Hyrax::Transactions::Container
|
56
98
|
end
|
57
99
|
|
58
|
-
def transactions
|
59
|
-
self.class.transactions
|
60
|
-
end
|
61
|
-
|
62
100
|
##
|
63
101
|
# @!group Class Method Interface
|
64
102
|
|
65
103
|
##
|
66
|
-
#
|
67
|
-
#
|
104
|
+
# When adding a child to a parent work, we save the parent.
|
105
|
+
# Locking appears inconsistent, so we are finding the parent and
|
106
|
+
# saving it with each child, but waiting until the end to reindex.
|
107
|
+
# To do this we are bypassing the save! method defined below
|
68
108
|
def self.add_child_to_parent_work(parent:, child:)
|
109
|
+
parent = self.find(parent.id)
|
69
110
|
return true if parent.member_ids.include?(child.id)
|
70
|
-
|
71
111
|
parent.member_ids << child.id
|
72
|
-
|
112
|
+
Hyrax.persister.save(resource: parent)
|
73
113
|
end
|
74
114
|
|
115
|
+
##
|
116
|
+
# The resource added to a collection can be either a work or another collection.
|
75
117
|
def self.add_resource_to_collection(collection:, resource:, user:)
|
118
|
+
resource = self.find(resource.id)
|
76
119
|
resource.member_of_collection_ids << collection.id
|
77
120
|
save!(resource: resource, user: user)
|
78
121
|
end
|
@@ -112,7 +155,7 @@ module Bulkrax
|
|
112
155
|
Hyrax.query_service.find_by(id: id)
|
113
156
|
# Because Hyrax is not a hard dependency, we need to transform the Hyrax exception into a
|
114
157
|
# common exception so that callers can handle a generalize exception.
|
115
|
-
rescue Hyrax::ObjectNotFoundError => e
|
158
|
+
rescue Hyrax::ObjectNotFoundError, Valkyrie::Persistence::ObjectNotFoundError => e
|
116
159
|
raise ObjectFactoryInterface::ObjectNotFoundError, e.message
|
117
160
|
end
|
118
161
|
|
@@ -127,6 +170,8 @@ module Bulkrax
|
|
127
170
|
end
|
128
171
|
|
129
172
|
def self.publish(event:, **kwargs)
|
173
|
+
# It's a bit unclear what this should be if we can't rely on Hyrax.
|
174
|
+
raise NotImplementedError, "#{self}.#{__method__}" unless defined?(Hyrax)
|
130
175
|
Hyrax.publisher.publish(event, **kwargs)
|
131
176
|
end
|
132
177
|
|
@@ -139,19 +184,19 @@ module Bulkrax
|
|
139
184
|
end
|
140
185
|
|
141
186
|
def self.save!(resource:, user:)
|
142
|
-
if
|
143
|
-
resource.save!
|
144
|
-
else
|
187
|
+
if defined?(Hyrax)
|
145
188
|
result = Hyrax.persister.save(resource: resource)
|
146
189
|
raise Valkyrie::Persistence::ObjectNotFoundError unless result
|
147
190
|
Hyrax.index_adapter.save(resource: result)
|
148
191
|
if result.collection?
|
149
|
-
publish('collection.metadata.updated', collection: result, user: user)
|
192
|
+
self.publish(event: 'collection.metadata.updated', collection: result, user: user)
|
150
193
|
else
|
151
|
-
publish('object.metadata.updated', object: result, user: user)
|
194
|
+
self.publish(event: 'object.metadata.updated', object: result, user: user)
|
152
195
|
end
|
153
|
-
|
196
|
+
else
|
197
|
+
resource.save!
|
154
198
|
end
|
199
|
+
resource
|
155
200
|
end
|
156
201
|
|
157
202
|
def self.update_index(resources:)
|
@@ -165,6 +210,46 @@ module Bulkrax
|
|
165
210
|
update_index(resources: file_sets)
|
166
211
|
end
|
167
212
|
|
213
|
+
##
|
214
|
+
# If we always want the valkyrized resource name, even for unmigrated objects, we can
|
215
|
+
# simply use resource.model_name.name. At this point, we are differentiating
|
216
|
+
# to help identify items which have been migrated to Valkyrie vs those which have not.
|
217
|
+
#
|
218
|
+
# @return [String] the name of the model class for the given resource/object.
|
219
|
+
def self.model_name(resource:)
|
220
|
+
resource.class.to_s
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# @return [File or FileMetadata] the thumbnail file for the given resource
|
225
|
+
def self.thumbnail_for(resource:)
|
226
|
+
# recursive call to parent if resource is a fileset - we want the work's thumbnail
|
227
|
+
return thumbnail_for(resource: resource&.parent) if resource.is_a?(Bulkrax.file_model_class)
|
228
|
+
|
229
|
+
return nil unless resource.respond_to?(:thumbnail_id) && resource.thumbnail_id.present?
|
230
|
+
Bulkrax.object_factory.find(resource.thumbnail_id.to_s)
|
231
|
+
rescue Bulkrax::ObjectFactoryInterface::ObjectNotFoundError
|
232
|
+
nil
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# @input [Fileset or FileMetadata]
|
237
|
+
# @return [FileMetadata] the original file
|
238
|
+
def self.original_file(fileset:)
|
239
|
+
fileset.try(:original_file) || fileset
|
240
|
+
end
|
241
|
+
|
242
|
+
##
|
243
|
+
# #input [Fileset or FileMetadata]
|
244
|
+
# @return [String] the file name for the given fileset
|
245
|
+
def self.filename_for(fileset:)
|
246
|
+
file = original_file(fileset: fileset)
|
247
|
+
return nil unless file
|
248
|
+
file.original_filename
|
249
|
+
rescue NoMethodError
|
250
|
+
nil
|
251
|
+
end
|
252
|
+
|
168
253
|
##
|
169
254
|
# @param value [String]
|
170
255
|
# @param klass [Class, #where]
|
@@ -176,13 +261,12 @@ module Bulkrax
|
|
176
261
|
# @return [Valkyrie::Resource] when a match is found, an instance of given
|
177
262
|
# :klass
|
178
263
|
# rubocop:disable Metrics/ParameterLists
|
179
|
-
def self.search_by_property(value:,
|
264
|
+
def self.search_by_property(value:, field: nil, name_field: nil, search_field:, **)
|
180
265
|
name_field ||= field
|
181
266
|
raise "Expected named_field or field got nil" if name_field.blank?
|
182
267
|
return if value.blank?
|
183
|
-
|
184
268
|
# Return nil or a single object.
|
185
|
-
Hyrax.query_service.
|
269
|
+
Hyrax.query_service.custom_queries.find_by_property_value(property: name_field, value: value, search_field: search_field)
|
186
270
|
end
|
187
271
|
# rubocop:enable Metrics/ParameterLists
|
188
272
|
|
@@ -208,11 +292,11 @@ module Bulkrax
|
|
208
292
|
|
209
293
|
def delete(user)
|
210
294
|
obj = find
|
211
|
-
|
295
|
+
raise ObjectFactoryInterface::ObjectNotFoundError, "Object not found to delete" unless obj
|
212
296
|
|
213
297
|
Hyrax.persister.delete(resource: obj)
|
214
298
|
Hyrax.index_adapter.delete(resource: obj)
|
215
|
-
|
299
|
+
Hyrax.publisher.publish('object.deleted', object: obj, user: user)
|
216
300
|
end
|
217
301
|
|
218
302
|
def run!
|
@@ -231,7 +315,7 @@ module Bulkrax
|
|
231
315
|
|
232
316
|
@object.depositor = @user.email
|
233
317
|
object = Hyrax.persister.save(resource: @object)
|
234
|
-
|
318
|
+
Hyrax.publisher.publish("object.metadata.updated", object: object, user: @user)
|
235
319
|
object
|
236
320
|
end
|
237
321
|
|
@@ -248,6 +332,7 @@ module Bulkrax
|
|
248
332
|
|
249
333
|
def create_file_set(attrs)
|
250
334
|
# TODO: Make it work for Valkyrie
|
335
|
+
raise NotImplementedError, __method__.to_s
|
251
336
|
end
|
252
337
|
|
253
338
|
def create_work(attrs)
|
@@ -337,7 +422,7 @@ module Bulkrax
|
|
337
422
|
end
|
338
423
|
|
339
424
|
def find_by_id
|
340
|
-
|
425
|
+
self.class.find(attributes[:id]) if attributes.key? :id
|
341
426
|
end
|
342
427
|
|
343
428
|
##
|
@@ -433,7 +518,6 @@ module Bulkrax
|
|
433
518
|
remote_files.map do |r|
|
434
519
|
file_path = download_file(r["url"])
|
435
520
|
next unless file_path
|
436
|
-
|
437
521
|
create_uploaded_file(file_path, r["file_name"])
|
438
522
|
end.compact
|
439
523
|
end
|
@@ -449,8 +533,7 @@ module Bulkrax
|
|
449
533
|
file.rewind
|
450
534
|
file.path
|
451
535
|
rescue => e
|
452
|
-
|
453
|
-
nil
|
536
|
+
raise "Failed to download file from #{url}: #{e.message}"
|
454
537
|
end
|
455
538
|
end
|
456
539
|
|
@@ -460,8 +543,7 @@ module Bulkrax
|
|
460
543
|
file.close
|
461
544
|
uploaded_file
|
462
545
|
rescue => e
|
463
|
-
|
464
|
-
nil
|
546
|
+
raise "Failed to create Hyrax::UploadedFile for #{file_name}: #{e.message}"
|
465
547
|
end
|
466
548
|
|
467
549
|
# @Override Destroy existing files with Hyrax::Transactions
|