bulkrax 6.0.1 → 7.0.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +7 -7
  3. data/app/assets/javascripts/bulkrax/bulkrax.js +11 -0
  4. data/app/assets/javascripts/bulkrax/datatables.js +139 -0
  5. data/app/assets/javascripts/bulkrax/exporters.js +4 -4
  6. data/app/assets/javascripts/bulkrax/importers.js.erb +15 -1
  7. data/app/assets/stylesheets/bulkrax/import_export.scss +6 -1
  8. data/app/controllers/bulkrax/entries_controller.rb +52 -3
  9. data/app/controllers/bulkrax/exporters_controller.rb +19 -7
  10. data/app/controllers/bulkrax/importers_controller.rb +29 -11
  11. data/app/controllers/concerns/bulkrax/datatables_behavior.rb +201 -0
  12. data/app/helpers/bulkrax/application_helper.rb +7 -3
  13. data/app/jobs/bulkrax/create_relationships_job.rb +1 -1
  14. data/app/jobs/bulkrax/delete_and_import_collection_job.rb +8 -0
  15. data/app/jobs/bulkrax/delete_and_import_file_set_job.rb +8 -0
  16. data/app/jobs/bulkrax/delete_and_import_job.rb +20 -0
  17. data/app/jobs/bulkrax/delete_and_import_work_job.rb +8 -0
  18. data/app/jobs/bulkrax/delete_job.rb +5 -1
  19. data/app/jobs/bulkrax/download_cloud_file_job.rb +1 -1
  20. data/app/jobs/bulkrax/import_collection_job.rb +1 -1
  21. data/app/jobs/bulkrax/import_file_set_job.rb +1 -1
  22. data/app/jobs/bulkrax/import_job.rb +7 -0
  23. data/app/jobs/bulkrax/import_work_job.rb +1 -1
  24. data/app/jobs/bulkrax/importer_job.rb +1 -1
  25. data/app/models/bulkrax/csv_collection_entry.rb +1 -3
  26. data/app/models/bulkrax/csv_entry.rb +2 -1
  27. data/app/models/bulkrax/entry.rb +2 -0
  28. data/app/models/bulkrax/exporter.rb +9 -2
  29. data/app/models/bulkrax/importer.rb +48 -7
  30. data/app/models/bulkrax/oai_set_entry.rb +1 -3
  31. data/app/models/bulkrax/rdf_collection_entry.rb +1 -4
  32. data/app/models/bulkrax/status.rb +10 -1
  33. data/app/models/concerns/bulkrax/file_set_entry_behavior.rb +4 -2
  34. data/app/models/concerns/bulkrax/import_behavior.rb +4 -16
  35. data/app/models/concerns/bulkrax/status_info.rb +8 -0
  36. data/app/parsers/bulkrax/application_parser.rb +90 -19
  37. data/app/parsers/bulkrax/bagit_parser.rb +0 -23
  38. data/app/parsers/bulkrax/csv_parser.rb +0 -52
  39. data/app/parsers/bulkrax/oai_dc_parser.rb +26 -16
  40. data/app/parsers/bulkrax/parser_export_record_set.rb +2 -2
  41. data/app/parsers/bulkrax/xml_parser.rb +18 -21
  42. data/app/services/bulkrax/factory_class_finder.rb +90 -0
  43. data/app/views/bulkrax/exporters/_form.html.erb +10 -10
  44. data/app/views/bulkrax/exporters/index.html.erb +13 -57
  45. data/app/views/bulkrax/exporters/show.html.erb +2 -10
  46. data/app/views/bulkrax/importers/_csv_fields.html.erb +7 -1
  47. data/app/views/bulkrax/importers/_edit_form_buttons.html.erb +8 -1
  48. data/app/views/bulkrax/importers/_edit_item_buttons.html.erb +18 -0
  49. data/app/views/bulkrax/importers/index.html.erb +20 -64
  50. data/app/views/bulkrax/importers/show.html.erb +7 -13
  51. data/app/views/bulkrax/shared/_entries_tab.html.erb +16 -0
  52. data/config/routes.rb +8 -2
  53. data/db/migrate/20240208005801_denormalize_status_message.rb +7 -0
  54. data/db/migrate/20240209070952_update_identifier_index.rb +6 -0
  55. data/lib/bulkrax/engine.rb +6 -0
  56. data/lib/bulkrax/persistence_layer/active_fedora_adapter.rb +27 -0
  57. data/lib/bulkrax/persistence_layer/valkyrie_adapter.rb +8 -0
  58. data/lib/bulkrax/persistence_layer.rb +38 -0
  59. data/lib/bulkrax/version.rb +1 -1
  60. data/lib/bulkrax.rb +88 -2
  61. data/lib/tasks/bulkrax_tasks.rake +12 -0
  62. metadata +46 -6
  63. data/app/views/bulkrax/shared/_collection_entries_tab.html.erb +0 -39
  64. data/app/views/bulkrax/shared/_file_set_entries_tab.html.erb +0 -39
  65. data/app/views/bulkrax/shared/_work_entries_tab.html.erb +0 -39
