dor-services 4.8.3 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -6,6 +6,7 @@ module Dor
|
|
|
6
6
|
set_terminology do |t|
|
|
7
7
|
t.root :path => 'contentMetadata', :index_as => [:not_searchable]
|
|
8
8
|
t.contentType :path => '/contentMetadata/@type', :index_as => [:not_searchable]
|
|
9
|
+
t.stacks :path=> '/contentMetadata/@stacks', :index_as => [:not_searchable]
|
|
9
10
|
t.resource(:index_as => [:not_searchable]) do
|
|
10
11
|
t.id_ :path => { :attribute => 'id' }
|
|
11
12
|
t.sequence :path => { :attribute => 'sequence' }#, :data_type => :integer
|
data/lib/dor/models/shelvable.rb
CHANGED
|
@@ -7,21 +7,58 @@ module Dor
|
|
|
7
7
|
|
|
8
8
|
# Push file changes for shelve-able files into the stacks
|
|
9
9
|
def shelve
|
|
10
|
+
# retrieve the differences between the current contentMetadata and the previously ingested version
|
|
11
|
+
shelve_diff = get_shelve_diff
|
|
12
|
+
stacks_object_pathname = get_stacks_location
|
|
13
|
+
# determine the location of the object's files in the stacks area
|
|
14
|
+
stacks_druid = DruidTools::StacksDruid.new id, stacks_object_pathname
|
|
15
|
+
stacks_object_pathname = Pathname(stacks_druid.path)
|
|
16
|
+
# determine the location of the object's content files in the workspace area
|
|
17
|
+
workspace_druid = DruidTools::Druid.new(id,Config.stacks.local_workspace_root)
|
|
18
|
+
workspace_content_pathname = workspace_content_dir(shelve_diff, workspace_druid)
|
|
19
|
+
# delete, rename, or copy files to the stacks area
|
|
20
|
+
DigitalStacksService.remove_from_stacks(stacks_object_pathname, shelve_diff)
|
|
21
|
+
DigitalStacksService.rename_in_stacks(stacks_object_pathname, shelve_diff)
|
|
22
|
+
DigitalStacksService.shelve_to_stacks(workspace_content_pathname, stacks_object_pathname, shelve_diff)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# retrieve the differences between the current contentMetadata and the previously ingested version
|
|
26
|
+
# (filtering to select only the files that should be shelved to stacks)
|
|
27
|
+
def get_shelve_diff
|
|
10
28
|
inventory_diff_xml = self.get_content_diff(:shelve)
|
|
11
29
|
inventory_diff = Moab::FileInventoryDifference.parse(inventory_diff_xml)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if content_group_diff.rename_require_temp_files(deltas[:renamed])
|
|
16
|
-
triplets = content_group_diff.rename_tempfile_triplets(deltas[:renamed])
|
|
17
|
-
DigitalStacksService.rename_in_stacks self.pid, triplets.collect{|old,new,temp| [old,temp]}
|
|
18
|
-
DigitalStacksService.rename_in_stacks self.pid, triplets.collect{|old,new,temp| [temp,new]}
|
|
19
|
-
else
|
|
20
|
-
DigitalStacksService.rename_in_stacks self.pid, deltas[:renamed]
|
|
21
|
-
end
|
|
22
|
-
DigitalStacksService.shelve_to_stacks self.pid, deltas[:modified] + deltas[:added] + deltas[:copyadded].collect{|old,new| new}
|
|
23
|
-
DigitalStacksService.remove_from_stacks self.pid, deltas[:deleted] + deltas[:copydeleted]
|
|
30
|
+
shelve_diff = inventory_diff.group_difference("content")
|
|
31
|
+
shelve_diff
|
|
24
32
|
end
|
|
25
33
|
|
|
34
|
+
# Find the location of the object's content files in the workspace area
|
|
35
|
+
# @param [Moab::FileGroupDifference] content_diff The differences between the current contentMetadata and the previously ingested version
|
|
36
|
+
# @param [DruidTools::Druid] workspace_druid the location of the object's files in the workspace area
|
|
37
|
+
# @return [Pathname] The location of the object's content files in the workspace area
|
|
38
|
+
def workspace_content_dir (content_diff, workspace_druid)
|
|
39
|
+
deltas = content_diff.file_deltas
|
|
40
|
+
filelist = deltas[:modified] + deltas[:added] + deltas[:copyadded].collect{|old,new| new}
|
|
41
|
+
return nil if filelist.empty?
|
|
42
|
+
content_pathname = Pathname(workspace_druid.find_filelist_parent('content', filelist))
|
|
43
|
+
content_pathname
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# get the stack location based on the contentMetadata stacks attribute
|
|
48
|
+
# or using the default value from the config file if it doesn't exist
|
|
49
|
+
def get_stacks_location
|
|
50
|
+
|
|
51
|
+
contentMetadataDS = self.datastreams['contentMetadata']
|
|
52
|
+
unless contentMetadataDS.nil? or contentMetadataDS.stacks.length == 0
|
|
53
|
+
stacks_location = contentMetadataDS.stacks[0]
|
|
54
|
+
if stacks_location.start_with?"/" #Absolute stacks path
|
|
55
|
+
return stacks_location
|
|
56
|
+
else
|
|
57
|
+
raise "stacks attribute for item: "+self.id+ " contentMetadata should start with /. The current value is "+stacks_location
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
return Config.stacks.local_stacks_root #Default stacks
|
|
61
|
+
|
|
62
|
+
end
|
|
26
63
|
end
|
|
27
64
|
end
|
|
@@ -2,42 +2,128 @@ require 'net/ssh'
|
|
|
2
2
|
require 'net/sftp'
|
|
3
3
|
|
|
4
4
|
module Dor
|
|
5
|
+
|
|
5
6
|
class DigitalStacksService
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
# Delete files from stacks that have change type 'deleted', 'copydeleted', or 'modified'
|
|
9
|
+
# @param [Pathname] stacks_object_pathname the stacks location of the digital object
|
|
10
|
+
# @param [Moab::FileGroupDifference] content_diff the content file version differences report
|
|
11
|
+
def self.remove_from_stacks(stacks_object_pathname, content_diff)
|
|
12
|
+
[:deleted, :copydeleted, :modified].each do |change_type|
|
|
13
|
+
subset = content_diff.subset(change_type) # {Moab::FileGroupDifferenceSubset}
|
|
14
|
+
subset.files.each do |moab_file| # {Moab::FileInstanceDifference}
|
|
15
|
+
moab_signature = moab_file.signatures.first # {Moab::FileSignature}
|
|
16
|
+
file_pathname = stacks_object_pathname.join(moab_file.basis_path)
|
|
17
|
+
self.delete_file(file_pathname, moab_signature)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Delete a file, but only if it exists and matches the expected signature
|
|
23
|
+
# @param [Pathname] file_pathname The location of the file to be deleted
|
|
24
|
+
# @param [Moab::FileSignature] moab_signature The fixity values of the file
|
|
25
|
+
# @return [Boolean] true if file deleted, false otherwise
|
|
26
|
+
def self.delete_file(file_pathname, moab_signature)
|
|
27
|
+
if file_pathname.exist? and (file_pathname.size == moab_signature.size)
|
|
28
|
+
file_signature = Moab::FileSignature.new.signature_from_file(file_pathname)
|
|
29
|
+
if (file_signature == moab_signature)
|
|
30
|
+
file_pathname.delete
|
|
31
|
+
return true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
return false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Rename files from stacks that have change type 'renamed' using an intermediate temp filename.
|
|
38
|
+
# The 2-step renaming allows chained or cyclic renames to occur without file collisions.
|
|
39
|
+
# @param [Pathname] stacks_object_pathname the stacks location of the digital object
|
|
40
|
+
# @param [Moab::FileGroupDifference] content_diff the content file version differences report
|
|
41
|
+
def self.rename_in_stacks(stacks_object_pathname, content_diff)
|
|
42
|
+
subset = content_diff.subset(:renamed) # {Moab::FileGroupDifferenceSubset
|
|
43
|
+
|
|
44
|
+
# 1st Pass - rename files from original name to checksum-based name
|
|
45
|
+
subset.files.each do |moab_file| # {Moab::FileInstanceDifference}
|
|
46
|
+
moab_signature = moab_file.signatures.first # {Moab::FileSignature}
|
|
47
|
+
original_pathname = stacks_object_pathname.join(moab_file.basis_path)
|
|
48
|
+
temp_pathname = stacks_object_pathname.join(moab_signature.checksums.values.last)
|
|
49
|
+
self.rename_file(original_pathname, temp_pathname, moab_signature)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# 2nd Pass - rename files from checksum-based name to new name
|
|
53
|
+
subset.files.each do |moab_file| # {Moab::FileInstanceDifference}
|
|
54
|
+
moab_signature = moab_file.signatures.first # {Moab::FileSignature}
|
|
55
|
+
temp_pathname = stacks_object_pathname.join(moab_signature.checksums.values.last)
|
|
56
|
+
new_pathname = stacks_object_pathname.join(moab_file.other_path)
|
|
57
|
+
self.rename_file(temp_pathname, new_pathname, moab_signature)
|
|
58
|
+
end
|
|
59
|
+
|
|
11
60
|
end
|
|
12
61
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
62
|
+
# Rename a file, but only if it exists and has the expected signature
|
|
63
|
+
# @param [Pathname] old_pathname The original location/name of the file being renamed
|
|
64
|
+
# @param [Pathname] new_pathname The new location/name of the file
|
|
65
|
+
# @param [Moab::FileSignature] moab_signature The fixity values of the file
|
|
66
|
+
# @return [Boolean] true if file renamed, false otherwise
|
|
67
|
+
def self.rename_file(old_pathname, new_pathname, moab_signature)
|
|
68
|
+
if old_pathname.exist? and (old_pathname.size == moab_signature.size)
|
|
69
|
+
file_signature = Moab::FileSignature.new.signature_from_file(old_pathname)
|
|
70
|
+
if (file_signature == moab_signature)
|
|
71
|
+
new_pathname.parent.mkpath
|
|
72
|
+
old_pathname.rename(new_pathname)
|
|
73
|
+
return true
|
|
74
|
+
end
|
|
18
75
|
end
|
|
76
|
+
return false
|
|
19
77
|
end
|
|
20
78
|
|
|
21
|
-
#
|
|
22
|
-
# @param [
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
79
|
+
# Add files to stacks that have change type 'added', 'copyadded' or 'modified'.
|
|
80
|
+
# @param [Pathname] workspace_content_pathname The dor workspace location of the digital object's content fies
|
|
81
|
+
# @param [Pathname] stacks_object_pathname the stacks location of the digital object's shelved files
|
|
82
|
+
# @param [Moab::FileGroupDifference] content_diff the content file version differences report
|
|
83
|
+
def self.shelve_to_stacks(workspace_content_pathname, stacks_object_pathname, content_diff)
|
|
84
|
+
return false if workspace_content_pathname.nil?
|
|
85
|
+
[:added, :copyadded, :modified,].each do |change_type|
|
|
86
|
+
subset = content_diff.subset(change_type) # {Moab::FileGroupDifferenceSubset
|
|
87
|
+
subset.files.each do |moab_file| # {Moab::FileInstanceDifference}
|
|
88
|
+
moab_signature = moab_file.signatures.last # {Moab::FileSignature}
|
|
89
|
+
filename = (change_type == :modified) ? moab_file.basis_path : moab_file.other_path
|
|
90
|
+
workspace_pathname = workspace_content_pathname.join(filename)
|
|
91
|
+
stacks_pathname = stacks_object_pathname.join(filename)
|
|
92
|
+
self.copy_file(workspace_pathname, stacks_pathname, moab_signature)
|
|
93
|
+
end
|
|
30
94
|
end
|
|
95
|
+
true
|
|
31
96
|
end
|
|
32
97
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
98
|
+
# Copy a file to stacks, but only if it does not yet exist with the expected signature
|
|
99
|
+
# @param [Pathname] workspace_pathname The location of the file in the DOR workspace
|
|
100
|
+
# @param [Pathname] stacks_pathname The location of the file in the stacks
|
|
101
|
+
# @param [Moab::FileSignature] moab_signature The fixity values of the file
|
|
102
|
+
# @return [Boolean] true if file copied, false otherwise
|
|
103
|
+
def self.copy_file(workspace_pathname, stacks_pathname, moab_signature)
|
|
104
|
+
if stacks_pathname.exist?
|
|
105
|
+
file_signature = Moab::FileSignature.new.signature_from_file(stacks_pathname)
|
|
106
|
+
stacks_pathname.delete if (file_signature != moab_signature)
|
|
107
|
+
end
|
|
108
|
+
unless stacks_pathname.exist?
|
|
109
|
+
stacks_pathname.parent.mkpath
|
|
110
|
+
FileUtils.cp workspace_pathname.to_s, stacks_pathname.to_s
|
|
111
|
+
return true
|
|
40
112
|
end
|
|
113
|
+
return false
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
### depricated ???
|
|
117
|
+
|
|
118
|
+
# Create a file inside the content directory under the stacks.local_document_cache_root
|
|
119
|
+
# @param [String] id The druid identifier for the object
|
|
120
|
+
# @param [String] content The contents of the file to be created
|
|
121
|
+
# @param [String] filename The name of the file to be created
|
|
122
|
+
# @return [void]
|
|
123
|
+
def self.transfer_to_document_store(id, content, filename)
|
|
124
|
+
druid = DruidTools::PurlDruid.new id, Config.stacks.local_document_cache_root
|
|
125
|
+
druid.content_dir # create the druid tree if it doesn't exist yet
|
|
126
|
+
File.open(File.join(druid.content_dir, filename), 'w') { |f| f.write content }
|
|
41
127
|
end
|
|
42
128
|
|
|
43
129
|
# Assumes the digital stacks storage root is mounted to the local file system
|
data/lib/dor/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dor-services
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.9.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -13,7 +13,7 @@ authors:
|
|
|
13
13
|
autorequire:
|
|
14
14
|
bindir: bin
|
|
15
15
|
cert_chain: []
|
|
16
|
-
date: 2014-
|
|
16
|
+
date: 2014-07-01 00:00:00.000000000 Z
|
|
17
17
|
dependencies:
|
|
18
18
|
- !ruby/object:Gem::Dependency
|
|
19
19
|
name: active-fedora
|