bulkrax 1.0.1 → 2.0.2

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/controllers/bulkrax/exporters_controller.rb +12 -4
  4. data/app/controllers/bulkrax/importers_controller.rb +22 -17
  5. data/app/factories/bulkrax/object_factory.rb +44 -61
  6. data/app/jobs/bulkrax/create_relationships_job.rb +187 -0
  7. data/app/jobs/bulkrax/delete_work_job.rb +6 -2
  8. data/app/jobs/bulkrax/export_work_job.rb +3 -1
  9. data/app/jobs/bulkrax/exporter_job.rb +1 -0
  10. data/app/jobs/bulkrax/{import_work_collection_job.rb → import_collection_job.rb} +2 -2
  11. data/app/jobs/bulkrax/importer_job.rb +16 -1
  12. data/app/matchers/bulkrax/application_matcher.rb +9 -6
  13. data/app/models/bulkrax/csv_collection_entry.rb +8 -6
  14. data/app/models/bulkrax/csv_entry.rb +139 -45
  15. data/app/models/bulkrax/entry.rb +19 -8
  16. data/app/models/bulkrax/exporter.rb +12 -5
  17. data/app/models/bulkrax/importer.rb +22 -5
  18. data/app/models/bulkrax/oai_entry.rb +5 -1
  19. data/app/models/bulkrax/rdf_entry.rb +16 -7
  20. data/app/models/bulkrax/xml_entry.rb +4 -0
  21. data/app/models/concerns/bulkrax/export_behavior.rb +2 -2
  22. data/app/models/concerns/bulkrax/file_factory.rb +2 -1
  23. data/app/models/concerns/bulkrax/has_matchers.rb +59 -16
  24. data/app/models/concerns/bulkrax/import_behavior.rb +39 -5
  25. data/app/models/concerns/bulkrax/importer_exporter_behavior.rb +19 -0
  26. data/app/models/concerns/bulkrax/status_info.rb +4 -4
  27. data/app/parsers/bulkrax/application_parser.rb +59 -84
  28. data/app/parsers/bulkrax/bagit_parser.rb +12 -3
  29. data/app/parsers/bulkrax/csv_parser.rb +137 -63
  30. data/app/parsers/bulkrax/oai_dc_parser.rb +5 -2
  31. data/app/parsers/bulkrax/xml_parser.rb +5 -0
  32. data/app/views/bulkrax/exporters/_form.html.erb +1 -1
  33. data/app/views/bulkrax/exporters/show.html.erb +13 -1
  34. data/app/views/bulkrax/importers/_edit_form_buttons.html.erb +45 -14
  35. data/app/views/bulkrax/importers/edit.html.erb +2 -0
  36. data/app/views/bulkrax/importers/index.html.erb +15 -17
  37. data/app/views/bulkrax/importers/show.html.erb +6 -2
  38. data/config/locales/bulkrax.en.yml +1 -0
  39. data/db/migrate/20190731114016_change_importer_and_exporter_to_polymorphic.rb +5 -1
  40. data/db/migrate/20211004170708_change_bulkrax_statuses_error_message_column_type_to_text.rb +5 -0
  41. data/db/migrate/20211203195233_rename_children_counters_to_relationships.rb +6 -0
  42. data/lib/bulkrax/engine.rb +1 -1
  43. data/lib/bulkrax/version.rb +1 -1
  44. data/lib/bulkrax.rb +9 -17
  45. data/lib/generators/bulkrax/templates/bin/importer +17 -11
  46. data/lib/generators/bulkrax/templates/config/bulkrax_api.yml +3 -1
  47. data/lib/generators/bulkrax/templates/config/initializers/bulkrax.rb +7 -12
  48. metadata +12 -6
  49. data/app/jobs/bulkrax/child_relationships_job.rb +0 -128
@@ -2,31 +2,47 @@
2
2
 
3
3
  require 'csv'
4
4
  module Bulkrax
5
- class CsvParser < ApplicationParser
5
+ class CsvParser < ApplicationParser # rubocop:disable Metrics/ClassLength
6
6
  include ErroredEntries
7
7
  def self.export_supported?
8
8
  true
9
9
  end
10
10
 