@@ -14,7 +14,7 @@ module Bulkrax
14
14
  :seen, :increment_counters, :parser_fields, :user, :keys_without_numbers,
15
15
  :key_without_numbers, :status, :set_status_info, :status_info, :status_at,
16
16
  :exporter_export_path, :exporter_export_zip_path, :importer_unzip_path, :validate_only,
17
- :zip?, :file?,
17
+ :zip?, :file?, :remove_and_rerun,
18
18
  to: :importerexporter
19
19
 
20
20
  # @todo Convert to `class_attribute :parser_fiels, default: {}`
@@ -47,6 +47,10 @@ module Bulkrax
47
47
  raise NotImplementedError, 'must be defined'
48
48
  end
49
49
 
50
+ def work_entry_class
51
+ entry_class
52
+ end
53
+
50
54
  # @api public
51
55
  # @abstract Subclass and override {#collection_entry_class} to implement behavior for the parser.
52
56
  def collection_entry_class
@@ -157,6 +161,22 @@ module Bulkrax
157
161
  @visibility ||= self.parser_fields['visibility'] || 'open'
158
162
  end
159
163
 
164
+ def create_collections
165
+ create_objects(['collection'])
166
+ end
167
+
168
+ def create_works
169
+ create_objects(['work'])
170
+ end
171
+
172
+ def create_file_sets
173
+ create_objects(['file_set'])
174
+ end
175
+
176
+ def create_relationships
177
+ create_objects(['relationship'])
178
+ end
179
+
160
180
  # @api public
161
181
  #
162
182
  # @param types [Array<Symbol>] the types of objects that we'll create.
@@ -166,30 +186,77 @@ module Bulkrax
166
186
  # @see #create_works
167
187
  # @see #create_file_sets
168
188
  # @see #create_relationships
169
- def create_objects(types = [])
170
- types.each do |object_type|
171
- send("create_#{object_type.pluralize}")
189
+ def create_objects(types_array = nil)
190
+ index = 0
191
+ (types_array || %w[collection work file_set relationship]).each do |type|
192
+ if type.eql?('relationship')
193
+ ScheduleRelationshipsJob.set(wait: 5.minutes).perform_later(importer_id: importerexporter.id)
194
+ next
195
+ end
196
+ send(type.pluralize).each do |current_record|
197
+ next unless record_has_source_identifier(current_record, index)
198
+ break if limit_reached?(limit, index)
199
+ seen[current_record[source_identifier]] = true
200
+ create_entry_and_job(current_record, type)
201
+ increment_counters(index, "#{type}": true)
202
+ index += 1
203
+ end
204
+ importer.record_status
205
+ end
206
+ true
207
+ rescue StandardError => e
208
+ set_status_info(e)
209
+ end
210
+
211
+ def rebuild_entries(types_array = nil)
212
+ index = 0
213
+ (types_array || %w[collection work file_set relationship]).each do |type|
214
+ # works are not gurneteed to have Work in the type
215
+
216
+ importer.entries.where(rebuild_entry_query(type, parser_fields['entry_statuses'])).find_each do |e|
217
+ seen[e.identifier] = true
218
+ e.status_info('Pending', importer.current_run)
219
+ if remove_and_rerun
220
+ delay = calculate_type_delay(type)
221
+ "Bulkrax::DeleteAndImport#{type.camelize}Job".constantize.set(wait: delay).send(perform_method, e, current_run)
222
+ else
223
+ "Bulkrax::Import#{type.camelize}Job".constantize.send(perform_method, e.id, current_run.id)
224
+ end
225
+ increment_counters(index)
226
+ index += 1
227
+ end
172
228
  end
