ddr-batch 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +12 -0
  3. data/README.md +100 -0
  4. data/Rakefile +36 -0
  5. data/app/jobs/ddr/batch/batch_processor_job.rb +21 -0
  6. data/app/mailers/ddr/batch/batch_processor_run_mailer.rb +19 -0
  7. data/app/models/ddr/batch/batch.rb +72 -0
  8. data/app/models/ddr/batch/batch_ability_definitions.rb +14 -0
  9. data/app/models/ddr/batch/batch_object.rb +297 -0
  10. data/app/models/ddr/batch/batch_object_attribute.rb +57 -0
  11. data/app/models/ddr/batch/batch_object_datastream.rb +23 -0
  12. data/app/models/ddr/batch/batch_object_relationship.rb +26 -0
  13. data/app/models/ddr/batch/ingest_batch_object.rb +118 -0
  14. data/app/models/ddr/batch/update_batch_object.rb +94 -0
  15. data/app/scripts/ddr/batch/batch_processor.rb +151 -0
  16. data/app/views/ddr/batch/batch_processor_run_mailer/send_notification.html.erb +34 -0
  17. data/app/views/ddr/batch/batch_processor_run_mailer/send_notification.text.erb +20 -0
  18. data/config/locales/en.yml +52 -0
  19. data/config/routes.rb +2 -0
  20. data/db/migrate/20150828183839_create_batches.rb +25 -0
  21. data/db/migrate/20150828201857_create_batch_objects.rb +18 -0
  22. data/db/migrate/20150828202118_create_batch_object_attributes.rb +16 -0
  23. data/db/migrate/20150828202200_create_batch_object_datastreams.rb +17 -0
  24. data/db/migrate/20150828202240_create_batch_object_relationships.rb +15 -0
  25. data/lib/ddr-batch.rb +1 -0
  26. data/lib/ddr/batch.rb +17 -0
  27. data/lib/ddr/batch/batch_user.rb +10 -0
  28. data/lib/ddr/batch/engine.rb +14 -0
  29. data/lib/ddr/batch/version.rb +5 -0
  30. data/lib/tasks/ddr_batch_tasks.rake +4 -0
  31. 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