11
- def initialize(importerexporter)
12
- @importerexporter = importerexporter
11
+ def records(_opts = {})
12
+ file_for_import = only_updates ? parser_fields['partial_import_file_path'] : import_file_path
13
+ # data for entry does not need source_identifier for csv, because csvs are read sequentially and mapped after raw data is read.
14
+ csv_data = entry_class.read_data(file_for_import)
15
+ importer.parser_fields['total'] = csv_data.count
16
+ importer.save
17
+ @records ||= csv_data.map { |record_data| entry_class.data_for_entry(record_data, nil) }
13
18
  end
14
19
 
15
20
  def collections
16
- # does the CSV contain a collection column?
17
- return [] unless import_fields.include?(:collection)
21
+ ActiveSupport::Deprecation.warn(
22
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
23
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
24
+ )
18
25
  # retrieve a list of unique collections
19
- records.map { |r| r[:collection].split(/\s*[;|]\s*/) if r[:collection].present? }.flatten.compact.uniq
26
+ records.map do |r|
27
+ collections = []
28
+ r[collection_field_mapping].split(/\s*[;|]\s*/).each { |title| collections << { title: title, from_collection_field_mapping: true } } if r[collection_field_mapping].present?
29
+ model_field_mappings.each do |model_mapping|
30
+ collections << r if r[model_mapping.to_sym]&.downcase == 'collection'
31
+ end
32
+ collections
33
+ end.flatten.compact.uniq
20
34
  end
21
35
 
22
36
  def collections_total
23
37
  collections.size
24
38
  end
25
39
 
26
- def records(_opts = {})
27
- file_for_import = only_updates ? parser_fields['partial_import_file_path'] : import_file_path
28
- # data for entry does not need source_identifier for csv, because csvs are read sequentially and mapped after raw data is read.
29
- @records ||= entry_class.read_data(file_for_import).map { |record_data| entry_class.data_for_entry(record_data, nil) }
40
+ def works
41
+ records - collections
42
+ end
43
+
44
+ def works_total
45
+ works.size
30
46
  end
31
47
 
32
48
  # We could use CsvEntry#fields_from_data(data) but that would mean re-reading the data
@@ -44,8 +60,9 @@ module Bulkrax
44
60
  end
45
61
 
46
62
  def valid_import?
47
- error_alert = "Missing at least one required element, missing element(s) are: #{missing_elements(import_fields).join(', ')}"
48
- raise StandardError, error_alert unless required_elements?(import_fields)
63
+ import_strings = keys_without_numbers(import_fields.map(&:to_s))
64
+ error_alert = "Missing at least one required element, missing element(s) are: #{missing_elements(import_strings).join(', ')}"
65
+ raise StandardError, error_alert unless required_elements?(import_strings)
49
66
 
50
67
  file_paths.is_a?(Array)
51
68
  rescue StandardError => e
@@ -56,26 +73,46 @@ module Bulkrax
56
73
  def create_collections
57
74
  collections.each_with_index do |collection, index|
58
75
  next if collection.blank?
59
- metadata = {
60
- title: [collection],
61
- work_identifier => [collection],
62
- visibility: 'open',
63
- collection_type_gid: Hyrax::CollectionType.find_or_create_default_collection_type.gid
64
- }
65
- new_entry = find_or_create_entry(collection_entry_class, collection, 'Bulkrax::Importer', metadata)
66
- ImportWorkCollectionJob.perform_now(new_entry.id, current_run.id)
76
+ break if records.find_index(collection).present? && limit_reached?(limit, records.find_index(collection))
77
+ ActiveSupport::Deprecation.warn(
78
+ 'Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.' \
79
+ ' Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.'
80
+ )
81
+
82
+ ## BEGIN
83
+ # Add required metadata to collections being imported using the collection_field_mapping, which only have a :title
84
+ # TODO: Remove once collection_field_mapping is removed
85
+ metadata = if collection.delete(:from_collection_field_mapping)
86
+ uci = unique_collection_identifier(collection)
87
+ {
88
+ title: [collection[:title]],
89
+ work_identifier => uci,
90
+ source_identifier => uci,
91
+ visibility: 'open',
92
+ collection_type_gid: ::Hyrax::CollectionType.find_or_create_default_collection_type.gid
93
+ }
94
+ end
95
+ collection_hash = metadata.presence || collection
96
+ ## END
97
+
98
+ new_entry = find_or_create_entry(collection_entry_class, collection_hash[source_identifier], 'Bulkrax::Importer', collection_hash)
99
+ # TODO: add support for :delete option
100
+ ImportCollectionJob.perform_now(new_entry.id, current_run.id)
67
101
  increment_counters(index, true)