173
229
  end
174
230
 
175
- # @abstract Subclass and override {#create_collections} to implement behavior for the parser.
176
- def create_collections
177
- raise NotImplementedError, 'must be defined' if importer?
178
- end
231
+ def rebuild_entry_query(type, statuses)
232
+ type_col = Bulkrax::Entry.arel_table['type']
233
+ status_col = Bulkrax::Entry.arel_table['status_message']
179
234
 
180
- # @abstract Subclass and override {#create_works} to implement behavior for the parser.
181
- def create_works
182
- raise NotImplementedError, 'must be defined' if importer?
235
+ query = (type == 'work' ? type_col.not.matches(%w[collection file_set]) : type_col.matches(type.camelize))
236
+ query.and(status_col.in(statuses))
183
237
  end
184
238
 
185
- # @abstract Subclass and override {#create_file_sets} to implement behavior for the parser.
186
- def create_file_sets
187
- raise NotImplementedError, 'must be defined' if importer?
239
+ def calculate_type_delay(type)
240
+ return 2.minutes if type == 'file_set'
241
+ return 1.minute if type == 'work'
242
+ return 0
188
243
  end
189
244
 
190
- # @abstract Subclass and override {#create_relationships} to implement behavior for the parser.
191
- def create_relationships
192
- raise NotImplementedError, 'must be defined' if importer?
245
+ def create_entry_and_job(current_record, type, identifier = nil)
246
+ identifier ||= current_record[source_identifier]
247
+ new_entry = find_or_create_entry(send("#{type}_entry_class"),
248
+ identifier,
249
+ 'Bulkrax::Importer',
250
+ current_record.to_h)
251
+ new_entry.status_info('Pending', importer.current_run)
252
+ if current_record[:delete].present?
253
+ "Bulkrax::Delete#{type.camelize}Job".constantize.send(perform_method, new_entry, current_run)
254
+ elsif current_record[:remove_and_rerun].present? || remove_and_rerun
255
+ delay = calculate_type_delay(type)
256
+ "Bulkrax::DeleteAndImport#{type.camelize}Job".constantize.set(wait: delay).send(perform_method, new_entry, current_run)
257
+ else
258
+ "Bulkrax::Import#{type.camelize}Job".constantize.send(perform_method, new_entry.id, current_run.id)
259
+ end
193
260
  end
194
261
 
195
262
  # Optional, define if using browse everything for file upload
@@ -305,11 +372,15 @@ module Bulkrax
305
372
  end
306
373
 
307
374
  def find_or_create_entry(entryclass, identifier, type, raw_metadata = nil)
308
- entry = entryclass.where(
375
+ # limit entry search to just this importer or exporter. Don't go moving them
376
+ entry = importerexporter.entries.where(
377
+ identifier: identifier
378
+ ).first
379
+ entry ||= entryclass.new(
309
380
  importerexporter_id: importerexporter.id,
310
381
  importerexporter_type: type,
311
382
  identifier: identifier
312
- ).first_or_create!
383
+ )
313
384
  entry.raw_metadata = raw_metadata
314
385
  entry.save!
315
386
  entry
@@ -63,29 +63,6 @@ module Bulkrax
63
63
  data
64
64
  end
65
65
 
66
- def create_works
67
- entry_class == CsvEntry ? super : create_rdf_works
68
- end
69
-
70
- def create_rdf_works
71
- records.each_with_index do |record, index|
72
- next unless record_has_source_identifier(record, index)
73
- break if limit_reached?(limit, index)
74
-
75
- seen[record[source_identifier]] = true
76
- new_entry = find_or_create_entry(entry_class, record[source_identifier], 'Bulkrax::Importer', record)
77
- if record[:delete].present?
78
- DeleteWorkJob.send(perform_method, new_entry, current_run)
79
- else
80
- ImportWorkJob.send(perform_method, new_entry.id, current_run.id)
81
- end
82
- increment_counters(index, work: true)
83
- end
84
- importer.record_status
85
- rescue StandardError => e
86
- set_status_info(e)
87
- end
88
-
89
66
  # export methods
