bulk_ops 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/app/assets/images/bulk_ops/github_logo.png +0 -0
- data/app/assets/javascripts/bulk_ops.js +14 -0
- data/app/assets/javascripts/bulk_ops/selections.js +24 -0
- data/app/assets/javascripts/selections.js +38 -0
- data/app/assets/javascripts/work_search.js +64 -0
- data/app/assets/stylesheets/bulk_ops.scss +99 -0
- data/app/controllers/bulk_ops/application_controller.rb +13 -0
- data/app/controllers/bulk_ops/github_authorization_controller.rb +33 -0
- data/app/controllers/bulk_ops/operations_controller.rb +481 -0
- data/app/jobs/bulk_ops/application_job.rb +4 -0
- data/app/mailers/bulk_ops/application_mailer.rb +6 -0
- data/app/models/bulk_ops/application_record.rb +5 -0
- data/app/views/bulk_ops/_bulk_ops_sidebar_widget.html.erb +15 -0
- data/app/views/bulk_ops/_github_auth_widget.html.erb +13 -0
- data/app/views/bulk_ops/operations/_bulk_ops_header.html.erb +4 -0
- data/app/views/bulk_ops/operations/_choose_fields.html.erb +22 -0
- data/app/views/bulk_ops/operations/_choose_notifications.html.erb +22 -0
- data/app/views/bulk_ops/operations/_git_message.html.erb +7 -0
- data/app/views/bulk_ops/operations/_ingest_options.html.erb +42 -0
- data/app/views/bulk_ops/operations/_operation_options.html.erb +38 -0
- data/app/views/bulk_ops/operations/_show_authorize.html.erb +13 -0
- data/app/views/bulk_ops/operations/_show_complete.html.erb +31 -0
- data/app/views/bulk_ops/operations/_show_draft.html.erb +20 -0
- data/app/views/bulk_ops/operations/_show_new.html.erb +2 -0
- data/app/views/bulk_ops/operations/_show_pending.html.erb +58 -0
- data/app/views/bulk_ops/operations/_show_running.html.erb +56 -0
- data/app/views/bulk_ops/operations/_show_verifying.html.erb +8 -0
- data/app/views/bulk_ops/operations/_show_waiting.html.erb +9 -0
- data/app/views/bulk_ops/operations/_update_draft_work_list.html.erb +45 -0
- data/app/views/bulk_ops/operations/_update_draft_work_search.html.erb +59 -0
- data/app/views/bulk_ops/operations/_update_options.html.erb +9 -0
- data/app/views/bulk_ops/operations/index.html.erb +51 -0
- data/app/views/bulk_ops/operations/new.html.erb +36 -0
- data/app/views/bulk_ops/operations/show.html.erb +7 -0
- data/config/routes.rb +25 -0
- data/db/migrate/20180926190757_create_github_credentials.rb +13 -0
- data/db/migrate/20181017180436_create_bulk_ops_tables.rb +40 -0
- data/lib/bulk_ops.rb +15 -0
- data/lib/bulk_ops/create_spreadsheet_job.rb +43 -0
- data/lib/bulk_ops/create_work_job.rb +14 -0
- data/lib/bulk_ops/delete_file_set_job.rb +15 -0
- data/lib/bulk_ops/engine.rb +6 -0
- data/lib/bulk_ops/error.rb +141 -0
- data/lib/bulk_ops/github_access.rb +284 -0
- data/lib/bulk_ops/github_credential.rb +3 -0
- data/lib/bulk_ops/operation.rb +358 -0
- data/lib/bulk_ops/relationship.rb +79 -0
- data/lib/bulk_ops/search_builder_behavior.rb +80 -0
- data/lib/bulk_ops/templates/configuration.yml +5 -0
- data/lib/bulk_ops/templates/readme.md +1 -0
- data/lib/bulk_ops/update_work_job.rb +14 -0
- data/lib/bulk_ops/verification.rb +210 -0
- data/lib/bulk_ops/verification_job.rb +23 -0
- data/lib/bulk_ops/version.rb +3 -0
- data/lib/bulk_ops/work_job.rb +104 -0
- data/lib/bulk_ops/work_proxy.rb +466 -0
- data/lib/generators/bulk_ops/install/install_generator.rb +27 -0
- data/lib/generators/bulk_ops/install/templates/config/github.yml.example +28 -0
- metadata +145 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
class BulkOps::Relationship < ActiveRecord::Base
|
2
|
+
RELATIONSHIP_FIELDS = ['parent','child','order','next','collection']
|
3
|
+
|
4
|
+
self.table_name = "bulk_ops_relationships"
|
5
|
+
belongs_to :work_proxy, class_name: "BulkOps::WorkProxy", foreign_key: "work_proxy_id"
|
6
|
+
|
7
|
+
def initialize *args
|
8
|
+
super *args
|
9
|
+
|
10
|
+
# Attempt to resolve the relationship immediately
|
11
|
+
# which might work in the case of updates
|
12
|
+
resolve!
|
13
|
+
end
|
14
|
+
|
15
|
+
def findObject
|
16
|
+
work_type = (relationship_type.downcase == "collection") ? "Collection" : work_proxy.work_type
|
17
|
+
case identifier_type
|
18
|
+
when "id"
|
19
|
+
begin
|
20
|
+
object = ActiveFedora::Base.find(object_identifier)
|
21
|
+
rescue Ldp::Gone
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
return object || false
|
25
|
+
when "title"
|
26
|
+
# TODO clean up solr query and add work type to it
|
27
|
+
query = "{!field f=title_tesim}#{object_identifier}"
|
28
|
+
objects = ActiveFedora::SolrService.instance.conn.get(ActiveFedora::SolrService.select_path,params: { fq: query, rows: 100})["response"]["docs"].first
|
29
|
+
object = objects.first
|
30
|
+
object ||= Collection.create(title: [object_identifier]) if work_type == "Collection"
|
31
|
+
return object || false
|
32
|
+
when "identifier"
|
33
|
+
query = "{!field f=identifier_tesim}#{object_identifier}"
|
34
|
+
objects = ActiveFedora::SolrService.instance.conn.get(ActiveFedora::SolrService.select_path,params: { fq: query, rows: 100})["response"]["docs"]
|
35
|
+
return false if objects.blank?
|
36
|
+
return objects.first
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def resolve! ()
|
41
|
+
unless subject = work_proxy.work and object = self.findObject
|
42
|
+
wait!
|
43
|
+
return
|
44
|
+
end
|
45
|
+
implement_relationship! relationship_type, subject, object
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def implement_relationship!(type,subject,object)
|
50
|
+
case type
|
51
|
+
when "parent"
|
52
|
+
object.ordered_members << subject
|
53
|
+
object.save
|
54
|
+
when "child"
|
55
|
+
subject.ordered_members << object
|
56
|
+
subject.save
|
57
|
+
when "collection"
|
58
|
+
object.add_members([subject.id])
|
59
|
+
object.save
|
60
|
+
when "next"
|
61
|
+
#TODO - implement this - related to ordering of filesets
|
62
|
+
when "order"
|
63
|
+
#TODO - implement this - related to ordering of filesets
|
64
|
+
|
65
|
+
end
|
66
|
+
update(status: "complete")
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def fail!
|
72
|
+
update(status: "failed")
|
73
|
+
end
|
74
|
+
|
75
|
+
def wait!
|
76
|
+
update(status: "pending")
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module BulkOps::SearchBuilderBehavior
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
included do
|
4
|
+
attr_reader :collection,
|
5
|
+
:admin_set,
|
6
|
+
:workflow_state
|
7
|
+
class_attribute :collection_field,
|
8
|
+
:collection_id_field,
|
9
|
+
:admin_set_field,
|
10
|
+
:admin_set_id_field,
|
11
|
+
:workflow_state_field,
|
12
|
+
:workflow_state_id_field,
|
13
|
+
:keyword_field
|
14
|
+
self.collection_field = 'member_of_collections_ssim'
|
15
|
+
self.collection_id_field = 'member_of_collection_ids_ssim'
|
16
|
+
self.admin_set_field = 'admin_set_tesim'
|
17
|
+
self.admin_set_id_field = 'isPartOf_ssim'
|
18
|
+
self.workflow_state_field = 'workflow_state_name_ssim'
|
19
|
+
self.keyword_field = 'all_fields'
|
20
|
+
|
21
|
+
self.default_processor_chain += [:member_of_collection,
|
22
|
+
:member_of_admin_set,
|
23
|
+
:in_workflow_state,
|
24
|
+
:with_keyword_query]
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param [scope] Typically the controller object
|
28
|
+
def initialize(scope: {},
|
29
|
+
collection: nil,
|
30
|
+
collection_id: nil,
|
31
|
+
admin_set: nil,
|
32
|
+
admin_set_id: nil,
|
33
|
+
workflow_state: nil,
|
34
|
+
keyword_query: nil)
|
35
|
+
|
36
|
+
@collection = collection unless collection.blank?
|
37
|
+
@admin_set = admin_set unless admin_set.blank?
|
38
|
+
@admin_set_id = admin_set_id unless admin_set_id.blank?
|
39
|
+
@workflow_state = workflow_state unless workflow_state.blank?
|
40
|
+
@collection_id = collection_id unless collection_id.blank?
|
41
|
+
@workflow_state = workflow_state unless workflow_state.blank?
|
42
|
+
@keyword_query = keyword_query unless keyword_query.blank?
|
43
|
+
super(scope)
|
44
|
+
end
|
45
|
+
|
46
|
+
def models
|
47
|
+
[Work,Course,Lecture]
|
48
|
+
end
|
49
|
+
|
50
|
+
# include filters into the query to only include the collection memebers
|
51
|
+
def member_of_collection(solr_parameters)
|
52
|
+
solr_parameters[:fq] ||= []
|
53
|
+
solr_parameters[:fq] << "#{collection_field}:#{@collection}" if @collection
|
54
|
+
solr_parameters[:fq] << "#{collection_id_field}:#{@collection_id}" if @collection_id
|
55
|
+
end
|
56
|
+
|
57
|
+
# include filters into the query to only include the collection memebers
|
58
|
+
def member_of_admin_set(solr_parameters)
|
59
|
+
solr_parameters[:fq] ||= []
|
60
|
+
solr_parameters[:fq] << "#{admin_set_field}:#{@admin_set}" if @admin_set
|
61
|
+
solr_parameters[:fq] << "#{admin_set_id_field}:#{@admin_set_id}" if @admin_set_id
|
62
|
+
end
|
63
|
+
|
64
|
+
# include filters into the query to only include the collection memebers
|
65
|
+
def in_workflow_state(solr_parameters)
|
66
|
+
solr_parameters[:fq] ||= []
|
67
|
+
solr_parameters[:fq] << "#{workflow_state_field}:#{@workflow_state}" if @workflow_state
|
68
|
+
end
|
69
|
+
|
70
|
+
def with_keyword_query(solr_parameters)
|
71
|
+
if @keyword_query
|
72
|
+
solr_parameters[:q] ||= []
|
73
|
+
# solr_parameters[:q] << "#{keyword_field}:#{@keyword_query}" if @keyword_query
|
74
|
+
solr_parameters[:q] << @keyword_query
|
75
|
+
solr_parameters[:qf] = "title_tesim titleAlternative_tesim subseries_tesim creator_label_tesim contributor_label_tesim originalPublisher_tesim publisher_tesim publisherHomepage_tesim resourceType_label_tesim rightsHolder_label_tesim scale_tesim series_tesim source_tesim staffNote_tesim coordinates_tesim subjectName_label_tesim subjectPlace_label_tesim subjectTemporal_label_tesim subjectTopic_label_tesim dateCreated_tesim dateCreatedDisplay_tesim dateDigitized_tesim datePublished_tesim description_tesim physicalFormat_label_tesim keyword_tesim language_label_tesim license_tesim masterFilename_tesim physicalDescription_tesim accessRights_tesim itemCallNumber_tesim collectionCallNumber_tesim donorProvenance_tesim genre_label_tesim boxFolder_tesim subject_label_tesim file_format_tesim all_text_timv"
|
76
|
+
end
|
77
|
+
solr_parameters
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
This is a readme file for the specific branch. The whole repository's readme file has different information. I'm not sure if we need both, since i haven't written either yet.
|
@@ -0,0 +1,210 @@
|
|
1
|
+
module BulkOps
|
2
|
+
module Verification
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def verify
|
6
|
+
@verification_errors ||= []
|
7
|
+
verify_column_headers
|
8
|
+
verify_remote_urls
|
9
|
+
verify_internal_references
|
10
|
+
verify_files
|
11
|
+
verify_works_to_update if operation_type.to_s == "update"
|
12
|
+
unless @verification_errors.blank?
|
13
|
+
error_file_name = BulkOps::Error.write_errors!(@verification_errors, git)
|
14
|
+
#notify everybody
|
15
|
+
notify(subject: "Errors verifying bulk #{operation_type} in Hycruz", message: "Hyrax ran a verification step to make sure that the spreadsheet for this bulk #{operation_type} is formatted correctly and won't create any errors. We found some problems. You can see a summary of the issues at this url: https://github.com/#{git.repo}/blob/#{git.name}/#{git.name}/errors/#{error_file_name}. Please fix these problems and run this verification again. The bulk #{operation_type} will not be allowed to move forward until all verification issues are resolved.")
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
|
21
|
+
def notify(subject: , message:)
|
22
|
+
options["notifications"].each do |email|
|
23
|
+
ActionMailer::Base.mail(from: "admin@digitalcollections.library.ucsc.edu",
|
24
|
+
to: email,
|
25
|
+
subject: subject,
|
26
|
+
body: message).deliver
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def is_file_field?(fieldname)
|
31
|
+
return false if fieldname.blank?
|
32
|
+
field_parts = fieldname.underscore.humanize.downcase.gsub(/[-_]/,' ').split(" ")
|
33
|
+
return false unless field_parts.any?{ |field_type| BulkOps::WorkProxy::FILE_FIELDS.include?(field_type) }
|
34
|
+
return "remove" if field_parts.any?{ |field_type| ['remove','delete'].include?(field_type) }
|
35
|
+
return "add"
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_field_name(fieldname)
|
39
|
+
name = fieldname.dup
|
40
|
+
name.gsub!(/[_\s-]?[aA]ttributes$/,'')
|
41
|
+
name.gsub!(/[_\s-]?[lL]abel$/,'')
|
42
|
+
name.gsub!(/^[rR]emove[_\s-]?/,'')
|
43
|
+
name.gsub!(/^[dD]elete[_\s-]?/,'')
|
44
|
+
possible_fields = Work.attribute_names + schema.all_field_names
|
45
|
+
matching_fields = possible_fields.select{|pfield| pfield.gsub(/[_\s-]/,'').parameterize == name.gsub(/[_\s-]/,'').parameterize }
|
46
|
+
return false if matching_fields.blank?
|
47
|
+
# raise Exception "Ambiguous metadata fields!" if matching_fields.uniq.count > 1
|
48
|
+
return matching_fields.first
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_file_paths(filestring)
|
52
|
+
return [] if filestring.blank?
|
53
|
+
filenames = filestring.split(BulkOps::WorkProxy::SEPARATOR)
|
54
|
+
filenames.map { |filename| File.join(BulkOps::Operation::INGEST_MEDIA_PATH, options['file_prefix'] || "", filename) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def record_exists? id
|
58
|
+
begin
|
59
|
+
return true if SolrDocument.find(id)
|
60
|
+
rescue Blacklight::Exceptions::RecordNotFound
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def verify_files
|
68
|
+
file_errors = []
|
69
|
+
get_spreadsheet.each_with_index do |row, row_num|
|
70
|
+
file_fields = row.select { |field, value| is_file_field?(field) }
|
71
|
+
file_fields.each do |column_name, filestring|
|
72
|
+
next if filestring.blank? or column_name == filestring
|
73
|
+
get_file_paths(filestring).each do |filepath|
|
74
|
+
file_errors << BulkOps::Error.new({type: :cannot_find_file, file: filepath}) unless File.file? filepath
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
@verification_errors.concat file_errors
|
79
|
+
return file_errors
|
80
|
+
end
|
81
|
+
|
82
|
+
def verify_configuration
|
83
|
+
BulkOps::Operation::OPTION_REQUIREMENTS.each do |option_name, option_info|
|
84
|
+
# Make sure it's present if required
|
85
|
+
if (option_info["required"].to_s == "true") || (option_info["required"].to_s == type)
|
86
|
+
if options[option_name].blank?
|
87
|
+
@verification_errors << BulkOps::Error.new({type: :missing_required_option, option_name: option_name})
|
88
|
+
end
|
89
|
+
end
|
90
|
+
# Make sure the values are acceptable if present
|
91
|
+
unless (values = option_info.values).blank? || options[option_name].blank?
|
92
|
+
unless values.include? option[option_name]
|
93
|
+
values_string = values.reduce{|a,b| "#{a}, #{b}"}
|
94
|
+
@verification_errors << BulkOps::Error.new({type: :invalid_config_value, option_name: option_name, option_values: values_string})
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def downcase_first_letter(str)
|
101
|
+
str[0].downcase + str[1..-1]
|
102
|
+
end
|
103
|
+
|
104
|
+
# Make sure the headers in the spreadsheet are matching to properties
|
105
|
+
def verify_column_headers
|
106
|
+
|
107
|
+
unless (headers = get_spreadsheet.headers)
|
108
|
+
# log an error if we can't get the metadata headers
|
109
|
+
@verification_errors << BulkOps::Error.new({type: :bad_header, field: column_name})
|
110
|
+
end
|
111
|
+
|
112
|
+
headers.each do |column_name|
|
113
|
+
next if column_name.blank?
|
114
|
+
column_name_redux = column_name.downcase.parameterize.gsub(/[_\s-]/,"")
|
115
|
+
# Ignore everything marked as a label
|
116
|
+
next if column_name_redux.ends_with? "label"
|
117
|
+
# Ignore any column names with special meaning in hyrax
|
118
|
+
next if BulkOps::Operation::SPECIAL_COLUMNS.any?{|col| col.downcase.parameterize.gsub(/[_\s-]/,"") == column_name_redux }
|
119
|
+
# Ignore any columns speficied to be ignored in the configuration
|
120
|
+
ignored = options["ignored headers"] || []
|
121
|
+
next if ignored.any?{|col| col.downcase.parameterize.gsub(/[_\s-]/,"") == column_name_redux }
|
122
|
+
# Column names corresponding to work attributes are legit
|
123
|
+
next if Work.attribute_names.any?{|col| col.downcase.parameterize.gsub(/[_\s-]/,"") == column_name_redux }
|
124
|
+
@verification_errors << BulkOps::Error.new({type: :bad_header, field: column_name})
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def verify_remote_urls
|
129
|
+
get_spreadsheet.each do |row, row_num|
|
130
|
+
schema.controlled_field_names.each do |controlled_field_name|
|
131
|
+
next unless (url = row[controlled_field_name])
|
132
|
+
label = ::WorkIndexer.fetch_remote_label(url)
|
133
|
+
if !label || label.blank?
|
134
|
+
@verification_errors << BulkOps::Error.new({type: :cannot_retrieve_label, row: row_num + ROW_OFFSET, field: controlled_field_name, url: url})
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_id_from_row row
|
141
|
+
ref_id = get_ref_id(row).to_sym
|
142
|
+
return :id if ref_id == :id
|
143
|
+
normrow = row.mapgsub(//,'').parameterize
|
144
|
+
if row.key?(ref_id)
|
145
|
+
|
146
|
+
# TODO if ref_id is another column
|
147
|
+
# TODO implement solr search
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def verify_works_to_update
|
153
|
+
return [] unless operation_type == "update"
|
154
|
+
get_spreadsheet.each_with_index do |row, row_num|
|
155
|
+
id = get_ref_id(row)
|
156
|
+
#TODO: find by other field. for now just id
|
157
|
+
unless (record_exists(id))
|
158
|
+
@verification_errors << BulkOps::Error.new(type: :cannot_find_work, id: id)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def get_ref_id row
|
164
|
+
row.each do |field,value|
|
165
|
+
next if field.blank? or value.blank? or field === value
|
166
|
+
next unless BulkOps::WorkProxy::REFERENCE_IDENTIFIER_FIELDS.any?{ |ref_field| normalize_field(ref_field) == normalize_field(field) }
|
167
|
+
return value
|
168
|
+
end
|
169
|
+
# No reference identifier specified in the row. Use the default for the operation.
|
170
|
+
return reference_identifier || :id
|
171
|
+
end
|
172
|
+
|
173
|
+
def normalize_field field
|
174
|
+
return '' if field.nil?
|
175
|
+
field.downcase.parameterize.gsub(/[_\s-]/,'')
|
176
|
+
end
|
177
|
+
|
178
|
+
def verify_internal_references
|
179
|
+
# TODO
|
180
|
+
# This is sketchy. Redo it.
|
181
|
+
get_spreadsheet.each do |row,row_num|
|
182
|
+
ref_id = get_ref_id(row)
|
183
|
+
BulkOps::Operation::RELATIONSHIP_COLUMNS.each do |relationship|
|
184
|
+
next unless (obj_id = row[relationship])
|
185
|
+
if (split = obj_id.split(':')).count == 2
|
186
|
+
ref_id = split[0].downcase
|
187
|
+
obj_id = split[1]
|
188
|
+
end
|
189
|
+
|
190
|
+
if ref_id == "row" || (ref_id == "id/row" && obj_id.is_a?(Integer))
|
191
|
+
# This is a row number reference. It should be an integer in the range of possible row numbers.
|
192
|
+
unless obj_id.is_a? Integer && obj_id > 0 && obj_id <= metadata.count
|
193
|
+
@verification_errors << BulkOps::Error.new({type: :bad_object_reference, object_id: obj_id, row_number: row_num + ROW_OFFSET})
|
194
|
+
end
|
195
|
+
elsif ref_id == "id" || ref_id == "hyrax id" || (ref_id == "id/row" && (obj_id.is_a? Integer))
|
196
|
+
# This is a hydra id reference. It should correspond to an object already in the repo
|
197
|
+
unless record_exists?(obj_id)
|
198
|
+
@verification_errors << BulkOps::Error.new({type: :bad_object_reference, object_id: obj_id, row_number: row_num+ROW_OFFSET})
|
199
|
+
end
|
200
|
+
else
|
201
|
+
|
202
|
+
# This must be based on some other presumably unique field in hyrax, or a dummy field in the spreadsheet. We haven't added this functionality yet. Ignore for now.
|
203
|
+
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#require 'hydra/access_controls'
|
2
|
+
#require 'hyrax/workflow/activate_object'
|
3
|
+
|
4
|
+
class BulkOps::VerificationJob < ActiveJob::Base
|
5
|
+
|
6
|
+
attr_accessor :operation
|
7
|
+
|
8
|
+
queue_as :default
|
9
|
+
|
10
|
+
def perform(operation)
|
11
|
+
|
12
|
+
if operation.verify
|
13
|
+
operation.set_stage "authorize"
|
14
|
+
if operation.create_pull_request
|
15
|
+
operation.notify(subject: "Bulk Operation Verification Successful", message: "Your bulk ingest has passed verification, and we have requested to start applying the operation. It may required one final approval from an administrator before the operation proceeds.")
|
16
|
+
else
|
17
|
+
operation.notify(subject: "Bulk Operation - Error creating Github pull request", message: "Your bulk ingest has passed verification, but we had a problem creating a pull request on Github in order to merge this operation with the master branch. Please check your github configuration.")
|
18
|
+
end
|
19
|
+
else
|
20
|
+
operation.set_stage "pending"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#require 'hydra/access_controls'
|
2
|
+
#require 'hyrax/workflow/activate_object'
|
3
|
+
|
4
|
+
class BulkOps::WorkJob < ActiveJob::Base
|
5
|
+
attr_accessor :status, :work, :type
|
6
|
+
|
7
|
+
queue_as :ingest
|
8
|
+
|
9
|
+
after_perform do |job|
|
10
|
+
|
11
|
+
# update BulkOperationsWorkProxy status
|
12
|
+
@work ||= ActiveFedora.find(@work_proxy.work_id)
|
13
|
+
if @work.id.nil?
|
14
|
+
status = "error"
|
15
|
+
else
|
16
|
+
@work_proxy.work_id = @work.id
|
17
|
+
status = "complete"
|
18
|
+
end
|
19
|
+
update_status status
|
20
|
+
|
21
|
+
# Attempt to resolve all of the relationships defined in this row
|
22
|
+
@work_proxy.relationships.each do |relationship|
|
23
|
+
relationship.resolve!
|
24
|
+
end
|
25
|
+
|
26
|
+
# Attempt to resolve each dangling (objectless) relationships using
|
27
|
+
# this work as an object
|
28
|
+
BulkOps::Relationship.where(:status => "objectless").each do |relationship|
|
29
|
+
relationship.resolve! @work.id
|
30
|
+
end
|
31
|
+
|
32
|
+
# Delete any UploadedFiles. These take up tons of unnecessary disk space.
|
33
|
+
@work.file_sets.each do |fileset|
|
34
|
+
if uf = Hyrax::UploadedFile.find_by(file: fileset.label)
|
35
|
+
uf.destroy!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Remove any edit holds placed on an item
|
40
|
+
@work_proxy.lift_hold
|
41
|
+
|
42
|
+
# Check if the parent operation is finished
|
43
|
+
# and do any cleanup if so
|
44
|
+
@work_proxy.operation.check_if_finished
|
45
|
+
end
|
46
|
+
|
47
|
+
def perform(workClass,user_email,attributes,work_proxy_id,visibility="private")
|
48
|
+
update_status "starting", "Initializing the job"
|
49
|
+
@work_proxy = BulkOps::WorkProxy.find(work_proxy_id)
|
50
|
+
unless @work_proxy
|
51
|
+
report_error("Cannot find work proxy with id: #{work_proxy_id}")
|
52
|
+
return
|
53
|
+
end
|
54
|
+
if record_exists?(@work_proxy.work_id)
|
55
|
+
# The work exists in Solr. Presumably we're updating it.
|
56
|
+
# Report an error if we can't retrieve the work from Fedora.
|
57
|
+
begin
|
58
|
+
@work = ActiveFedora::Base.find(@work_proxy.work_id)
|
59
|
+
rescue ActiveFedora::ObjectNotFoundError
|
60
|
+
report_error "Could not find work to update in Fedora (though it shows up in Solr). Work id: #{@work_proxy.work_id}"
|
61
|
+
return
|
62
|
+
end
|
63
|
+
else # The work is not found in Solr. If we're trying to update a work, we're in trouble.
|
64
|
+
if (type == "update")
|
65
|
+
report_error "Could not find work to update with id: #{@work_proxy.work_id}"
|
66
|
+
return
|
67
|
+
end
|
68
|
+
# Create the work we are ingesting
|
69
|
+
@work = workClass.capitalize.constantize.new
|
70
|
+
end
|
71
|
+
user = User.find_by_email(user_email)
|
72
|
+
update_status "running", "Started background task at #{DateTime.now.strftime("%d/%m/%Y %H:%M")}"
|
73
|
+
ability = Ability.new(user)
|
74
|
+
env = Hyrax::Actors::Environment.new(@work, ability, attributes)
|
75
|
+
update_status "complete", Hyrax::CurationConcern.actor.send(type,env)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def record_exists? id
|
81
|
+
begin
|
82
|
+
return true if SolrDocument.find(id)
|
83
|
+
rescue Blacklight::Exceptions::RecordNotFound
|
84
|
+
return false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def report_error message=nil
|
89
|
+
update_status "job_error", message: message
|
90
|
+
end
|
91
|
+
|
92
|
+
def type
|
93
|
+
#override this, setting as ingest by default
|
94
|
+
:create
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_status status, message=false
|
98
|
+
return false unless @work_proxy
|
99
|
+
atts = {status: status}
|
100
|
+
atts[:message] = message if message
|
101
|
+
@work_proxy.update(atts)
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|