68
102
  end
103
+ importer.record_status
104
+ rescue StandardError => e
105
+ status_info(e)
69
106
  end
70
107
 
71
108
  def create_works
72
- records.each_with_index do |record, index|
73
- next unless record_has_source_identifier(record, index)
74
- break if limit_reached?(limit, index)
109
+ works.each_with_index do |work, index|
110
+ next unless record_has_source_identifier(work, records.find_index(work))
111
+ break if limit_reached?(limit, records.find_index(work))
75
112
 
76
- seen[record[source_identifier]] = true
77
- new_entry = find_or_create_entry(entry_class, record[source_identifier], 'Bulkrax::Importer', record.to_h.compact)
78
- if record[:delete].present?
113
+ seen[work[source_identifier]] = true
114
+ new_entry = find_or_create_entry(entry_class, work[source_identifier], 'Bulkrax::Importer', work.to_h)
115
+ if work[:delete].present?
79
116
  DeleteWorkJob.send(perform_method, new_entry, current_run)
80
117
  else
81
118
  ImportWorkJob.send(perform_method, new_entry.id, current_run.id)
@@ -99,10 +136,6 @@ module Bulkrax
99
136
  path
100
137
  end
101
138
 
102
- def create_parent_child_relationships
103
- super
104
- end
105
-
106
139
  def extra_filters
107
140
  output = ""
108
141
  if importerexporter.start_date.present?
@@ -117,6 +150,8 @@ module Bulkrax
117
150
 
118
151
  def current_work_ids
119
152
  case importerexporter.export_from
153
+ when 'all'
154
+ ActiveFedora::SolrService.query("has_model_ssim:(#{Hyrax.config.curation_concerns.join(' OR ')}) #{extra_filters}", rows: 2_147_483_647).map(&:id)
120
155
  when 'collection'
121
156
  ActiveFedora::SolrService.query("member_of_collection_ids_ssim:#{importerexporter.export_source + extra_filters}", rows: 2_000_000_000).map(&:id)
122
157
  when 'worktype'
@@ -126,9 +161,16 @@ module Bulkrax
126
161
  complete_statuses = Bulkrax::Status.latest_by_statusable
127
162
  .includes(:statusable)
128
163
  .where('bulkrax_statuses.statusable_id IN (?) AND bulkrax_statuses.statusable_type = ? AND status_message = ?', entry_ids, 'Bulkrax::Entry', 'Complete')
129
- complete_entry_identifiers = complete_statuses.map { |s| s.statusable&.identifier }
130
164
 
131
- ActiveFedora::SolrService.query("#{work_identifier}_tesim:(#{complete_entry_identifiers.join(' OR ')})#{extra_filters}", rows: 2_000_000_000).map(&:id)
165
+ complete_entry_identifiers = complete_statuses.map { |s| s.statusable&.identifier&.gsub(':', '\:') }
166
+ extra_filters = extra_filters.presence || '*:*'
167
+
168
+ ActiveFedora::SolrService.get(
169
+ extra_filters.to_s,
170
+ fq: "#{work_identifier}_sim:(#{complete_entry_identifiers.join(' OR ')})",
171
+ fl: 'id',
172
+ rows: 2_000_000_000
173
+ )['response']['docs'].map { |obj| obj['id'] }
132
174
  end
133
175
  end
134
176
 
@@ -136,12 +178,18 @@ module Bulkrax
136
178
  current_work_ids.each_with_index do |wid, index|
137
179
  break if limit_reached?(limit, index)
138
180
  new_entry = find_or_create_entry(entry_class, wid, 'Bulkrax::Exporter')
139
- Bulkrax::ExportWorkJob.perform_now(new_entry.id, current_run.id)
181
+ begin
182
+ entry = Bulkrax::ExportWorkJob.perform_now(new_entry.id, current_run.id)
183
+ rescue => e
184
+ Rails.logger.info("#{e.message} was detected during export")
185
+ end
186
+ self.headers |= entry.parsed_metadata.keys if entry
140
187
  end
141
188
  end
142
189
  alias create_from_collection create_new_entries
143
190
  alias create_from_importer create_new_entries
144
191
  alias create_from_worktype create_new_entries
192
+ alias create_from_all create_new_entries
145
193
 
146
194
  def entry_class
147
195
  CsvEntry