90
67
 
91
68
  # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
@@ -113,57 +113,6 @@ module Bulkrax
113
113
  false
114
114
  end
115
115
 
116
- def create_collections
117
- create_objects(['collection'])
118
- end
119
-
120
- def create_works
121
- create_objects(['work'])
122
- end
123
-
124
- def create_file_sets
125
- create_objects(['file_set'])
126
- end
127
-
128
- def create_relationships
129
- create_objects(['relationship'])
130
- end
131
-
132
- def create_objects(types_array = nil)
133
- index = 0
134
- (types_array || %w[collection work file_set relationship]).each do |type|
135
- if type.eql?('relationship')
136
- ScheduleRelationshipsJob.set(wait: 5.minutes).perform_later(importer_id: importerexporter.id)
137
- next
138
- end
139
- send(type.pluralize).each do |current_record|
140
- next unless record_has_source_identifier(current_record, index)
141
- break if limit_reached?(limit, index)
142
-
143
- seen[current_record[source_identifier]] = true
144
- create_entry_and_job(current_record, type)
145
- increment_counters(index, "#{type}": true)
146
- index += 1
147
- end
148
- importer.record_status
149
- end
150
- true
151
- rescue StandardError => e
152
- set_status_info(e)
153
- end
154
-
155
- def create_entry_and_job(current_record, type)
156
- new_entry = find_or_create_entry(send("#{type}_entry_class"),
157
- current_record[source_identifier],
158
- 'Bulkrax::Importer',
159
- current_record.to_h)
160
- if current_record[:delete].present?
161
- "Bulkrax::Delete#{type.camelize}Job".constantize.send(perform_method, new_entry, current_run)
162
- else
163
- "Bulkrax::Import#{type.camelize}Job".constantize.send(perform_method, new_entry.id, current_run.id)
164
- end
165
- end
166
-
167
116
  def write_partial_import_file(file)
168
117
  import_filename = import_file_path.split('/').last
169
118
  partial_import_filename = "#{File.basename(import_filename, '.csv')}_corrected_entries.csv"
@@ -204,7 +153,6 @@ module Bulkrax
204
153
  def entry_class
205
154
  CsvEntry
206
155
  end
207
- alias work_entry_class entry_class
208
156
 
209
157
  def collection_entry_class
210
158
  CsvCollectionEntry
@@ -63,6 +63,12 @@ module Bulkrax
63
63
 
64
64
  delegate :list_sets, to: :client
65
65
 
66
+ def create_objects(types = [])
67
+ types.each do |object_type|
68
+ send("create_#{object_type.pluralize}")
69
+ end
70
+ end
71
+
66
72
  def create_collections
