ddr-batch 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +12 -0
- data/README.md +100 -0
- data/Rakefile +36 -0
- data/app/jobs/ddr/batch/batch_processor_job.rb +21 -0
- data/app/mailers/ddr/batch/batch_processor_run_mailer.rb +19 -0
- data/app/models/ddr/batch/batch.rb +72 -0
- data/app/models/ddr/batch/batch_ability_definitions.rb +14 -0
- data/app/models/ddr/batch/batch_object.rb +297 -0
- data/app/models/ddr/batch/batch_object_attribute.rb +57 -0
- data/app/models/ddr/batch/batch_object_datastream.rb +23 -0
- data/app/models/ddr/batch/batch_object_relationship.rb +26 -0
- data/app/models/ddr/batch/ingest_batch_object.rb +118 -0
- data/app/models/ddr/batch/update_batch_object.rb +94 -0
- data/app/scripts/ddr/batch/batch_processor.rb +151 -0
- data/app/views/ddr/batch/batch_processor_run_mailer/send_notification.html.erb +34 -0
- data/app/views/ddr/batch/batch_processor_run_mailer/send_notification.text.erb +20 -0
- data/config/locales/en.yml +52 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20150828183839_create_batches.rb +25 -0
- data/db/migrate/20150828201857_create_batch_objects.rb +18 -0
- data/db/migrate/20150828202118_create_batch_object_attributes.rb +16 -0
- data/db/migrate/20150828202200_create_batch_object_datastreams.rb +17 -0
- data/db/migrate/20150828202240_create_batch_object_relationships.rb +15 -0
- data/lib/ddr-batch.rb +1 -0
- data/lib/ddr/batch.rb +17 -0
- data/lib/ddr/batch/batch_user.rb +10 -0
- data/lib/ddr/batch/engine.rb +14 -0
- data/lib/ddr/batch/version.rb +5 -0
- data/lib/tasks/ddr_batch_tasks.rake +4 -0
- metadata +228 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: da0b053081d66d3ec2124f3ff59ad25154475822
|
4
|
+
data.tar.gz: 5be40e70e6c0233ea0f0abe6533743e03f1dfa11
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9d891c9ece98cd96791fa342bfb11c0824b550618a9a562551984cbdbd2449d526a95fae120ea609f9ae2f28d90ca4a9e765da3c56af4f2a61c8154b9b873b1d
|
7
|
+
data.tar.gz: faed3d3c6dcd10550441aabf4641555f7c4339dff93354ccf5a822c7c529a6931b9cd50c41945677fb2dece04b3d05995c288a4dade814907dbc823c1b9ce7ae
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Copyright (c) 2015, Duke University
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
7
|
+
|
8
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
9
|
+
|
10
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
11
|
+
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# ddr-batch
|
2
|
+
|
3
|
+
A Rails engine providing batch processing functionality for the Duke Digital Repository.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'ddr-batch'
|
10
|
+
|
11
|
+
and
|
12
|
+
|
13
|
+
bundle install
|
14
|
+
|
15
|
+
## Configuration
|
16
|
+
|
17
|
+
### User model
|
18
|
+
|
19
|
+
Include `Ddr::Batch::BatchUser` in `app/models/user.rb`.
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class User < ActiveRecord::Base
|
23
|
+
|
24
|
+
# DO NOT REMOVE:
|
25
|
+
# Blacklight::User
|
26
|
+
# Ddr::Auth::User
|
27
|
+
#
|
28
|
+
include Ddr::Batch::BatchUser
|
29
|
+
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
### Ability class
|
34
|
+
|
35
|
+
Add `Ddr::Batch::BatchAbilityDefinitions` to the list of `ability_definitions`.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class Ability < Ddr::Auth::Ability
|
39
|
+
|
40
|
+
self.ability_definitions += [ Ddr::Batch::BatchAbilityDefinitions ]
|
41
|
+
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
### Log4r
|
46
|
+
|
47
|
+
#### Application.rb
|
48
|
+
|
49
|
+
Add the following lines to `config\application.rb` if they are not already there:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'log4r'
|
53
|
+
require 'log4r/yamlconfigurator'
|
54
|
+
require 'log4r/outputter/datefileoutputter'
|
55
|
+
include Log4r
|
56
|
+
```
|
57
|
+
|
58
|
+
#### Configuration
|
59
|
+
|
60
|
+
`Ddr::Batch::BatchProcessor` expects a Log4r configuration file at `config\log4r_batch_processor.yml`.
|
61
|
+
|
62
|
+
##### Example
|
63
|
+
|
64
|
+
```yaml
|
65
|
+
log4r_config:
|
66
|
+
loggers:
|
67
|
+
- name : batch_processor
|
68
|
+
level : DEBUG
|
69
|
+
trace : 'false'
|
70
|
+
outputters:
|
71
|
+
- logfile
|
72
|
+
outputters:
|
73
|
+
- type : StdoutOutputter
|
74
|
+
name : stdout
|
75
|
+
level : DEBUG
|
76
|
+
formatter :
|
77
|
+
date_pattern: '%F %T.%L'
|
78
|
+
pattern : '%d %l: %m'
|
79
|
+
type : PatternFormatter
|
80
|
+
- type : FileOutputter
|
81
|
+
name : logfile
|
82
|
+
trunc : 'false'
|
83
|
+
filename : "#{LOG_FILE}"
|
84
|
+
formatter :
|
85
|
+
date_pattern: '%F %T.%L'
|
86
|
+
pattern : '%d %l: %m'
|
87
|
+
type : PatternFormatter
|
88
|
+
```
|
89
|
+
|
90
|
+
### Migrations
|
91
|
+
|
92
|
+
Install the ddr-batch migrations:
|
93
|
+
|
94
|
+
`rake ddr_batch:install:migrations`
|
95
|
+
|
96
|
+
then
|
97
|
+
|
98
|
+
`rake db:migrate`
|
99
|
+
|
100
|
+
`rake db:test:prepare`
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
Bundler::GemHelper.install_tasks
|
8
|
+
|
9
|
+
APP_ROOT = File.dirname(__FILE__)
|
10
|
+
|
11
|
+
JETTY_CONFIG = {
|
12
|
+
jetty_home: File.join(APP_ROOT, "jetty"),
|
13
|
+
jetty_port: 8983,
|
14
|
+
startup_wait: 60,
|
15
|
+
quiet: true,
|
16
|
+
java_opts: ["-Xmx256m", "-XX:MaxPermSize=128m"]
|
17
|
+
}
|
18
|
+
|
19
|
+
require 'jettywrapper'
|
20
|
+
|
21
|
+
Jettywrapper.instance.base_path = APP_ROOT
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
24
|
+
load 'rails/tasks/engine.rake'
|
25
|
+
|
26
|
+
require 'rspec/core/rake_task'
|
27
|
+
|
28
|
+
desc "Run all specs in spec directory"
|
29
|
+
RSpec::Core::RakeTask.new(:spec => ["app:db:migrate", "app:db:test:prepare"])
|
30
|
+
|
31
|
+
desc "Run the CI build"
|
32
|
+
task :ci => ["jetty:clean"] do
|
33
|
+
Jettywrapper.wrap(JETTY_CONFIG) do
|
34
|
+
Rake::Task['spec'].invoke
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Ddr::Batch
|
2
|
+
class BatchProcessorJob
|
3
|
+
@queue = :batch
|
4
|
+
|
5
|
+
def self.perform(batch_id, operator_id)
|
6
|
+
ts = Time.now.strftime("%Y%m%d%H%M%S%L")
|
7
|
+
logfile = "batch_processor_#{ts}_log.txt"
|
8
|
+
batch = Batch.find(batch_id)
|
9
|
+
operator = User.find(operator_id)
|
10
|
+
bp = BatchProcessor.new(batch, operator, log_file: logfile)
|
11
|
+
bp.execute
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.after_enqueue_set_status(batch_id, operator_id)
|
15
|
+
batch = Batch.find(batch_id)
|
16
|
+
batch.status = Batch::STATUS_QUEUED
|
17
|
+
batch.save
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ddr::Batch
|
2
|
+
|
3
|
+
class BatchProcessorRunMailer < ActionMailer::Base
|
4
|
+
|
5
|
+
default :from => "noreply@duke.edu"
|
6
|
+
|
7
|
+
def send_notification(batch)
|
8
|
+
@batch = batch
|
9
|
+
@title = "Batch Processor Run #{@batch.status}"
|
10
|
+
@host = `uname -n`.strip
|
11
|
+
@subject = "[#{@host}] #{@title}"
|
12
|
+
from = "#{`echo $USER`.strip}@#{@host}"
|
13
|
+
attachments["details.txt"] = File.read(@batch.logfile.path)
|
14
|
+
mail(from: from, to: @batch.user.email, subject: @subject)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Ddr::Batch
|
2
|
+
|
3
|
+
class Batch < ActiveRecord::Base
|
4
|
+
belongs_to :user, :inverse_of => :batches, class_name: ::User
|
5
|
+
has_many :batch_objects, -> { order("id ASC") }, :inverse_of => :batch, :dependent => :destroy
|
6
|
+
has_attached_file :logfile
|
7
|
+
do_not_validate_attachment_file_type :logfile
|
8
|
+
|
9
|
+
OUTCOME_SUCCESS = "SUCCESS"
|
10
|
+
OUTCOME_FAILURE = "FAILURE"
|
11
|
+
|
12
|
+
STATUS_READY = "READY"
|
13
|
+
STATUS_VALIDATING = "VALIDATING"
|
14
|
+
STATUS_INVALID = "INVALID"
|
15
|
+
STATUS_VALIDATED = "VALIDATED"
|
16
|
+
STATUS_QUEUED = "QUEUED"
|
17
|
+
STATUS_PROCESSING = "PROCESSING"
|
18
|
+
STATUS_RUNNING = "RUNNING"
|
19
|
+
STATUS_FINISHED = "FINISHED"
|
20
|
+
STATUS_INTERRUPTED = "INTERRUPTED"
|
21
|
+
STATUS_RESTARTABLE = "INTERRUPTED - RESTARTABLE"
|
22
|
+
|
23
|
+
def validate
|
24
|
+
errors = []
|
25
|
+
begin
|
26
|
+
batch_objects.each do |object|
|
27
|
+
unless object.verified
|
28
|
+
errors << object.validate
|
29
|
+
end
|
30
|
+
end
|
31
|
+
rescue Exception => e
|
32
|
+
errors << "Exception raised during batch validation: #{e.backtrace}"
|
33
|
+
end
|
34
|
+
errors.flatten
|
35
|
+
end
|
36
|
+
|
37
|
+
def completed_count
|
38
|
+
batch_objects.where(verified: true).count
|
39
|
+
end
|
40
|
+
|
41
|
+
def time_to_complete
|
42
|
+
unless processing_step_start.nil?
|
43
|
+
if completed_count > 0
|
44
|
+
completed = completed_count
|
45
|
+
((Time.now - processing_step_start.to_time) / completed) * (batch_objects.count - completed)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def found_pids
|
51
|
+
@found_pids ||= {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_found_pid(pid, model)
|
55
|
+
@found_pids[pid] = model
|
56
|
+
end
|
57
|
+
|
58
|
+
def pre_assigned_pids
|
59
|
+
@pre_assigned_pids ||= collect_pre_assigned_pids
|
60
|
+
end
|
61
|
+
|
62
|
+
def collect_pre_assigned_pids
|
63
|
+
batch_objects.map{ |x| x.pid if x.pid.present? }.compact
|
64
|
+
end
|
65
|
+
|
66
|
+
def finished?
|
67
|
+
status == STATUS_FINISHED
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Ddr::Batch
|
2
|
+
class BatchAbilityDefinitions < Ddr::Auth::AbilityDefinitions
|
3
|
+
|
4
|
+
def call
|
5
|
+
if authenticated?
|
6
|
+
can :manage, Batch, user_id: user.id
|
7
|
+
end
|
8
|
+
can :manage, Ddr::Batch::BatchObject do |batch_object|
|
9
|
+
can? :manage, batch_object.batch
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
module Ddr::Batch
|
2
|
+
|
3
|
+
# This is a superclass containing methods common to all batch object objects. It is not intended to be instantiated directly.
|
4
|
+
# This superclass and its subclasses are designed following the ActiveRecord single-table inheritance pattern.
|
5
|
+
class BatchObject < ActiveRecord::Base
|
6
|
+
|
7
|
+
belongs_to :batch, inverse_of: :batch_objects
|
8
|
+
has_many :batch_object_attributes, -> { order "id ASC" }, inverse_of: :batch_object, dependent: :destroy
|
9
|
+
has_many :batch_object_datastreams, inverse_of: :batch_object, dependent: :destroy
|
10
|
+
has_many :batch_object_relationships, inverse_of: :batch_object, dependent: :destroy
|
11
|
+
|
12
|
+
VERIFICATION_PASS = "PASS"
|
13
|
+
VERIFICATION_FAIL = "FAIL"
|
14
|
+
|
15
|
+
EVENT_SUMMARY = <<-EOS
|
16
|
+
%{label}
|
17
|
+
Batch object database id: %{batch_id}
|
18
|
+
Batch object identifier: %{identifier}
|
19
|
+
Model: %{model}
|
20
|
+
EOS
|
21
|
+
|
22
|
+
def self.pid_from_identifier(identifier, batch_id)
|
23
|
+
query = "identifier = :identifier"
|
24
|
+
query << " and batch_id = :batch_id" if batch_id
|
25
|
+
params = { :identifier => identifier }
|
26
|
+
params[:batch_id] = batch_id if batch_id
|
27
|
+
sort = "updated_at asc"
|
28
|
+
found_objects = BatchObject.where(query, params).order(sort)
|
29
|
+
pids = []
|
30
|
+
found_objects.each { |obj| pids << obj.pid }
|
31
|
+
return pids
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate
|
35
|
+
@error_prefix = I18n.t('ddr.batch.errors.prefix', :identifier => identifier, :id => id)
|
36
|
+
errors = []
|
37
|
+
errors += validate_model if model
|
38
|
+
errors += validate_datastreams if batch_object_datastreams
|
39
|
+
errors += validate_relationships if batch_object_relationships
|
40
|
+
errors += local_validations
|
41
|
+
return errors
|
42
|
+
end
|
43
|
+
|
44
|
+
def local_validations
|
45
|
+
[]
|
46
|
+
end
|
47
|
+
|
48
|
+
def model_datastream_keys
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
52
|
+
def process(user, opts = {})
|
53
|
+
raise NotImplementedError
|
54
|
+
end
|
55
|
+
|
56
|
+
def results_message
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
59
|
+
|
60
|
+
Results = Struct.new(:repository_object, :verified, :verifications)
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def validate_model
|
65
|
+
errs = []
|
66
|
+
begin
|
67
|
+
model.constantize
|
68
|
+
rescue NameError
|
69
|
+
errs << "#{@error_prefix} Invalid model name: #{model}"
|
70
|
+
end
|
71
|
+
return errs
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_datastreams
|
75
|
+
errs = []
|
76
|
+
batch_object_datastreams.each do |d|
|
77
|
+
if model_datastream_keys.present?
|
78
|
+
unless model_datastream_keys.include?(d.name)
|
79
|
+
errs << "#{@error_prefix} Invalid datastream name for #{model}: #{d.name}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
unless BatchObjectDatastream::PAYLOAD_TYPES.include?(d.payload_type)
|
83
|
+
errs << "#{@error_prefix} Invalid payload type for #{d.name} datastream: #{d.payload_type}"
|
84
|
+
end
|
85
|
+
if d.payload_type.eql?(BatchObjectDatastream::PAYLOAD_TYPE_FILENAME)
|
86
|
+
unless File.readable?(d.payload)
|
87
|
+
errs << "#{@error_prefix} Missing or unreadable file for #{d[:name]} datastream: #{d[:payload]}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
if d.checksum && !d.checksum_type
|
91
|
+
errs << "#{@error_prefix} Must specify checksum type if providing checksum for #{d.name} datastream"
|
92
|
+
end
|
93
|
+
if d.checksum_type
|
94
|
+
unless Ddr::Datastreams::CHECKSUM_TYPES.include?(d.checksum_type)
|
95
|
+
errs << "#{@error_prefix} Invalid checksum type for #{d.name} datastream: #{d.checksum_type}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
return errs
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_relationships
|
103
|
+
errs = []
|
104
|
+
batch_object_relationships.each do |r|
|
105
|
+
obj_model = nil
|
106
|
+
unless BatchObjectRelationship::OBJECT_TYPES.include?(r[:object_type])
|
107
|
+
errs << "#{@error_prefix} Invalid object_type for #{r[:name]} relationship: #{r[:object_type]}"
|
108
|
+
end
|
109
|
+
if r[:object_type].eql?(BatchObjectRelationship::OBJECT_TYPE_PID)
|
110
|
+
if batch.present? && batch.found_pids.keys.include?(r[:object])
|
111
|
+
obj_model = batch.found_pids[r[:object]]
|
112
|
+
else
|
113
|
+
begin
|
114
|
+
obj = ActiveFedora::Base.find(r[:object], :cast => true)
|
115
|
+
obj_model = obj.class.name
|
116
|
+
if batch.present?
|
117
|
+
batch.add_found_pid(obj.pid, obj_model)
|
118
|
+
end
|
119
|
+
rescue ActiveFedora::ObjectNotFoundError
|
120
|
+
pid_in_batch = false
|
121
|
+
if batch.present?
|
122
|
+
if batch.pre_assigned_pids.include?(r[:object])
|
123
|
+
pid_in_batch = true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
unless pid_in_batch
|
127
|
+
errs << "#{@error_prefix} #{r[:name]} relationship object does not exist: #{r[:object]}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
if obj_model
|
132
|
+
relationship_reflection = Ddr::Utils.relationship_object_reflection(model, r[:name])
|
133
|
+
if relationship_reflection
|
134
|
+
klass = Ddr::Utils.reflection_object_class(relationship_reflection)
|
135
|
+
if klass
|
136
|
+
errs << "#{@error_prefix} #{r[:name]} relationship object #{r[:object]} exists but is not a(n) #{klass}" unless batch.found_pids[r[:object]].eql?(klass.name)
|
137
|
+
end
|
138
|
+
else
|
139
|
+
errs << "#{@error_prefix} #{model} does not define a(n) #{r[:name]} relationship"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
return errs
|
145
|
+
end
|
146
|
+
|
147
|
+
def verify_repository_object
|
148
|
+
verifications = {}
|
149
|
+
begin
|
150
|
+
repo_object = ActiveFedora::Base.find(pid, :cast => true)
|
151
|
+
rescue ActiveFedora::ObjectNotFound
|
152
|
+
verifications["Object exists in repository"] = VERIFICATION_FAIL
|
153
|
+
else
|
154
|
+
verifications["Object exists in repository"] = VERIFICATION_PASS
|
155
|
+
verifications["Object is correct model"] = verify_model(repo_object) if model
|
156
|
+
verifications["Object has correct label"] = verify_label(repo_object) if label
|
157
|
+
unless batch_object_attributes.empty?
|
158
|
+
batch_object_attributes.each do |a|
|
159
|
+
if a.operation == BatchObjectAttribute::OPERATION_ADD
|
160
|
+
verifications["#{a.name} attribute set correctly"] = verify_attribute(repo_object, a)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
unless batch_object_datastreams.empty?
|
165
|
+
batch_object_datastreams.each do |d|
|
166
|
+
verifications["#{d.name} datastream present and not empty"] = verify_datastream(repo_object, d)
|
167
|
+
verifications["#{d.name} external checksum match"] = verify_datastream_external_checksum(repo_object, d) if d.checksum
|
168
|
+
end
|
169
|
+
end
|
170
|
+
unless batch_object_relationships.empty?
|
171
|
+
batch_object_relationships.each do |r|
|
172
|
+
verifications["#{r.name} relationship is correct"] = verify_relationship(repo_object, r)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
result = Ddr::Actions::FixityCheck.execute repo_object
|
176
|
+
verifications["Fixity check"] = result.success ? VERIFICATION_PASS : VERIFICATION_FAIL
|
177
|
+
end
|
178
|
+
verifications
|
179
|
+
end
|
180
|
+
|
181
|
+
def verify_model(repo_object)
|
182
|
+
begin
|
183
|
+
if repo_object.class.eql?(model.constantize)
|
184
|
+
return VERIFICATION_PASS
|
185
|
+
else
|
186
|
+
return VERIFICATION_FAIL
|
187
|
+
end
|
188
|
+
rescue NameError
|
189
|
+
return VERIFICATION_FAIL
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def verify_label(repo_object)
|
194
|
+
repo_object.label.eql?(label) ? VERIFICATION_PASS : VERIFICATION_FAIL
|
195
|
+
end
|
196
|
+
|
197
|
+
def verify_attribute(repo_object, attribute)
|
198
|
+
repo_object.datastreams[attribute.datastream].values(attribute.name).include?(attribute.value) ?
|
199
|
+
VERIFICATION_PASS : VERIFICATION_FAIL
|
200
|
+
end
|
201
|
+
|
202
|
+
def verify_datastream(repo_object, datastream)
|
203
|
+
if repo_object.datastreams.include?(datastream.name) &&
|
204
|
+
repo_object.datastreams[datastream.name].has_content?
|
205
|
+
VERIFICATION_PASS
|
206
|
+
else
|
207
|
+
VERIFICATION_FAIL
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def verify_datastream_external_checksum(repo_object, datastream)
|
212
|
+
repo_object.datastreams[datastream.name].validate_checksum! datastream.checksum, datastream.checksum_type
|
213
|
+
return VERIFICATION_PASS
|
214
|
+
rescue Ddr::Models::ChecksumInvalid
|
215
|
+
return VERIFICATION_FAIL
|
216
|
+
end
|
217
|
+
|
218
|
+
def verify_relationship(repo_object, relationship)
|
219
|
+
relationship_reflection = Ddr::Utils.relationship_object_reflection(model, relationship.name)
|
220
|
+
relationship_object_class = Ddr::Utils.reflection_object_class(relationship_reflection)
|
221
|
+
relationship_object = repo_object.send(relationship.name)
|
222
|
+
if !relationship_object.nil? &&
|
223
|
+
relationship_object.pid.eql?(relationship.object) &&
|
224
|
+
relationship_object.is_a?(relationship_object_class)
|
225
|
+
VERIFICATION_PASS
|
226
|
+
else
|
227
|
+
VERIFICATION_FAIL
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def add_attribute(repo_object, attribute)
|
232
|
+
repo_object.datastreams[attribute.datastream].add_value(attribute.name, attribute.value)
|
233
|
+
return repo_object
|
234
|
+
end
|
235
|
+
|
236
|
+
def clear_attribute(repo_object, attribute)
|
237
|
+
repo_object.datastreams[attribute.datastream].set_values(attribute.name, nil)
|
238
|
+
return repo_object
|
239
|
+
end
|
240
|
+
|
241
|
+
def clear_attributes(repo_object, attribute)
|
242
|
+
repo_object.datastreams[attribute.datastream].class.term_names.each do |term|
|
243
|
+
repo_object.datastreams[attribute.datastream].set_values(term, nil) if repo_object.datastreams[attribute.datastream].values(term)
|
244
|
+
end
|
245
|
+
return repo_object
|
246
|
+
end
|
247
|
+
|
248
|
+
def populate_datastream(repo_object, datastream)
|
249
|
+
case datastream[:payload_type]
|
250
|
+
when BatchObjectDatastream::PAYLOAD_TYPE_BYTES
|
251
|
+
ds_content = datastream[:payload]
|
252
|
+
if repo_object.datastreams[datastream[:name]].is_a? ActiveFedora::RDFDatastream
|
253
|
+
ds_content = set_rdf_subject(repo_object, ds_content)
|
254
|
+
end
|
255
|
+
repo_object.datastreams[datastream[:name]].content = ds_content
|
256
|
+
when BatchObjectDatastream::PAYLOAD_TYPE_FILENAME
|
257
|
+
if repo_object.datastreams[datastream[:name]].is_a? ActiveFedora::RDFDatastream
|
258
|
+
ds_content = set_rdf_subject(repo_object, File.read(datastream[:payload]))
|
259
|
+
mime_type = "application/n-triples"
|
260
|
+
else
|
261
|
+
ds_content = File.new(datastream[:payload])
|
262
|
+
end
|
263
|
+
file_name = File.basename(datastream[:payload])
|
264
|
+
dsid = datastream[:name]
|
265
|
+
opts = { filename: file_name }
|
266
|
+
opts.merge({ mime_type: mime_type }) if mime_type
|
267
|
+
repo_object.add_file(ds_content, dsid, opts)
|
268
|
+
end
|
269
|
+
return repo_object
|
270
|
+
end
|
271
|
+
|
272
|
+
def add_relationship(repo_object, relationship)
|
273
|
+
relationship_object = case relationship[:object_type]
|
274
|
+
when BatchObjectRelationship::OBJECT_TYPE_PID
|
275
|
+
ActiveFedora::Base.find(relationship[:object], :cast => true)
|
276
|
+
end
|
277
|
+
repo_object.send("#{relationship[:name]}=", relationship_object)
|
278
|
+
return repo_object
|
279
|
+
end
|
280
|
+
|
281
|
+
def set_rdf_subject(repo_object, ds_content)
|
282
|
+
graph = RDF::Graph.new
|
283
|
+
RDF::Reader.for(:ntriples).new(ds_content) do |reader|
|
284
|
+
reader.each_statement do |statement|
|
285
|
+
if statement.subject.is_a? RDF::Node
|
286
|
+
statement.subject = RDF::URI(repo_object.internal_uri)
|
287
|
+
end
|
288
|
+
graph.insert(statement)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
graph.dump :ntriples
|
292
|
+
end
|
293
|
+
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
end
|