@@ -154,19 +202,11 @@ module Bulkrax
154
202
  # See https://stackoverflow.com/questions/2650517/count-the-number-of-lines-in-a-file-without-reading-entire-file-into-memory
155
203
  # Changed to grep as wc -l counts blank lines, and ignores the final unescaped line (which may or may not contain data)
156
204
  def total
157
- if importer?
158
- return @total if @total&.positive?
159
- # windows enocded
160
- @total = `grep -c ^M #{real_import_file_path}`.to_i - 1
161
- # unix encoded
162
- @total = `grep -vc ^$ #{real_import_file_path}`.to_i - 1 if @total < 1
163
- elsif exporter?
164
- @total = importerexporter.entries.count
165
- else
166
- @total = 0
167
- end
168
- return @total
169
- rescue StandardErrorr
205
+ @total = importer.parser_fields['total'] || 0 if importer?
206
+ @total = importerexporter.entries.count if exporter?
207
+
208
+ return @total || 0
209
+ rescue StandardError
170
210
  @total = 0
171
211
  end
172
212
 
@@ -201,32 +241,58 @@ module Bulkrax
201
241
  end
202
242
  end
203
243
 
204
- def key_allowed(key)
205
- !Bulkrax.reserved_properties.include?(key) &&
206
- new_entry(entry_class, 'Bulkrax::Exporter').field_supported?(key) &&
244
+ def export_key_allowed(key)
245
+ new_entry(entry_class, 'Bulkrax::Exporter').field_supported?(key) &&
207
246
  key != source_identifier.to_s
208
247
  end
209
248
 
210
249
  # All possible column names
211
250
  def export_headers
212
- headers = ['id']
213
- headers << source_identifier.to_s
214
- headers << 'model'
215
- headers << 'collections'
216
- importerexporter.mapping.each_key { |key| headers << key if key_allowed(key) }
217
- headers << 'file'
251
+ headers = sort_headers(self.headers)
252
+
253
+ # we don't want access_control_id exported and we want file at the end
254
+ headers.delete('access_control_id') if headers.include?('access_control_id')
255
+
256
+ # add the headers below at the beginning or end to maintain the preexisting export behavior
257
+ headers.prepend('model')
258
+ headers.prepend(source_identifier.to_s)
259
+ headers.prepend('id')
260
+
218
261
  headers.uniq
219
262
  end
220
263
 