67
73
  metadata = {
68
74
  visibility: 'open'
@@ -86,27 +92,31 @@ module Bulkrax
86
92
  results = self.records(quick: true)
87
93
  return if results.blank?
88
94
  results.full.each_with_index do |record, index|
89
- identifier = record.send(source_identifier)
90
- if identifier.blank?
91
- if Bulkrax.fill_in_blank_source_identifiers.present?
92
- identifier = Bulkrax.fill_in_blank_source_identifiers.call(self, index)
93
- else
94
- invalid_record("Missing #{source_identifier} for #{record.to_h}\n")
95
- next
96
- end
97
- end
98
-
95
+ identifier = record_has_source_identifier(record, index)
96
+ next unless identifier
99
97
  break if limit_reached?(limit, index)
98
+
100
99
  seen[identifier] = true
101
- new_entry = entry_class.where(importerexporter: self.importerexporter, identifier: identifier).first_or_create!
102
- if record.deleted?
103
- DeleteWorkJob.send(perform_method, new_entry, importerexporter.current_run)
104
- else
105
- ImportWorkJob.send(perform_method, new_entry.id, importerexporter.current_run.id)
106
- end
100
+ create_entry_and_job(record, 'work', identifier)
107
101
  increment_counters(index, work: true)
108
102
  end
109
103
  importer.record_status
104
+ rescue StandardError => e
105
+ set_status_info(e)
106
+ end
107
+
108
+ # oai records so not let us set the source identifier easily
109
+ def record_has_source_identifier(record, index)
110
+ identifier = record.send(source_identifier)
111
+ if identifier.blank?
112
+ if Bulkrax.fill_in_blank_source_identifiers.present?
113
+ identifier = Bulkrax.fill_in_blank_source_identifiers.call(self, index)
114
+ else
115
+ invalid_record("Missing #{source_identifier} for #{record.to_h}\n")
116
+ return false
117
+ end
118
+ end
119
+ identifier
110
120
  end
111
121
 
112
122
  def collections
@@ -113,14 +113,14 @@ module Bulkrax
113
113
  #
114
114
  # @see #file_sets
115
115
  def candidate_file_set_ids
116
- @candidate_file_set_ids ||= works.flat_map { |work| work.fetch("#{Bulkrax.file_model_class.to_s.underscore}_ids_ssim", []) }
116
+ @candidate_file_set_ids ||= works.flat_map { |work| work.fetch(Bulkrax.solr_key_for_member_file_ids, []) }
117
117
  end
118
118
 
119
119
  # @note Specifically not memoizing this so we can merge values without changing the object.
120
120
  #
121
121
  # No sense attempting to query for more than the limit.
122
122
  def query_kwargs
123
- { fl: "id,#{Bulkrax.file_model_class.to_s.underscore}_ids_ssim", method: :post, rows: row_limit }
123
+ { fl: "id,#{Bulkrax.solr_key_for_member_file_ids}", method: :post, rows: row_limit }
124
124
  end
125
125
 
126
126
  # If we have a limit, we need not query beyond that limit
@@ -11,13 +11,29 @@ module Bulkrax
11
11
  def collection_entry_class; end
12
12
 
13
13
  # @todo not yet supported
14
- def create_collections; end
14
+ def create_collections
15
+ raise NotImplementedError
16
+ end
15
17
 
16
18
  # @todo not yet supported
17
19
  def file_set_entry_class; end
18
20
 
19
21
  # @todo not yet supported
20
- def create_file_sets; end
22
+ def create_file_sets
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def file_sets
27
+ raise NotImplementedError
28
+ end
29
+
30
+ def collections
31
+ raise NotImplementedError
32
+ end
33
+
34
+ def works
35
+ records
36
+ end
21
37
 
22
38
  # TODO: change to differentiate between collection and work records when adding ability to import collection metadata
23
39
  def works_total
@@ -92,25 +108,6 @@ module Bulkrax
92
108
  %w[.xml .xls .xsd].include?(File.extname(path)) || ::Marcel::MimeType.for(path).include?('application/xml')
93
109
  end
94
110
 
95
- def create_works
96
- records.each_with_index do |record, index|
97
- next unless record_has_source_identifier(record, index)
98
- break if !limit.nil? && index >= limit
99
-
100
- seen[record[source_identifier]] = true
101
- new_entry = find_or_create_entry(entry_class, record[source_identifier], 'Bulkrax::Importer', record)
102
- if record[:delete].present?
103
- DeleteWorkJob.send(perform_method, new_entry, current_run)
104
- else
105
- ImportWorkJob.send(perform_method, new_entry.id, current_run.id)
106
- end
107
- increment_counters(index, work: true)
108
- end
109
- importer.record_status
110
- rescue StandardError => e
111
- set_status_info(e)
112
- end
113
-
114
111
  def total
115
112
  records.size
116
113
  end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bulkrax
4
+ class FactoryClassFinder
5
+ ##
6
+ # The v6.0.0 default coercer. Responsible for converting a factory class name to a constant.
7
+ module DefaultCoercer
8
+ ##
9
+ # @param name [String]
10
+ # @return [Class] when the name is a coercible constant.
11
+ # @raise [NameError] when the name is not coercible to a constant.
12
+ def self.call(name)
13
+ name.constantize
14
+ end
15
+ end
16
+
17
+ ##
18
+ # A name coercer that favors classes that end with "Resource" but will attempt to fallback to
19
+ # those that don't.
20
+ module ValkyrieMigrationCoercer
21
+ SUFFIX = "Resource"
22
+
23
+ ##
24
+ # @param name [String]
25
+ # @param suffix [String] the suffix we use for a naming convention.
26
+ #
27
+ # @return [Class] when the name is a coercible constant.
28
+ # @raise [NameError] when the name is not coercible to a constant.
29
+ def self.call(name, suffix: SUFFIX)
30
+ if name.end_with?(suffix)
31
+ name.constantize
32
+ else
33
+ begin
34
+ "#{name}#{suffix}".constantize
35
+ rescue NameError
36
+ name.constantize
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ ##
43
+ # @param entry [Bulkrax::Entry]
44
+ # @return [Class]
45
+ def self.find(entry:, coercer: Bulkrax.factory_class_name_coercer || DefaultCoercer)
46
+ new(entry: entry, coercer: coercer).find
47
+ end
48
+
49
+ def initialize(entry:, coercer:)
50
+ @entry = entry
51
+ @coercer = coercer
52
+ end
53
+ attr_reader :entry, :coercer
54
+
55
+ ##
56
+ # @return [Class] when we are able to derive the class based on the {#name}.
57
+ # @return [Nil] when we encounter errors with constantizing the {#name}.
58
+ # @see #name
59
+ def find
60
+ coercer.call(name)
61
+ rescue NameError
62
+ nil
63
+ rescue
64
+ entry.default_work_type.constantize
65
+ end
66
+
67
+ ##
68
+ # @api private
69
+ # @return [String]
70
+ def name
71
+ fc = if entry.parsed_metadata&.[]('model').present?
72
+ Array.wrap(entry.parsed_metadata['model']).first
73
+ elsif entry.importerexporter&.mapping&.[]('work_type').present?
74
+ # Because of delegation's nil guard, we're reaching rather far into the implementation
75
+ # details.
76
+ Array.wrap(entry.parsed_metadata['work_type']).first
77
+ else
78
+ entry.default_work_type
79
+ end
80
+
81
+ # Let's coerce this into the right shape; we're not mutating the string because it might well
82
+ # be frozen.
83
+ fc = fc.tr(' ', '_')
84
+ fc = fc.downcase if fc.match?(/[-_]/)
85
+ fc.camelcase
86
+ rescue
87
+ entry.default_work_type
88
+ end
89
+ end
90
+ end
@@ -33,8 +33,8 @@
33
33
  label: t('bulkrax.exporter.labels.importer'),
34
34
  required: true,
35
35
  prompt: 'Select from the list',
36
- label_html: { class: 'importer export-source-option d-none' },
37
- input_html: { class: 'importer export-source-option d-none form-control' },
36
+ label_html: { class: 'importer export-source-option d-none hidden' },
37
+ input_html: { class: 'importer export-source-option d-none hidden form-control' },
38
38
  collection: form.object.importers_list.sort %>
39
39
 
40
40
  <%= form.input :export_source_collection,
@@ -42,9 +42,9 @@
42
42
  label: t('bulkrax.exporter.labels.collection'),
43
43
  required: true,
44
44
  placeholder: @collection&.title&.first,
45
- label_html: { class: 'collection export-source-option d-none' },
45
+ label_html: { class: 'collection export-source-option d-none hidden' },
46
46
  input_html: {
47
- class: 'collection export-source-option d-none form-control',
47
+ class: 'collection export-source-option d-none hidden form-control',
48
48
  data: {
49
49
  'autocomplete-url' => '/authorities/search/collections',
50
50
  'autocomplete' => 'collection'
@@ -56,8 +56,8 @@
56
56
  label: t('bulkrax.exporter.labels.worktype'),
57
57
  required: true,
58
58
  prompt: 'Select from the list',
59
- label_html: { class: 'worktype export-source-option d-none' },
60
- input_html: { class: 'worktype export-source-option d-none form-control' },
59
+ label_html: { class: 'worktype export-source-option d-none hidden' },
60
+ input_html: { class: 'worktype export-source-option d-none hidden form-control' },
61
61
  collection: Bulkrax.curation_concerns.map { |cc| [cc.to_s, cc.to_s] } %>
62
62
 
63
63
  <%= form.input :limit,
@@ -80,7 +80,7 @@
80
80
  as: :boolean,
81
81
  label: t('bulkrax.exporter.labels.filter_by_date') %>
82
82
 
83
- <div id="date_filter_picker" class="d-none">
83
+ <div id="date_filter_picker" class="d-none hidden">
84
84
  <%= form.input :start_date,
85
85
  as: :date,
86
86
  label: t('bulkrax.exporter.labels.start_date'),
@@ -136,13 +136,13 @@
136
136
  // get the date filter option and show the corresponding date selectors
137
137
  $('.exporter_date_filter').change(function () {
138
138
  if ($('.exporter_date_filter').find(".boolean").is(":checked"))
139
- $('#date_filter_picker').removeClass('d-none');
139
+ $('#date_filter_picker').removeClass('d-none hidden');
140
140
  else
141
- $('#date_filter_picker').addClass('d-none');
141
+ $('#date_filter_picker').addClass('d-none hidden');
142
142
  });
143
143
 
144
144
  if ($('.exporter_date_filter').find(".boolean").is(":checked"))
145
- $('#date_filter_picker').removeClass('d-none');
145
+ $('#date_filter_picker').removeClass('d-none hidden');
146
146
  });
147
147
  });
