bulkrax 1.0.1 → 2.0.2

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