264
+ def object_names
265
+ return @object_names if @object_names
266
+
267
+ @object_names = mapping.values.map { |value| value['object'] }
268
+ @object_names.uniq!.delete(nil)
269
+
270
+ @object_names
271
+ end
272
+
273
+ def sort_headers(headers)
274
+ # converting headers like creator_name_1 to creator_1_name so they get sorted by numerical order
275
+ # while keeping objects grouped together
276
+ headers.sort_by do |item|
277
+ number = item.match(/\d+/)&.[](0) || 0.to_s
278
+ sort_number = number.rjust(4, "0")
279
+ object_prefix = object_names.detect { |o| item.match(/^#{o}/) } || item
280
+ remainder = item.gsub(/^#{object_prefix}_/, '').gsub(/_#{number}/, '')
281
+ "#{object_prefix}_#{sort_number}_#{remainder}"
282
+ end
283
+ end
284
+
221
285
  # in the parser as it is specific to the format
222
286
  def setup_export_file
223
- File.join(importerexporter.exporter_export_path, 'export.csv')
287
+ File.join(importerexporter.exporter_export_path, "export_#{importerexporter.export_source}_from_#{importerexporter.export_from}.csv")
224
288
  end
225
289
 
226
290
  # Retrieve file paths for [:file] mapping in records
227
291
  # and check all listed files exist.
228
292
  def file_paths
229
293
  raise StandardError, 'No records were found' if records.blank?
294
+ return [] if importerexporter.metadata_only?
295
+
230
296
  @file_paths ||= records.map do |r|
231
297
  file_mapping = Bulkrax.field_mappings.dig(self.class.to_s, 'file', :from)&.first&.to_sym || :file
232
298
  next if r[file_mapping].blank?
@@ -245,23 +311,31 @@ module Bulkrax
245
311
  # Retrieve the path where we expect to find the files
246
312
  def path_to_files
247
313
  @path_to_files ||= File.join(
248
- File.file?(import_file_path) ? File.dirname(import_file_path) : import_file_path,
314
+ zip? ? importer_unzip_path : File.dirname(import_file_path),
249
315
  'files'
250
316
  )
251
317
  end
252
318
 
253
319
  private
254
320
 
321
+ def unique_collection_identifier(collection_hash)
322
+ entry_uid = collection_hash[source_identifier]
323
+ entry_uid ||= if Bulkrax.fill_in_blank_source_identifiers.present?
324
+ Bulkrax.fill_in_blank_source_identifiers.call(self, records.find_index(collection_hash))
325
+ else
326
+ collection_hash[:title].split(/\s*[;|]\s*/).first
327
+ end
328
+
329
+ entry_uid
330
+ end
331
+
255
332
  # Override to return the first CSV in the path, if a zip file is supplied
256
333
  # We expect a single CSV at the top level of the zip in the CSVParser
257
334
  # but we are willing to go look for it if need be
258
335
  def real_import_file_path
259
- if file? && zip?
260
- unzip(parser_fields['import_file_path'])
261
- return Dir["#{importer_unzip_path}/**/*.csv"].first
262
- else
263
- parser_fields['import_file_path']
264
- end
336
+ return Dir["#{importer_unzip_path}/**/*.csv"].first if file? && zip?
337
+
338
+ parser_fields['import_file_path']
265
339
  end
266
340
  end
267
341
  end
@@ -75,7 +75,7 @@ module Bulkrax
75
75
 
76
76
  new_entry = collection_entry_class.where(importerexporter: importerexporter, identifier: unique_collection_identifier, raw_metadata: metadata).first_or_create!
77
77
  # perform now to ensure this gets created before work imports start
78
- ImportWorkCollectionJob.perform_now(new_entry.id, importerexporter.current_run.id)
78
+ ImportCollectionJob.perform_now(new_entry.id, importerexporter.current_run.id)
79
79
  increment_counters(index, true)
80
80
  end
81
81
  end
@@ -119,7 +119,10 @@ module Bulkrax
119
119
  end
120
120
  end
121
121
 
122
- def create_parent_child_relationships; end
122
+ # TODO: change to differentiate between collection and work records when adding ability to import collection metadata
123
+ def works_total
124
+ total
125
+ end
123
126
 
124
127
  def total
125
128
  @total ||= records(quick: true).doc.find(".//resumptionToken").to_a.first.attributes["completeListSize"].to_i
@@ -12,6 +12,11 @@ module Bulkrax
12
12
  # @todo not yet supported
13
13
  def create_collections; end
14
14
 
15
+ # TODO: change to differentiate between collection and work records when adding ability to import collection metadata
16
+ def works_total
17
+ total
18
+ end
19
+
15
20
  # @todo not yet supported
16
21
  def import_fields; end
17
22
 
@@ -32,7 +32,7 @@
32
32
  prompt: 'Select from the list',
33
33
  label_html: { class: 'importer export-source-option hidden' },
34
34
  input_html: { class: 'importer export-source-option hidden' },
35
- collection: form.object.importers_list %>
35
+ collection: form.object.importers_list.sort %>
36
36
 
37
37
  <%= form.input :export_source_collection,
38
38
  prompt: 'Start typing ...',
@@ -7,6 +7,13 @@
7
7
  <div class='panel panel-default'>
8
8
  <div class='panel-body'>
9
9
 
10
+ <% if File.exist?(@exporter.exporter_export_zip_path) %>
11
+ <p class='bulkrax-p-align'>
12
+ <strong>Download:</strong>
13
+ <%= link_to raw('<span class="glyphicon glyphicon-download"></span>'), exporter_download_path(@exporter) %>
14
+ </p>
15
+ <% end %>
16
+
10
17
  <p class='bulkrax-p-align'>
11
18
  <strong><%= t('bulkrax.exporter.labels.name') %>:</strong>
12
19
  <%= @exporter.name %>
@@ -50,8 +57,9 @@
50
57
  <strong><%= t('bulkrax.exporter.labels.limit') %>:</strong>
51
58
  <%= @exporter.limit %>
52
59
  </p>
60
+ <%= render partial: 'bulkrax/shared/bulkrax_errors', locals: {item: @exporter} %>
53
61
 
54
- <%= render partial: 'bulkrax/shared/bulkrax_field_mapping', locals: {item: @exporter} %>
62
+ <%= render partial: 'bulkrax/shared/bulkrax_field_mapping', locals: {item: @exporter} %>
55
63
 
56
64
  <%# Currently, no parser-specific fields exist on Exporter,
57
65
  thus there's no real reason to always show this field %>
@@ -116,6 +124,10 @@
116
124
  <%= page_entries_info(@work_entries) %><br>
117
125
  <%= paginate(@work_entries, param_name: :work_entries_page) %>
118
126
  <br>
127
+ <% if File.exist?(@exporter.exporter_export_zip_path) %>
128
+ <%= link_to 'Download', exporter_download_path(@exporter) %>
129
+ |
130
+ <% end %>
119
131
  <%= link_to 'Edit', edit_exporter_path(@exporter) %>
120
132
  |
121
133
  <%= link_to 'Back', exporters_path %>
@@ -1,16 +1,47 @@
1
+ <div class="modal fade" id="bulkraxModal" tabindex="-1" role="dialog" aria-labelledby="bulkraxModalLabel">
2
+ <div class="modal-dialog" role="document">
3
+ <div class="modal-content">
4
+ <div class="modal-body">
5
+ <h5>Options for Updating the Importer</h5>
6
+ <hr />
1
7
 
2
- <%# If we can know the parser, we can vary the buttons here %>
8
+ <% if @importer.importer_runs.blank? %>
9
+ <p>Only update the values in the importer form. Do not import metadata or files for any works or collections.</p>
10
+ <%= form.button :submit, value: 'Update Importer', class: 'btn btn-primary' %>
11
+ <hr />
12
+ <p>Update the values in the importer form and run the importer for the first time.</p>
13
+ <%= form.button :submit, value: 'Update and Import', class: 'btn btn-primary' %>
14
+ <% elsif @importer.parser_klass.include?('Oai') %>
15
+ <p>Only update the values in the importer form. Do not update metadata or files for any works or collections.</p>
16
+ <%= form.button :submit, value: 'Update Importer', class: 'btn btn-primary' %>
17
+ <hr />
18
+ <p>Update the values in the importer form and update items that have changed at the source.</p>
19
+ <%= form.button :submit, value: 'Update and Harvest Updated Items', class: 'btn btn-primary' %>
20
+ <hr />
21
+ <p>Update the values in the importer form and recreate all items from the source.</p>
22
+ <%= form.button :submit, value: 'Update and Re-Harvest All Items', class: 'btn btn-primary' %>
23
+ <% else %>
24
+ <p>Only update the values in the importer form. Do not update metadata or files for any works or collections.</p>
25
+ <%= form.button :submit, value: 'Update Importer', class: 'btn btn-primary' %>
26
+ <hr />
27
+ <p>Update the values in the importer form and update the metadata for all works. Do not update any files.</p>
28
+ <%= form.button :submit, value: 'Update Metadata', class: 'btn btn-primary' %>
29
+ <hr />
30
+ <p>Update the values in the importer form and update the metadata and files for all works. Creates new versions of the files and retains the old versions.</p>
31
+ <%= form.button :submit, value: 'Update Metadata and Files', class: 'btn btn-primary' %>
32
+ <hr />
33
+ <p>Update the values in the importer form and update the metadata. Completely removes all files attached to works for this importer and recreates the files from scratch.</p>
34
+ <%= form.button :submit,
35
+ value: 'Update and Replace Files',
36
+ class: 'btn btn-primary',
37
+ data: {confirm: "Are you sure? This will remove all files before adding them from the import."} %>
38
+ <% end %>
39
+ <hr />
3
40
 
4
- <% if @importer.importer_runs.blank? %>
5
- <%= form.button :submit, value: 'Update Importer', class: 'btn btn-primary' %>
6
- | <%= form.button :submit, value: 'Update and Import (importer has not yet been run)', class: 'btn btn-primary' %>
7
- <% elsif @importer.parser_klass.include?('Oai') %>
8
- <%= form.button :submit, value: 'Update Importer', class: 'btn btn-primary' %>
9
- | <%= form.button :submit, value: 'Update and Harvest Updated Items', class: 'btn btn-primary' %>
10
- | <%= form.button :submit, value: 'Update and Re-Harvest All Items', class: 'btn btn-primary' %>
11
- <% else %>
12
- <%= form.button :submit, value: 'Update Importer', class: 'btn btn-primary' %>
13
- | <%= form.button :submit, value: 'Update and Re-Import (update metadata only)', class: 'btn btn-primary' %>
14
- | <%= form.button :submit, value: 'Update All (update metadata and update files)', class: 'btn btn-primary' %>
15
- | <%= form.button :submit, value: 'Update and Re-Import (update metadata and replace files)', class: 'btn btn-primary', data: {confirm: "Are you sure? This will remove all files before adding them from the import."} %>
16
- <% end %>
41
+ </div>
42
+ <div class="modal-footer">
43
+ <button type="button" class="btn btn-default" data-dismiss="modal"><%= t('helpers.action.cancel') %></button>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ </div>
@@ -5,10 +5,12 @@
5
5
  <div class="row">
6
6
  <div class="col-md-12">
7
7
  <div class="panel panel-default tabs">
8
+
8
9
  <%= simple_form_for @importer, html: { multipart: true } do |form| %>
9
10
  <%= render 'form', importer: @importer, form: form %>
10
11
  <div class="panel-footer">
11
12
  <div class='pull-right'>
13
+ <%= link_to 'Update Importer', '#bulkraxModal', class: "btn btn-primary", data: { toggle: 'modal' } %>
12
14
  <%= render 'edit_form_buttons', form: form %>
13
15
  <% cancel_path = form.object.persisted? ? importer_path(form.object) : importers_path %>
14
16
  | <%= link_to t('.cancel'), cancel_path, class: 'btn btn-default ' %>
@@ -31,23 +31,21 @@
31
31
  </thead>
32
32
  <tbody>
33
33
  <% @importers.each do |importer| %>
34
- <tr>
35
- <th scope="row"><%= link_to importer.name, importer_path(importer) %></th>
36
- <td>
37
- <%= importer.status %>
38
- </td>
39
- <td><%= importer.last_imported_at.strftime("%b %d, %Y") if importer.last_imported_at %></td>
40
- <td><%= importer.next_import_at.strftime("%b %d, %Y") if importer.next_import_at %></td>
41
- <td><%= importer.importer_runs.last&.enqueued_records %></td>
42
- <td><%= (importer.importer_runs.last&.processed_collections || 0) + (importer.importer_runs.last&.processed_records || 0) %></td>
43
- <td><%= (importer.importer_runs.last&.failed_collections || 0) + (importer.importer_runs.last&.failed_records || 0) %></td>
44
- <td><%= importer.importer_runs.last&.deleted_records %></td>
45
- <td><%= importer.importer_runs.last&.total_collection_entries %></td>
46
- <td><%= importer.importer_runs.last&.total_work_entries %></td>
47
- <td><%= link_to raw('<span class="glyphicon glyphicon-info-sign"></span>'), importer_path(importer) %></td>
48
- <td><%= link_to raw('<span class="glyphicon glyphicon-pencil"></span>'), edit_importer_path(importer) %></td>
49
- <td><%= link_to raw('<span class="glyphicon glyphicon-remove"></span>'), importer, method: :delete, data: { confirm: 'Are you sure?' } %></td>
50
- </tr>
34
+ <tr>
35
+ <th scope="row"><%= link_to importer.name, importer_path(importer) %></th>
36
+ <td><%= importer.status %></td>
37
+ <td><%= importer.last_imported_at.strftime("%b %d, %Y") if importer.last_imported_at %></td>
38
+ <td><%= importer.next_import_at.strftime("%b %d, %Y") if importer.next_import_at %></td>
39
+ <td><%= importer.importer_runs.last&.enqueued_records %></td>
40
+ <td><%= (importer.importer_runs.last&.processed_collections || 0) + (importer.importer_runs.last&.processed_records || 0) %></td>
41
+ <td><%= (importer.importer_runs.last&.failed_collections || 0) + (importer.importer_runs.last&.failed_records || 0) %></td>
42
+ <td><%= importer.importer_runs.last&.deleted_records %></td>
43
+ <td><%= importer.importer_runs.last&.total_collection_entries %></td>
44
+ <td><%= importer.importer_runs.last&.total_work_entries %></td>
45
+ <td><%= link_to raw('<span class="glyphicon glyphicon-info-sign"></span>'), importer_path(importer) %></td>
46
+ <td><%= link_to raw('<span class="glyphicon glyphicon-pencil"></span>'), edit_importer_path(importer) %></td>
47
+ <td><%= link_to raw('<span class="glyphicon glyphicon-remove"></span>'), importer, method: :delete, data: { confirm: 'Are you sure?' } %></td>
48
+ </tr>
51
49
  <% end %>
52
50
  </tbody>
53
51
  </table>
@@ -101,8 +101,10 @@
101
101
  <td><%= e.id %></td>
102
102
  <% if e.status == "Complete" %>
103
103
  <td><span class="glyphicon glyphicon-ok" style="color: green;"></span> <%= e.status %></td>
104
+ <% elsif e.status == "Pending" %>
105
+ <td><span class="glyphicon glyphicon-option-horizontal" style="color: blue;"></span> <%= e.status %></td>
104
106
  <% else %>
105
- <td><span class="glyphicon glyphicon-remove" style="color: red;"></span> <%= e.status %></td>
107
+ <td><span class="glyphicon glyphicon-remove" style="color: <%= e.status == 'Deleted' ? 'green' : 'red' %>;"></span> <%= e.status %></td>
106
108
  <% end %>
107
109
  <% if e.last_error.present? %>
108
110
  <td><%= link_to e.last_error.dig("error_class"), bulkrax.importer_entry_path(@importer.id, e.id) %></td>
@@ -137,8 +139,10 @@
137
139
  <td><%= e.id %></td>
138
140
  <% if e.status == "Complete" %>
139
141
  <td><span class="glyphicon glyphicon-ok" style="color: green;"></span> <%= e.status %></td>
142
+ <% elsif e.status == "Pending" %>
143
+ <td><span class="glyphicon glyphicon-option-horizontal" style="color: blue;"></span> <%= e.status %></td>
140
144
  <% else %>
141
- <td><span class="glyphicon glyphicon-remove" style="color: red;"></span> <%= e.status %></td>
145
+ <td><span class="glyphicon glyphicon-remove" style="color: <%= e.status == 'Deleted' ? 'green' : 'red' %>;"></span> <%= e.status %></td>
142
146
  <% end %>
143
147
  <% if e.last_error.present? %>
144
148
  <td><%= link_to e.last_error.dig("error_class"), bulkrax.importer_entry_path(@importer.id, e.id) %></td>
@@ -6,6 +6,7 @@ en:
6
6
  importers: Importers
7
7
  exporter:
8
8
  labels:
9
+ all: All
9
10
  collection: Collection
10
11
  export_format: Export Format
11
12
  export_from: Export From
@@ -6,7 +6,11 @@ end
6
6
 
7
7
  class ChangeImporterAndExporterToPolymorphic < ActiveRecord::Migration[5.1]
8
8
  def change
9
- rename_column :bulkrax_entries, :importer_id, :importerexporter_id if column_exists?(:bulkrax_entries, :importer_id)
9
+ if column_exists?(:bulkrax_entries, :importer_id)
10
+ remove_foreign_key :bulkrax_entries, column: :importer_id
11
+ remove_index :bulkrax_entries, :importer_id
12
+ rename_column :bulkrax_entries, :importer_id, :importerexporter_id
13
+ end
10
14
  add_column :bulkrax_entries, :importerexporter_type, :string, after: :id, default: 'Bulkrax::Importer' unless column_exists?(:bulkrax_entries, :importerexporter_type)
11
15
  end
12
16
  end
@@ -0,0 +1,5 @@
1
+ class ChangeBulkraxStatusesErrorMessageColumnTypeToText < ActiveRecord::Migration[5.1]
2
+ def change
3
+ change_column :bulkrax_statuses, :error_message, :text
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class RenameChildrenCountersToRelationships < ActiveRecord::Migration[5.2]
2
+ def change
3
+ rename_column :bulkrax_importer_runs, :processed_children, :processed_relationships
4
+ rename_column :bulkrax_importer_runs, :failed_children, :failed_relationships
5
+ end
6
+ end
@@ -25,7 +25,7 @@ module Bulkrax
25
25
  config.after_initialize do
26
26
  my_engine_root = Bulkrax::Engine.root.to_s
27
27
  paths = ActionController::Base.view_paths.collect(&:to_s)
28
- hyrax_path = paths.detect { |path| path.match('/hyrax-') }
28
+ hyrax_path = paths.detect { |path| path.match(/\/hyrax-[\d\.]+.*/) }
29
29
  paths = if hyrax_path
30
30
  paths.insert(paths.index(hyrax_path), my_engine_root + '/app/views')
31
31
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bulkrax
4
- VERSION = '1.0.1'
4
+ VERSION = '2.0.2'
5
5
  end