148
148
  </script>
@@ -13,62 +13,18 @@
13
13
 
14
14
  <div class="panel panel-default">
15
15
  <div class="panel-body">
16
- <% if @exporters.present? %>
17
- <div class="table-responsive">
18
- <table class="table table-striped datatable">
19
- <thead>
20
- <tr>
21
- <th scope="col">Name</th>
22
- <th scope="col">Status</th>
23
- <th scope="col">Date Exported</th>
24
- <th scope="col">Downloadable Files</th>
25
- <th scope="col"></th>
26
- <th scope="col"></th>
27
- <th scope="col"></th>
28
- </tr>
29
- </thead>
30
- <tbody>
31
- <% @exporters.each do |exporter| %>
32
- <tr>
33
- <th scope="row"><%= link_to exporter.name, exporter_path(exporter) %></th>
34
- <td><%= exporter.status %></td>
35
- <td><%= exporter.created_at %></td>
36
- <td>
37
- <% if File.exist?(exporter.exporter_export_zip_path) %>
38
- <%= simple_form_for(exporter, method: :get, url: exporter_download_path(exporter)) do |form| %>
39
- <%= render 'downloads', exporter: exporter, form: form %>
40
- <%= form.button :submit, value: 'Download', data: { disable_with: false } %>
41
- <% end %>
42
- <% end%>
43
- </td>
44
- <td><%= link_to raw('<span class="glyphicon glyphicon-info-sign"></span>'), exporter_path(exporter) %></td>
45
- <td><%= link_to raw('<span class="glyphicon glyphicon-pencil"></span>'), edit_exporter_path(exporter), data: { turbolinks: false } %></td>
46
- <td><%= link_to raw('<span class="glyphicon glyphicon-remove"></span>'), exporter, method: :delete, data: { confirm: 'Are you sure?' } %></td>
47
- </tr>
48
- <% end %>
49
- </tbody>
50
- </table>
51
- </div>
52
- <% else %>
53
- <p>No exporters have been created.</p>
54
- <% end %>
16
+ <div class="table-responsive">
17
+ <table id='exporters-table' class="table table-striped">
18
+ <thead>
19
+ <tr>
20
+ <th scope="col">Name</th>
21
+ <th scope="col">Status</th>
22
+ <th scope="col">Date Exported</th>
23
+ <th scope="col">Downloadable Files</th>
24
+ <th scope="col">Actions</th>
25
+ </tr>
26
+ </thead>
27
+ </table>
28
+ </div>
55
29
  </div>
