bulkrax 6.0.1 → 7.0.0

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