libis-tools 0.9.20 → 0.9.21
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 +4 -4
- data/README.md +36 -233
- data/Rakefile +5 -0
- data/lib/libis/tools.rb +1 -0
- data/lib/libis/tools/assert.rb +11 -0
- data/lib/libis/tools/checksum.rb +22 -5
- data/lib/libis/tools/command.rb +24 -3
- data/lib/libis/tools/config.rb +61 -33
- data/lib/libis/tools/config_file.rb +0 -1
- data/lib/libis/tools/deep_struct.rb +10 -2
- data/lib/libis/tools/extend/empty.rb +2 -2
- data/lib/libis/tools/extend/hash.rb +37 -18
- data/lib/libis/tools/extend/kernel.rb +9 -0
- data/lib/libis/tools/extend/string.rb +17 -8
- data/lib/libis/tools/logger.rb +95 -44
- data/lib/libis/tools/metadata.rb +5 -1
- data/lib/libis/tools/metadata/dublin_core_record.rb +22 -4
- data/lib/libis/tools/metadata/field_format.rb +49 -9
- data/lib/libis/tools/metadata/fix_field.rb +5 -0
- data/lib/libis/tools/metadata/mapper.rb +2 -1
- data/lib/libis/tools/metadata/mappers/flandrica.rb +8 -1
- data/lib/libis/tools/metadata/mappers/kuleuven.rb +6 -2
- data/lib/libis/tools/metadata/marc21_record.rb +1 -0
- data/lib/libis/tools/metadata/marc_record.rb +31 -12
- data/lib/libis/tools/metadata/parser/basic_parser.rb +2 -0
- data/lib/libis/tools/metadata/parser/dublin_core_parser.rb +2 -1
- data/lib/libis/tools/metadata/parser/marc21_parser.rb +2 -1
- data/lib/libis/tools/metadata/parser/marc_format_parser.rb +2 -1
- data/lib/libis/tools/metadata/parser/marc_rules.rb +2 -1
- data/lib/libis/tools/metadata/parser/marc_select_parser.rb +2 -1
- data/lib/libis/tools/metadata/parser/patch.rb +1 -0
- data/lib/libis/tools/metadata/parser/subfield_criteria_parser.rb +2 -1
- data/lib/libis/tools/metadata/sharepoint_mapping.rb +1 -0
- data/lib/libis/tools/metadata/sharepoint_record.rb +2 -0
- data/lib/libis/tools/metadata/var_field.rb +8 -0
- data/lib/libis/tools/mets_dnx.rb +61 -0
- data/lib/libis/tools/mets_file.rb +87 -604
- data/lib/libis/tools/mets_objects.rb +534 -0
- data/lib/libis/tools/parameter.rb +144 -21
- data/lib/libis/tools/thread_safe.rb +31 -0
- data/lib/libis/tools/version.rb +1 -1
- data/lib/libis/tools/xml_document.rb +18 -24
- data/libis-tools.gemspec +6 -2
- data/spec/config_spec.rb +3 -4
- data/spec/logger_spec.rb +13 -30
- data/spec/mets_file_spec.rb +17 -17
- metadata +53 -7
@@ -8,6 +8,7 @@ module Libis
|
|
8
8
|
module Tools
|
9
9
|
module Metadata
|
10
10
|
|
11
|
+
# Helper class implementing a variable field for MARC
|
11
12
|
class VarField
|
12
13
|
|
13
14
|
attr_reader :tag
|
@@ -15,6 +16,10 @@ module Libis
|
|
15
16
|
attr_reader :ind2
|
16
17
|
attr_reader :subfield_data
|
17
18
|
|
19
|
+
# Create new variable field with given tag and indicators
|
20
|
+
# @param [String] tag tag
|
21
|
+
# @param [String] ind1 first indicator. nil will be translated into empty string.
|
22
|
+
# @param [String] ind2 second indicator. nil will be translated into empty string.
|
18
23
|
def initialize(tag, ind1, ind2)
|
19
24
|
@tag = tag
|
20
25
|
@ind1 = ind1 || ''
|
@@ -22,6 +27,9 @@ module Libis
|
|
22
27
|
@subfield_data = Hash.new { |h, k| h[k] = Array.new }
|
23
28
|
end
|
24
29
|
|
30
|
+
# Add subfield to variable field
|
31
|
+
# @param [String] name subfield indicator without '$'
|
32
|
+
# @param [String] value content of the subfield
|
25
33
|
def add_subfield(name, value)
|
26
34
|
@subfield_data[name] << value
|
27
35
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Libis
|
2
|
+
module Tools
|
3
|
+
class MetsFile
|
4
|
+
|
5
|
+
# Base class for a DNX section in the METS file. Each {DnxSection} derived class has a unique tag which will
|
6
|
+
# become the id of the <section> element.
|
7
|
+
class DnxSection < OpenStruct
|
8
|
+
|
9
|
+
# Instance method to access the class tag for DNX.
|
10
|
+
def tag
|
11
|
+
_tag = self.class.name.split('::').last
|
12
|
+
_tag[0] = _tag[0].downcase
|
13
|
+
_tag
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Specialized DNX section.
|
18
|
+
class ObjectCharacteristics < DnxSection; end
|
19
|
+
# Specialized DNX section.
|
20
|
+
class GeneralIECharacteristics < DnxSection; end
|
21
|
+
# Specialized DNX section.
|
22
|
+
class GeneralRepCharacteristics < DnxSection; end
|
23
|
+
# Specialized DNX section.
|
24
|
+
class GeneralFileCharacteristics < DnxSection; end
|
25
|
+
# Specialized DNX section.
|
26
|
+
class RetentionPolicy < DnxSection; end
|
27
|
+
# Specialized DNX section.
|
28
|
+
class FileFixity < DnxSection; end
|
29
|
+
# Specialized DNX section.
|
30
|
+
class AccessRightsPolicy < DnxSection; end
|
31
|
+
# Specialized DNX section.
|
32
|
+
class WebHarvesting < DnxSection; end
|
33
|
+
# Specialized DNX section.
|
34
|
+
class Collection < DnxSection; end
|
35
|
+
# Specialized DNX section.
|
36
|
+
class PreservationLevel < DnxSection; end
|
37
|
+
# Specialized DNX section.
|
38
|
+
class EnvironmentDependencies < DnxSection; end
|
39
|
+
# Specialized DNX section.
|
40
|
+
class EnvHardwareRegistry < DnxSection; end
|
41
|
+
# Specialized DNX section.
|
42
|
+
class EnvSoftwareRegistry < DnxSection; end
|
43
|
+
# Specialized DNX section.
|
44
|
+
class EnvironmentHardware < DnxSection; end
|
45
|
+
# Specialized DNX section.
|
46
|
+
class EnvironmentSoftware < DnxSection; end
|
47
|
+
# Specialized DNX section.
|
48
|
+
class Relationship < DnxSection; end
|
49
|
+
# Specialized DNX section.
|
50
|
+
class Environment < DnxSection; end
|
51
|
+
# Specialized DNX section.
|
52
|
+
class Inhibitors < DnxSection; end
|
53
|
+
# Specialized DNX section.
|
54
|
+
class SignatureInformation < DnxSection; end
|
55
|
+
# Specialized DNX section.
|
56
|
+
class CreatingApplication < DnxSection; end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -3,613 +3,43 @@ require 'ostruct'
|
|
3
3
|
|
4
4
|
require 'libis/tools/extend/hash'
|
5
5
|
require_relative 'xml_document'
|
6
|
+
require_relative 'thread_safe'
|
7
|
+
require_relative 'mets_dnx'
|
8
|
+
require_relative 'mets_objects'
|
6
9
|
|
7
10
|
module Libis
|
8
11
|
module Tools
|
9
12
|
|
10
13
|
# noinspection RubyResolve
|
11
|
-
# noinspection RubyClassVariableUsageInspection
|
12
|
-
class MetsFile
|
13
|
-
|
14
|
-
module IdContainer
|
15
|
-
|
16
|
-
def set_from_hash(h)
|
17
|
-
h.each { |k, v| send "#{k}=", v if respond_to?(k) }
|
18
|
-
end
|
19
|
-
|
20
|
-
def id
|
21
|
-
return @id if @id
|
22
|
-
@id = self.class.instance_variable_get('@id') || 1
|
23
|
-
self.class.instance_variable_set('@id', @id + 1)
|
24
|
-
@id
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_s
|
28
|
-
"#{self.class}:\n" +
|
29
|
-
self.instance_variables.map do |var|
|
30
|
-
v = self.instance_variable_get(var)
|
31
|
-
v = "#{v.class}-#{v.id}" if v.is_a? IdContainer
|
32
|
-
v = v.map do |x|
|
33
|
-
x.is_a?(IdContainer) ? "#{x.class}-#{x.id}" : x.to_s
|
34
|
-
end.join(',') if v.is_a? Array
|
35
|
-
" - #{var.to_s.gsub(/^@/, '')}: #{v}"
|
36
|
-
end.join("\n")
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
class Representation
|
42
|
-
include IdContainer
|
43
|
-
|
44
|
-
attr_accessor :label, :preservation_type, :usage_type, :representation_code, :entity_type, :access_right_id,
|
45
|
-
:user_a, :user_b, :user_c,
|
46
|
-
:group_id, :priority, :order,
|
47
|
-
:digital_original, :content, :context, :hardware, :carrier, :original_name,
|
48
|
-
:preservation_levels, :env_dependencies, :hardware_ids, :software_ids,
|
49
|
-
:hardware_infos, :software_infos, :relationship_infos, :environments,
|
50
|
-
:dc_record, :source_metadata
|
51
|
-
|
52
|
-
def xml_id
|
53
|
-
"rep#{id}"
|
54
|
-
end
|
55
|
-
|
56
|
-
def amd
|
57
|
-
dnx = {}
|
58
|
-
tech_data = []
|
59
|
-
# General characteristics
|
60
|
-
data = {
|
61
|
-
preservationType: preservation_type,
|
62
|
-
usageType: usage_type,
|
63
|
-
DigitalOriginal: digital_original,
|
64
|
-
label: label,
|
65
|
-
representationEntityType: entity_type,
|
66
|
-
contentType: content,
|
67
|
-
contextType: context,
|
68
|
-
hardwareUsed: hardware,
|
69
|
-
physicalCarrierMedia: carrier,
|
70
|
-
deliveryPriority: priority,
|
71
|
-
orderingSequence: order,
|
72
|
-
RepresentationCode: representation_code,
|
73
|
-
RepresentationOriginalName: original_name,
|
74
|
-
UserDefinedA: user_a,
|
75
|
-
UserDefinedB: user_b,
|
76
|
-
UserDefinedC: user_c,
|
77
|
-
}.cleanup
|
78
|
-
tech_data << TechGeneralRep.new(data) unless data.empty?
|
79
|
-
# Object characteristics
|
80
|
-
data = {
|
81
|
-
groupID: group_id
|
82
|
-
}.cleanup
|
83
|
-
tech_data << TechObjectChars.new(data) unless data.empty?
|
84
|
-
# Preservation level
|
85
|
-
if preservation_levels
|
86
|
-
data_list = []
|
87
|
-
preservation_levels.each do |preservation_level|
|
88
|
-
data = {
|
89
|
-
preservationLevelValue: preservation_level[:value],
|
90
|
-
preservationLevelRole: preservation_level[:role],
|
91
|
-
preservationLevelRationale: preservation_level[:rationale],
|
92
|
-
preservationLevelDateAssigned: preservation_level[:date],
|
93
|
-
}.cleanup
|
94
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
95
|
-
end
|
96
|
-
tech_data << PreservationLevel.new(array: data_list) unless data_list.empty?
|
97
|
-
end
|
98
|
-
# Dependencies
|
99
|
-
if env_dependencies
|
100
|
-
data_list = []
|
101
|
-
env_dependencies.each do |dependency|
|
102
|
-
data = {
|
103
|
-
dependencyName: dependency[:name],
|
104
|
-
dependencyIdentifierType1: dependency[:type1],
|
105
|
-
dependencyIdentifierValue1: dependency[:value1],
|
106
|
-
dependencyIdentifierType2: dependency[:type2],
|
107
|
-
dependencyIdentifierValue2: dependency[:value2],
|
108
|
-
dependencyIdentifierType3: dependency[:type3],
|
109
|
-
dependencyIdentifierValue3: dependency[:value3],
|
110
|
-
}.cleanup
|
111
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
112
|
-
end
|
113
|
-
tech_data << EnvDeps.new(array: data_list) unless data_list.empty?
|
114
|
-
end
|
115
|
-
# Hardware registry id
|
116
|
-
if hardware_ids
|
117
|
-
data_list = []
|
118
|
-
hardware_ids.each do |id|
|
119
|
-
data = {
|
120
|
-
registryId: id
|
121
|
-
}.cleanup
|
122
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
123
|
-
end
|
124
|
-
tech_data << HardwareId.new(array: data_list) unless data_list.empty?
|
125
|
-
end
|
126
|
-
# Software registry id
|
127
|
-
if software_ids
|
128
|
-
data_list = []
|
129
|
-
software_ids.each do |id|
|
130
|
-
data = {
|
131
|
-
registryId: id
|
132
|
-
}.cleanup
|
133
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
134
|
-
end
|
135
|
-
tech_data << SoftwareId.new(array: data_list) unless data_list.empty?
|
136
|
-
end
|
137
|
-
# Hardware
|
138
|
-
if hardware_infos
|
139
|
-
data_list = []
|
140
|
-
hardware_infos.each do |hardware|
|
141
|
-
data = {
|
142
|
-
hardwareName: hardware[:name],
|
143
|
-
hardwareType: hardware[:type],
|
144
|
-
hardwareOtherInformation: hardware[:info],
|
145
|
-
}.cleanup
|
146
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
147
|
-
end
|
148
|
-
tech_data << HardwareInfo.new(array: data_list) unless data_list.empty?
|
149
|
-
end
|
150
|
-
# Software
|
151
|
-
if software_infos
|
152
|
-
data_list = []
|
153
|
-
software_infos.each do |software|
|
154
|
-
data = {
|
155
|
-
softwareName: software[:name],
|
156
|
-
softwareVersion: software[:version],
|
157
|
-
softwareType: software[:type],
|
158
|
-
softwareOtherInformation: software[:info],
|
159
|
-
softwareDependancy: software[:dependency],
|
160
|
-
}.cleanup
|
161
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
162
|
-
end
|
163
|
-
tech_data << SoftwareInfo.new(array: data_list) unless data_list.empty?
|
164
|
-
end
|
165
|
-
# Relationship
|
166
|
-
if relationship_infos
|
167
|
-
data_list = []
|
168
|
-
relationship_infos.each do |relationship|
|
169
|
-
data = {
|
170
|
-
relationshipType: relationship[:type],
|
171
|
-
relationshipSubType: relationship[:subtype],
|
172
|
-
relatedObjectIdentifierType1: relationship[:type1],
|
173
|
-
relatedObjectIdentifierValue1: relationship[:id1],
|
174
|
-
relatedObjectSequence1: relationship[:seq1],
|
175
|
-
relatedObjectIdentifierType2: relationship[:type2],
|
176
|
-
relatedObjectIdentifierValue2: relationship[:id2],
|
177
|
-
relatedObjectSequence2: relationship[:seq2],
|
178
|
-
relatedObjectIdentifierType3: relationship[:type3],
|
179
|
-
relatedObjectIdentifierValue3: relationship[:id3],
|
180
|
-
relatedObjectSequence3: relationship[:seq3],
|
181
|
-
}.cleanup
|
182
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
183
|
-
end
|
184
|
-
tech_data << RelationShip.new(array: data_list) unless data_list.empty?
|
185
|
-
end
|
186
|
-
# Environment
|
187
|
-
if environments
|
188
|
-
data_list = []
|
189
|
-
environments.each do |environment|
|
190
|
-
data = {
|
191
|
-
environmentCharacteristic: environment[:characteristic],
|
192
|
-
environmentPurpose: environment[:purpose],
|
193
|
-
environmentNote: environment[:note],
|
194
|
-
}.cleanup
|
195
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
196
|
-
end
|
197
|
-
tech_data << Environment.new(array: data_list) unless data_list.empty?
|
198
|
-
end
|
199
|
-
# Finally assemble technical section
|
200
|
-
dnx[:tech] = tech_data unless tech_data.empty?
|
201
|
-
# Rights section
|
202
|
-
rights_data = []
|
203
|
-
data = {
|
204
|
-
policyId: access_right_id
|
205
|
-
}.cleanup
|
206
|
-
rights_data << Rights.new(data) unless data.empty?
|
207
|
-
dnx[:rights] = rights_data unless rights_data.empty?
|
208
|
-
# Source metadata
|
209
|
-
if source_metadata
|
210
|
-
source_metadata.each_with_index do |metadata, i|
|
211
|
-
dnx["source-#{metadata[:type].to_s.upcase}-#{i}"] = metadata[:data]
|
212
|
-
end
|
213
|
-
end
|
214
|
-
dnx
|
215
|
-
end
|
216
|
-
|
217
|
-
end
|
218
14
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
def make_group_id
|
235
|
-
"grp#{group_id || master.id rescue id}"
|
236
|
-
end
|
237
|
-
|
238
|
-
def master
|
239
|
-
@master ||= nil
|
240
|
-
end
|
241
|
-
|
242
|
-
def master=(file)
|
243
|
-
@master = file
|
244
|
-
end
|
245
|
-
|
246
|
-
def manifestations
|
247
|
-
@manifestations ||= Array.new
|
248
|
-
end
|
249
|
-
|
250
|
-
def add_manifestation(file)
|
251
|
-
manifestations << file
|
252
|
-
file.master = self
|
253
|
-
end
|
254
|
-
|
255
|
-
def orig_name
|
256
|
-
::File.basename(location)
|
257
|
-
end
|
258
|
-
|
259
|
-
def orig_path
|
260
|
-
::File.dirname(location)
|
261
|
-
end
|
262
|
-
|
263
|
-
def target
|
264
|
-
if target_location.nil?
|
265
|
-
return "#{xml_id}#{::File.extname(location)}"
|
266
|
-
end
|
267
|
-
target_location
|
268
|
-
end
|
269
|
-
|
270
|
-
def amd
|
271
|
-
dnx = {}
|
272
|
-
tech_data = []
|
273
|
-
# General File charateristics
|
274
|
-
data = {
|
275
|
-
label: label,
|
276
|
-
note: note,
|
277
|
-
fileCreationDate: creation_date,
|
278
|
-
fileModificationDate: modification_date,
|
279
|
-
FileEntityType: entity_type,
|
280
|
-
compositionLevel: composition_level,
|
281
|
-
# fileLocationType: 'FILE',
|
282
|
-
# fileLocation: '',
|
283
|
-
fileOriginalName: orig_name,
|
284
|
-
fileOriginalPath: orig_path,
|
285
|
-
fileOriginalID: location,
|
286
|
-
fileExtension: ::File.extname(orig_name),
|
287
|
-
fileMIMEType: mimetype,
|
288
|
-
fileSizeBytes: size,
|
289
|
-
formatLibraryId: puid
|
290
|
-
}.cleanup
|
291
|
-
tech_data << TechGeneralFile.new(data) unless data.empty?
|
292
|
-
# Fixity
|
293
|
-
%w'MD5 SHA1 SHA256 SHA384 SHA512'.each do |checksum_type|
|
294
|
-
if (checksum = self.send("checksum_#{checksum_type}"))
|
295
|
-
data = {
|
296
|
-
fixityType: checksum_type,
|
297
|
-
fixityValue: checksum,
|
298
|
-
}.cleanup
|
299
|
-
tech_data << TechFixity.new(data) unless data.empty?
|
300
|
-
end
|
301
|
-
end
|
302
|
-
# Object characteristics
|
303
|
-
data = {
|
304
|
-
groupID: make_group_id
|
305
|
-
}.cleanup
|
306
|
-
tech_data << TechObjectChars.new(data) unless data.empty?
|
307
|
-
# Preservation level
|
308
|
-
if preservation_levels
|
309
|
-
data_list = []
|
310
|
-
preservation_levels.each do |preservation_level|
|
311
|
-
data = {
|
312
|
-
preservationLevelValue: preservation_level[:value],
|
313
|
-
preservationLevelRole: preservation_level[:role],
|
314
|
-
preservationLevelRationale: preservation_level[:rationale],
|
315
|
-
preservationLevelDateAssigned: preservation_level[:date],
|
316
|
-
}.cleanup
|
317
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
318
|
-
end
|
319
|
-
tech_data << PreservationLevel.new(array: data_list) unless data_list.empty?
|
320
|
-
end
|
321
|
-
# Inhibitor
|
322
|
-
if inhibitors
|
323
|
-
data_list = []
|
324
|
-
inhibitors.each do |inhibitor|
|
325
|
-
data = {
|
326
|
-
inhibitorType: inhibitor[:type],
|
327
|
-
inhibitorTarget: inhibitor[:target],
|
328
|
-
inhibitorKey: inhibitor[:key],
|
329
|
-
}.cleanup
|
330
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
331
|
-
end
|
332
|
-
tech_data << Inhibitor.new(array: data_list) unless data_list.empty?
|
333
|
-
end
|
334
|
-
# Dependencies
|
335
|
-
if env_dependencies
|
336
|
-
data_list = []
|
337
|
-
env_dependencies.each do |dependency|
|
338
|
-
data = {
|
339
|
-
dependencyName: dependency[:name],
|
340
|
-
dependencyIdentifierType1: dependency[:type1],
|
341
|
-
dependencyIdentifierValue1: dependency[:value1],
|
342
|
-
dependencyIdentifierType2: dependency[:type2],
|
343
|
-
dependencyIdentifierValue2: dependency[:value2],
|
344
|
-
dependencyIdentifierType3: dependency[:type3],
|
345
|
-
dependencyIdentifierValue3: dependency[:value3],
|
346
|
-
}.cleanup
|
347
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
348
|
-
end
|
349
|
-
tech_data << EnvDeps.new(array: data_list) unless data_list.empty?
|
350
|
-
end
|
351
|
-
# Hardware registry id
|
352
|
-
if hardware_ids
|
353
|
-
data_list = []
|
354
|
-
hardware_ids.each do |id|
|
355
|
-
data = {
|
356
|
-
registryId: id
|
357
|
-
}.cleanup
|
358
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
359
|
-
end
|
360
|
-
tech_data << HardwareId.new(array: data_list) unless data_list.empty?
|
361
|
-
end
|
362
|
-
# Software registry id
|
363
|
-
if software_ids
|
364
|
-
data_list = []
|
365
|
-
software_ids.each do |id|
|
366
|
-
data = {
|
367
|
-
registryId: id
|
368
|
-
}.cleanup
|
369
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
370
|
-
end
|
371
|
-
tech_data << SoftwareId.new(array: data_list) unless data_list.empty?
|
372
|
-
end
|
373
|
-
# Singatures
|
374
|
-
if signatures
|
375
|
-
data_list = []
|
376
|
-
signatures.each do |signature|
|
377
|
-
data = {
|
378
|
-
signatureInformationEncoding: signature[:encoding],
|
379
|
-
signer: signature[:signer],
|
380
|
-
signatureMethod: signature[:method],
|
381
|
-
signatureValue: signature[:value],
|
382
|
-
signatureValidationRules: signature[:rules],
|
383
|
-
signatureProperties: signature[:properties],
|
384
|
-
}.cleanup
|
385
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
386
|
-
end
|
387
|
-
tech_data << Signature.new(array: data_list) unless data_list.empty?
|
388
|
-
end
|
389
|
-
# Hardware
|
390
|
-
if hardware_infos
|
391
|
-
data_list = []
|
392
|
-
hardware_infos.each do |hardware|
|
393
|
-
data = {
|
394
|
-
hardwareName: hardware[:name],
|
395
|
-
hardwareType: hardware[:type],
|
396
|
-
hardwareOtherInformation: hardware[:info],
|
397
|
-
}.cleanup
|
398
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
399
|
-
end
|
400
|
-
tech_data << HardwareInfo.new(array: data_list) unless data_list.empty?
|
401
|
-
end
|
402
|
-
# Software
|
403
|
-
if software_infos
|
404
|
-
data_list = []
|
405
|
-
software_infos.each do |software|
|
406
|
-
data = {
|
407
|
-
softwareName: software[:name],
|
408
|
-
softwareVersion: software[:version],
|
409
|
-
softwareType: software[:type],
|
410
|
-
softwareOtherInformation: software[:info],
|
411
|
-
softwareDependancy: software[:dependency],
|
412
|
-
}.cleanup
|
413
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
414
|
-
end
|
415
|
-
tech_data << SoftwareInfo.new(array: data_list) unless data_list.empty?
|
416
|
-
end
|
417
|
-
# Relationship
|
418
|
-
if relationship_infos
|
419
|
-
data_list = []
|
420
|
-
relationship_infos.each do |relationship|
|
421
|
-
data = {
|
422
|
-
relationshipType: relationship[:type],
|
423
|
-
relationshipSubType: relationship[:subtype],
|
424
|
-
relatedObjectIdentifierType1: relationship[:type1],
|
425
|
-
relatedObjectIdentifierValue1: relationship[:id1],
|
426
|
-
relatedObjectSequence1: relationship[:seq1],
|
427
|
-
relatedObjectIdentifierType2: relationship[:type2],
|
428
|
-
relatedObjectIdentifierValue2: relationship[:id2],
|
429
|
-
relatedObjectSequence2: relationship[:seq2],
|
430
|
-
relatedObjectIdentifierType3: relationship[:type3],
|
431
|
-
relatedObjectIdentifierValue3: relationship[:id3],
|
432
|
-
relatedObjectSequence3: relationship[:seq3],
|
433
|
-
}.cleanup
|
434
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
435
|
-
end
|
436
|
-
tech_data << RelationShip.new(array: data_list) unless data_list.empty?
|
437
|
-
end
|
438
|
-
# Environment
|
439
|
-
if environments
|
440
|
-
data_list = []
|
441
|
-
environments.each do |environment|
|
442
|
-
data = {
|
443
|
-
environmentCharacteristic: environment[:characteristic],
|
444
|
-
environmentPurpose: environment[:purpose],
|
445
|
-
environmentNote: environment[:note],
|
446
|
-
}.cleanup
|
447
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
448
|
-
end
|
449
|
-
tech_data << Environment.new(array: data_list) unless data_list.empty?
|
450
|
-
end
|
451
|
-
# Application
|
452
|
-
if applications
|
453
|
-
data_list = []
|
454
|
-
applications.each do |application|
|
455
|
-
data = {
|
456
|
-
creatingApplicationName: application[:name],
|
457
|
-
creatingApplicationVersion: application[:version],
|
458
|
-
dateCreatedByApplication: application[:date],
|
459
|
-
}.cleanup
|
460
|
-
data_list << OpenStruct.new(data) unless data.empty?
|
461
|
-
end
|
462
|
-
tech_data << Application.new(array: data_list) unless data_list.empty?
|
463
|
-
end
|
464
|
-
# Finally assemble technical section
|
465
|
-
dnx[:tech] = tech_data unless tech_data.empty?
|
466
|
-
dnx
|
467
|
-
end
|
468
|
-
|
469
|
-
end
|
470
|
-
|
471
|
-
class Div
|
472
|
-
include IdContainer
|
473
|
-
|
474
|
-
attr_accessor :label
|
475
|
-
|
476
|
-
def xml_id
|
477
|
-
"div-#{id}"
|
478
|
-
end
|
479
|
-
|
480
|
-
def children
|
481
|
-
files + divs
|
482
|
-
end
|
483
|
-
|
484
|
-
def files
|
485
|
-
@files ||= Array.new
|
486
|
-
end
|
487
|
-
|
488
|
-
def divs
|
489
|
-
@divs ||= Array.new
|
490
|
-
end
|
491
|
-
|
492
|
-
def <<(obj)
|
493
|
-
case obj
|
494
|
-
when File
|
495
|
-
files << obj
|
496
|
-
when Div
|
497
|
-
divs << obj
|
498
|
-
else
|
499
|
-
raise RuntimeError, "Child object type not supported: #{obj.class}"
|
500
|
-
end
|
501
|
-
end
|
502
|
-
end
|
503
|
-
|
504
|
-
class Map
|
505
|
-
include IdContainer
|
506
|
-
|
507
|
-
attr_accessor :representation, :div
|
508
|
-
|
509
|
-
def xml_id
|
510
|
-
"smap-#{id}"
|
511
|
-
end
|
512
|
-
|
513
|
-
end
|
514
|
-
|
515
|
-
class DnxSection < OpenStruct
|
516
|
-
def self.tag(value = nil)
|
517
|
-
var_name = '@tag'
|
518
|
-
if value.nil?
|
519
|
-
instance_variable_get(var_name)
|
520
|
-
else
|
521
|
-
instance_variable_set(var_name, value)
|
522
|
-
end
|
523
|
-
end
|
524
|
-
|
525
|
-
def tag
|
526
|
-
self.class.tag
|
527
|
-
end
|
528
|
-
end
|
529
|
-
|
530
|
-
class TechObjectChars < DnxSection
|
531
|
-
tag 'objectCharacteristics'
|
532
|
-
end
|
533
|
-
|
534
|
-
class TechGeneralIE < DnxSection
|
535
|
-
tag 'generalIECharacteristics'
|
536
|
-
end
|
537
|
-
|
538
|
-
class TechGeneralRep < DnxSection
|
539
|
-
tag 'generalRepCharacteristics'
|
540
|
-
end
|
541
|
-
|
542
|
-
class TechGeneralFile < DnxSection
|
543
|
-
tag 'generalFileCharacteristics'
|
544
|
-
end
|
545
|
-
|
546
|
-
class RetentionPeriod < DnxSection
|
547
|
-
tag 'retentionPolicy'
|
548
|
-
end
|
549
|
-
|
550
|
-
class TechFixity < DnxSection
|
551
|
-
tag 'fileFixity'
|
552
|
-
end
|
553
|
-
|
554
|
-
class Rights < DnxSection
|
555
|
-
tag 'accessRightsPolicy'
|
556
|
-
end
|
557
|
-
|
558
|
-
class WebHarvesting < DnxSection
|
559
|
-
tag 'webHarvesting'
|
560
|
-
end
|
561
|
-
|
562
|
-
class CollectionInfo < DnxSection
|
563
|
-
tag 'Collection'
|
564
|
-
end
|
565
|
-
|
566
|
-
class PreservationLevel < DnxSection
|
567
|
-
tag 'preservationLevel'
|
568
|
-
end
|
569
|
-
|
570
|
-
class EnvDeps < DnxSection
|
571
|
-
tag 'environmentDependencies'
|
572
|
-
end
|
573
|
-
|
574
|
-
class HardwareId < DnxSection
|
575
|
-
tag 'envHardwareRegistry'
|
576
|
-
end
|
577
|
-
|
578
|
-
class SoftwareId < DnxSection
|
579
|
-
tag 'envSoftwareRegistry'
|
580
|
-
end
|
581
|
-
|
582
|
-
class HardwareInfo < DnxSection
|
583
|
-
tag 'environmentHardware'
|
584
|
-
end
|
585
|
-
|
586
|
-
class SoftwareInfo < DnxSection
|
587
|
-
tag 'environmentSoftware'
|
588
|
-
end
|
589
|
-
|
590
|
-
class Relationship < DnxSection
|
591
|
-
tag 'relationship'
|
592
|
-
end
|
593
|
-
|
594
|
-
class Environment < DnxSection
|
595
|
-
tag 'environment'
|
596
|
-
end
|
597
|
-
|
598
|
-
class Inhibitor < DnxSection
|
599
|
-
tag 'inhibitors'
|
600
|
-
end
|
601
|
-
|
602
|
-
class Signature < DnxSection
|
603
|
-
tag 'signatureInformation'
|
604
|
-
end
|
605
|
-
|
606
|
-
class Application < DnxSection
|
607
|
-
tag 'creatingApplication'
|
608
|
-
end
|
15
|
+
# This class supports creating METS files in a friendlier way than by creating an XML file.
|
16
|
+
#
|
17
|
+
# There are sub-classes that represent {Representation}s, {File}s, {Div}isions and {Map}s.
|
18
|
+
# These are simple container classes that take care of assigning proper ids and accept most known attributes.
|
19
|
+
# Each of the container classes have a corresponding method on the METS class that takes a Hash with attributes
|
20
|
+
# and returns the created object.
|
21
|
+
#
|
22
|
+
# {Div} and {File} instances can be added to a {Div} instance and a Div can be associated with a {Representation},
|
23
|
+
# thus creating a structmap.
|
24
|
+
#
|
25
|
+
# The {#amd_info=} method on the {MetsFile} class sets the main amd parameters and
|
26
|
+
#
|
27
|
+
# With the help of the {DnxSection} class and it's derived classes, the container classes can generate the amd
|
28
|
+
# sections for the METS file.
|
29
|
+
class MetsFile
|
609
30
|
|
610
|
-
|
31
|
+
# Keeps track of {Representation}s created
|
32
|
+
attr_reader :representations
|
33
|
+
# Keeps track of {File}s created
|
34
|
+
attr_reader :files
|
35
|
+
# Keeps track of {Div}s created
|
36
|
+
attr_reader :divs
|
37
|
+
# Keeps track of {Map}s created
|
38
|
+
attr_reader :maps
|
611
39
|
|
612
40
|
# noinspection RubyConstantNamingConvention
|
41
|
+
|
42
|
+
# Namespace constants for METS XML
|
613
43
|
NS = {
|
614
44
|
mets: 'http://www.loc.gov/METS/',
|
615
45
|
dc: 'http://purl.org/dc/elements/1.1/',
|
@@ -617,6 +47,7 @@ module Libis
|
|
617
47
|
xlin: 'http://www.w3.org/1999/xlink',
|
618
48
|
}
|
619
49
|
|
50
|
+
# Creates an initializes a new {MetsFile} instance
|
620
51
|
def initialize
|
621
52
|
@representations = {}
|
622
53
|
@files = {}
|
@@ -626,6 +57,20 @@ module Libis
|
|
626
57
|
@dc_record = nil
|
627
58
|
end
|
628
59
|
|
60
|
+
# Reads an existing METS XML file and parses into a large Hash structure for inspection.
|
61
|
+
# It will not immediately allow you to create a {MetsFile} instance from it, but with some inspection and
|
62
|
+
# knowledge of METS file structure it should be possible to recreate a similar file using the class.
|
63
|
+
#
|
64
|
+
# The returned Hash has the following structure:
|
65
|
+
#
|
66
|
+
# * :amd - the general AMD section with subsections
|
67
|
+
# * :dmd - the general DMD section with the DC record(s)
|
68
|
+
# Each amd section has one or more subsections with keys :tech, :rights, :source or :digiprov. Each
|
69
|
+
# subsection is a Hash with section id as key and an array as value. For each <record> element a Hash is
|
70
|
+
# added to the array with <key@id> as key and <key> content as value.
|
71
|
+
#
|
72
|
+
# @param [String,Hash,::Libis::Tools::XmlDocument, Nokogiri::XML::Document] xml XML file in any of the listed formats.
|
73
|
+
# @return [Hash] The parsed information.
|
629
74
|
def self.parse(xml)
|
630
75
|
xml_doc = case xml
|
631
76
|
when String
|
@@ -716,17 +161,40 @@ module Libis
|
|
716
161
|
)
|
717
162
|
end
|
718
163
|
|
164
|
+
# Sets the DC record for the global amd section.
|
165
|
+
#
|
166
|
+
# @param [String] xml Serialized Dublin Core XML file
|
719
167
|
def dc_record=(xml)
|
720
168
|
@dc_record = xml
|
721
169
|
end
|
722
170
|
|
171
|
+
# Sets the attributes for the global amd section.
|
172
|
+
#
|
173
|
+
# @param [Hash] hash name, value pairs for the DNX sections. Each will go into it's appropriate AMD and DNX
|
174
|
+
# sections automatically.
|
175
|
+
# The following names are currently supported:
|
176
|
+
# * status
|
177
|
+
# * entity_type
|
178
|
+
# * user_a
|
179
|
+
# * user_b
|
180
|
+
# * user_c
|
181
|
+
# * submission_reason
|
182
|
+
# * retention_id - RentionPolicy ID
|
183
|
+
# * harvest_url
|
184
|
+
# * harvest_id
|
185
|
+
# * harvest_target
|
186
|
+
# * harvest_group
|
187
|
+
# * harvest_date
|
188
|
+
# * harvest_time
|
189
|
+
# * collection_id - Collection ID where the IE should be added to
|
190
|
+
# * access_right - AccessRight ID
|
191
|
+
# * source_metadata - Array with hashes like {type: 'MyXML', data: '<xml ....>'}
|
723
192
|
def amd_info=(hash)
|
724
|
-
@dnx = {}
|
725
193
|
tech_data = []
|
726
194
|
data = {
|
727
195
|
groupID: hash[:group_id]
|
728
196
|
}.cleanup
|
729
|
-
tech_data <<
|
197
|
+
tech_data << ObjectCharacteristics.new(data) unless data.empty?
|
730
198
|
data = {
|
731
199
|
status: hash[:status],
|
732
200
|
IEEntityType: hash[:entity_type],
|
@@ -735,11 +203,11 @@ module Libis
|
|
735
203
|
UserDefinedC: hash[:user_c],
|
736
204
|
submissionReason: hash[:submission_reason],
|
737
205
|
}.cleanup
|
738
|
-
tech_data <<
|
206
|
+
tech_data << GeneralIECharacteristics.new(data) unless data.empty?
|
739
207
|
data = {
|
740
208
|
policyId: hash[:retention_id],
|
741
209
|
}.cleanup
|
742
|
-
tech_data <<
|
210
|
+
tech_data << RetentionPolicy.new(data) unless data.empty?
|
743
211
|
data = {
|
744
212
|
primarySeedURL: hash[:harvest_url],
|
745
213
|
WCTIdentifier: hash[:harvest_id],
|
@@ -752,19 +220,20 @@ module Libis
|
|
752
220
|
data = {
|
753
221
|
collectionId: hash[:collection_id]
|
754
222
|
}.cleanup
|
755
|
-
tech_data <<
|
223
|
+
tech_data << Collection.new(data) unless data.empty?
|
756
224
|
@dnx[:tech] = tech_data unless tech_data.empty?
|
757
225
|
data = {
|
758
226
|
policyId: hash[:access_right]
|
759
227
|
}.cleanup
|
760
228
|
rights_data = []
|
761
|
-
rights_data <<
|
229
|
+
rights_data << AccessRightsPolicy.new(data) unless data.empty?
|
762
230
|
@dnx[:rights] = rights_data unless rights_data.empty?
|
763
231
|
(hash[:source_metadata] || []).each_with_index do |metadata, i|
|
764
232
|
@dnx["source-#{metadata[:type].to_s.upcase}-#{i+1}"] = metadata[:data]
|
765
233
|
end
|
766
234
|
end
|
767
235
|
|
236
|
+
# Create a new representation. See {Representation} for supported Hash keys.
|
768
237
|
# @param [Hash] hash
|
769
238
|
# @return [Libis::Tools::MetsFile::Representation]
|
770
239
|
def representation(hash = {})
|
@@ -773,6 +242,7 @@ module Libis
|
|
773
242
|
@representations[rep.id] = rep
|
774
243
|
end
|
775
244
|
|
245
|
+
# Create a new division. See {Div} for supported Hash keys.
|
776
246
|
# @param [Hash] hash
|
777
247
|
# @return [Libis::Tools::MetsFile::Div]
|
778
248
|
def div(hash = {})
|
@@ -781,6 +251,7 @@ module Libis
|
|
781
251
|
@divs[div.id] = div
|
782
252
|
end
|
783
253
|
|
254
|
+
# Create a new file. See {File} for supported Hash keys.
|
784
255
|
# @param [Hash] hash
|
785
256
|
# @return [Libis::Tools::MetsFile::File]
|
786
257
|
def file(hash = {})
|
@@ -789,6 +260,7 @@ module Libis
|
|
789
260
|
@files[file.id] = file
|
790
261
|
end
|
791
262
|
|
263
|
+
# Create a new structmap.
|
792
264
|
# @param [Libis::Tools::MetsFile::Representation] rep
|
793
265
|
# @param [Libis::Tools::MetsFile::Div] div
|
794
266
|
# @return [Libis::Tools::MetsFile::Map]
|
@@ -799,6 +271,7 @@ module Libis
|
|
799
271
|
@maps[map.id] = map
|
800
272
|
end
|
801
273
|
|
274
|
+
# Create the METS XML document.
|
802
275
|
# @return [Libis::Tools::XmlDocument]
|
803
276
|
def xml_doc
|
804
277
|
::Libis::Tools::XmlDocument.build do |xml|
|
@@ -815,14 +288,17 @@ module Libis
|
|
815
288
|
|
816
289
|
protected
|
817
290
|
|
291
|
+
# ID for the DMD section of a representation, division or file
|
818
292
|
def dmd_id(id)
|
819
293
|
"#{id}-dmd"
|
820
294
|
end
|
821
295
|
|
296
|
+
# ID for the AMD section of a representation, division or file
|
822
297
|
def amd_id(id)
|
823
298
|
"#{id}-amd"
|
824
299
|
end
|
825
300
|
|
301
|
+
# Helper method to create the XML DMD sections
|
826
302
|
def add_dmd(xml, object = nil)
|
827
303
|
case object
|
828
304
|
when NilClass
|
@@ -838,6 +314,7 @@ module Libis
|
|
838
314
|
end
|
839
315
|
end
|
840
316
|
|
317
|
+
# Helper method to create the XML AMD sections
|
841
318
|
def add_amd(xml, object = nil)
|
842
319
|
case object
|
843
320
|
when NilClass
|
@@ -855,6 +332,7 @@ module Libis
|
|
855
332
|
end
|
856
333
|
end
|
857
334
|
|
335
|
+
# Helper method to create the XML file section
|
858
336
|
def add_filesec(xml, object = nil, representation = nil)
|
859
337
|
case object
|
860
338
|
when NilClass
|
@@ -895,6 +373,7 @@ module Libis
|
|
895
373
|
end
|
896
374
|
end
|
897
375
|
|
376
|
+
# Helper method to create the Structmap
|
898
377
|
def add_struct_map(xml, object = nil)
|
899
378
|
case object
|
900
379
|
when NilClass
|
@@ -930,6 +409,7 @@ module Libis
|
|
930
409
|
|
931
410
|
end
|
932
411
|
|
412
|
+
# Helper method to create a single XML DMD section
|
933
413
|
def add_dmd_section(xml, id, dc_record = nil)
|
934
414
|
return if dc_record.nil?
|
935
415
|
xml[:mets].dmdSec(ID: dmd_id(id)) {
|
@@ -941,6 +421,7 @@ module Libis
|
|
941
421
|
}
|
942
422
|
end
|
943
423
|
|
424
|
+
# Helper method to create a single AMD section
|
944
425
|
def add_amd_section(xml, id, dnx_sections = {})
|
945
426
|
xml[:mets].amdSec(ID: amd_id(id)) {
|
946
427
|
dnx_sections.each do |section_type, data|
|
@@ -965,6 +446,7 @@ module Libis
|
|
965
446
|
}
|
966
447
|
end
|
967
448
|
|
449
|
+
# Helper method to create the XML DNX sections
|
968
450
|
def add_dnx_sections(xml, section_data)
|
969
451
|
section_data ||= []
|
970
452
|
xml.dnx(xmlns: NS[:dnx]) {
|
@@ -984,6 +466,7 @@ module Libis
|
|
984
466
|
}
|
985
467
|
end
|
986
468
|
|
469
|
+
# Helper method to parse a XML div
|
987
470
|
def parse_div(div, rep)
|
988
471
|
if div[:TYPE] == 'FILE'
|
989
472
|
file_id = div.children.first[:FILEID]
|