56
30
  </div>
57
-
58
- <script>
59
- $(function() {
60
- $('#DataTables_Table_0').DataTable({
61
- destroy: true, /* Reinitialize DataTable with config below */
62
- 'columnDefs': [
63
- { 'orderable': true, 'targets': [0, 1, 2] },
64
- { 'orderable': false, 'targets': [3, 4, 5, 6] }
65
- ],
66
- 'language': {
67
- 'info': 'Showing _START_ to _END_ of _TOTAL_ exporters',
68
- 'infoEmpty': 'No exporters to show',
69
- 'infoFiltered': '(filtered from _MAX_ total exporters)',
70
- 'lengthMenu': 'Show _MENU_ exporters'
71
- }
72
- })
73
- })
74
- </script>
@@ -95,16 +95,8 @@
95
95
 
96
96
  <div class="bulkrax-nav-tab-bottom-margin">
97
97
  <!-- Nav tabs -->
98
- <ul class="bulkrax-nav-tab-top-margin tab-nav nav nav-tabs" role="tablist">
99
- <li role="presentation" class="nav-link active"><a href="#work-entries" aria-controls="work-entries" role="tab" data-toggle="tab"><%= t('bulkrax.exporter.labels.work_entries') %></a></li>
100
- <li role="presentation" class="nav-link"><a href="#collection-entries" aria-controls="collection-entries" role="tab" data-toggle="tab"><%= t('bulkrax.exporter.labels.collection_entries') %></a></li>
101
- <li role="presentation" class="nav-link"><a href="#file-set-entries" aria-controls="file-set-entries" role="tab" data-toggle="tab"><%= t('bulkrax.exporter.labels.file_set_entries') %></a></li>
102
- </ul>
103
- <!-- Tab panes -->
104
- <div class="tab-content outline">
105
- <%= render partial: 'bulkrax/shared/work_entries_tab', locals: { item: @exporter, entries: @work_entries } %>
106
- <%= render partial: 'bulkrax/shared/collection_entries_tab', locals: { item: @exporter, entries: @collection_entries } %>
107
- <%= render partial: 'bulkrax/shared/file_set_entries_tab', locals: { item: @exporter, entries: @file_set_entries } %>
98
+ <div class="outline">
99
+ <%= render partial: 'bulkrax/shared/entries_tab', locals: { item: @exporter } %>
108
100
  </div>
109
101
  <br>
110
102
  <%= link_to 'Edit', edit_exporter_path(@exporter) %>
@@ -25,13 +25,19 @@
25
25
  <h4>Add CSV File to Import:</h4>
26
26
  <%# accept a single file upload; data files and bags will need to be added another way %>
27
27
 
28
- <%= fi.input :file_style, collection: ['Upload a File', 'Specify a Path on the Server'], as: :radio_buttons, label: false %>
28
+ <% file_style_list = ['Upload a File', 'Specify a Path on the Server'] %>
29
+ <% file_style_list << 'Existing Entries' unless importer.new_record? %>
30
+ <%= fi.input :file_style, collection: file_style_list, as: :radio_buttons, label: false %>
29
31
  <div id='file_upload'>
30
32
  <%= fi.input 'file', as: :file, input_html: { accept: 'text/csv,application/zip' } %><br />
31
33
  </div>
32
34
  <div id='file_path'>
33
35
  <%= fi.input :import_file_path, as: :string, input_html: { value: importer.parser_fields['import_file_path'] } %>
34
36
  </div>
37
+ <div id='existing_options'>
38
+ <%= fi.collection_check_boxes :entry_statuses, [['Failed'], ['Pending'], ['Skipped'], ['Deleted'], ['Complete']], :first, :first %>
39
+ </div>
40
+
35
41
  <% if defined?(::Hyrax) && Hyrax.config.browse_everything? %>
36
42
  <h4>Add Files to Import:</h4>
37
43
  <p>Choose files to upload. The filenames must be unique, and the filenames must be referenced in a column called 'file' in the accompanying CSV file.</p>
@@ -34,7 +34,14 @@
34
34
  <%= form.button :submit,
35
35
  value: 'Update and Replace Files',
36
36
  class: 'btn btn-primary',
37
- data: {confirm: "Are you sure? This will remove all files before adding them from the import."} %>
37
+ data: {confirm: "Are you sure? This will remove all files before adding them from the import."} %>
38
+ <hr />
39
+ <p>Remove all works and then run the import again from a clean slate. This will remove all files and associations and any edits made since the last import will be lost.</p>
40
+ <%= form.button :submit,
41
+ value: 'Remove and Rerun',
42
+ class: 'btn btn-primary',
43
+ data: {confirm: "Are you sure? This will delete all the works and any associated files and relationships before re running."} %>
44
+
38
45
  <% end %>
39
46
  <hr />
40
47