cocina-models 0.78.0 → 0.81.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/description_types.yml +2 -0
- data/docs/description_types.md +1 -0
- data/lib/cocina/generator/schema.rb +2 -2
- data/lib/cocina/generator/schema_base.rb +10 -0
- data/lib/cocina/generator/schema_value.rb +0 -4
- data/lib/cocina/models/access_role.rb +1 -0
- data/lib/cocina/models/access_role_member.rb +1 -0
- data/lib/cocina/models/admin_policy_access_template.rb +1 -0
- data/lib/cocina/models/admin_policy_administrative.rb +1 -0
- data/lib/cocina/models/admin_policy_with_metadata.rb +1 -0
- data/lib/cocina/models/applies_to.rb +1 -0
- data/lib/cocina/models/builders/name_title_group_builder.rb +0 -1
- data/lib/cocina/models/collection.rb +1 -0
- data/lib/cocina/models/collection_access.rb +1 -0
- data/lib/cocina/models/collection_with_metadata.rb +1 -0
- data/lib/cocina/models/contributor.rb +1 -0
- data/lib/cocina/models/descriptive_access_metadata.rb +1 -0
- data/lib/cocina/models/descriptive_admin_metadata.rb +1 -0
- data/lib/cocina/models/descriptive_basic_value.rb +1 -0
- data/lib/cocina/models/descriptive_geographic_metadata.rb +1 -0
- data/lib/cocina/models/descriptive_grouped_value.rb +1 -0
- data/lib/cocina/models/descriptive_parallel_contributor.rb +2 -0
- data/lib/cocina/models/descriptive_parallel_event.rb +1 -0
- data/lib/cocina/models/descriptive_parallel_value.rb +1 -0
- data/lib/cocina/models/descriptive_structured_value.rb +1 -0
- data/lib/cocina/models/descriptive_value.rb +1 -0
- data/lib/cocina/models/descriptive_value_language.rb +1 -0
- data/lib/cocina/models/dro.rb +1 -0
- data/lib/cocina/models/dro_structural.rb +1 -0
- data/lib/cocina/models/dro_with_metadata.rb +1 -0
- data/lib/cocina/models/event.rb +1 -0
- data/lib/cocina/models/file.rb +1 -0
- data/lib/cocina/models/file_access.rb +1 -0
- data/lib/cocina/models/file_set.rb +1 -0
- data/lib/cocina/models/file_set_structural.rb +1 -0
- data/lib/cocina/models/geographic.rb +1 -0
- data/lib/cocina/models/language.rb +1 -0
- data/lib/cocina/models/mapping/from_mods/name_builder.rb +79 -23
- data/lib/cocina/models/mapping/normalizers/mods/name_normalizer.rb +27 -2
- data/lib/cocina/models/mapping/to_mods/name_writer.rb +79 -21
- data/lib/cocina/models/mapping/to_mods/subject.rb +2 -2
- data/lib/cocina/models/message_digest.rb +1 -0
- data/lib/cocina/models/object_metadata.rb +1 -0
- data/lib/cocina/models/presentation.rb +1 -0
- data/lib/cocina/models/related_resource.rb +1 -0
- data/lib/cocina/models/release_tag.rb +1 -0
- data/lib/cocina/models/request_admin_policy.rb +1 -0
- data/lib/cocina/models/request_collection.rb +1 -0
- data/lib/cocina/models/request_description.rb +1 -0
- data/lib/cocina/models/request_dro.rb +1 -0
- data/lib/cocina/models/request_dro_structural.rb +1 -0
- data/lib/cocina/models/request_file_set_structural.rb +1 -0
- data/lib/cocina/models/request_identification.rb +1 -0
- data/lib/cocina/models/sequence.rb +1 -0
- data/lib/cocina/models/source.rb +1 -0
- data/lib/cocina/models/standard.rb +1 -0
- data/lib/cocina/models/validators/associated_name_validator.rb +2 -4
- data/lib/cocina/models/validators/description_types_validator.rb +2 -3
- data/lib/cocina/models/validators/description_values_validator.rb +2 -3
- data/lib/cocina/models/validators/validator.rb +1 -2
- data/lib/cocina/models/version.rb +1 -1
- data/lib/cocina/models.rb +2 -2
- data/lib/cocina/rspec/factories.rb +13 -6
- data/openapi.yml +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d8392fadece0bb4ec12ed93e958c99abf04e29ad0a78c14ee8651ec8ecd1e37
|
4
|
+
data.tar.gz: db4a5de344bb53d98b2b71f725e8b1d1b9e0d5e0b28416989ad55f19df826376
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4880e4eb86935116e0bec017926aeab5e5aa12d8792ee5066d0aeef5f514e6f13f63c8bc68c03035fdff2748f95aa7215f236384e109a8e532bd76e9677ae53c
|
7
|
+
data.tar.gz: 22154804ee4b103bdbd3aaaee449327bb4a6c9bc1dce9431a5311ed06c5e76039ccb420634d275f56d644a60ffa529bd9cb822f9c549be057f4972d791a34bf7
|
data/description_types.yml
CHANGED
data/docs/description_types.md
CHANGED
@@ -41,6 +41,12 @@ module Cocina
|
|
41
41
|
"# #{schema_doc.description}\n"
|
42
42
|
end
|
43
43
|
|
44
|
+
def deprecation
|
45
|
+
return '' unless schema_doc.deprecated?
|
46
|
+
|
47
|
+
"# DEPRECATED\n"
|
48
|
+
end
|
49
|
+
|
44
50
|
def example
|
45
51
|
return '' unless schema_doc.example
|
46
52
|
|
@@ -82,6 +88,10 @@ module Cocina
|
|
82
88
|
'Strict::String'
|
83
89
|
end
|
84
90
|
end
|
91
|
+
|
92
|
+
def preamble
|
93
|
+
"#{deprecation}#{description}#{example}#{relaxed_comment}"
|
94
|
+
end
|
85
95
|
end
|
86
96
|
end
|
87
97
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Access role conferred by an AdminPolicy to objects within it. (used by Argo)
|
5
6
|
class AccessRole < Struct
|
6
7
|
# Name of role
|
7
8
|
attribute :name, Types::Strict::String.enum('dor-apo-depositor', 'dor-apo-manager', 'dor-apo-viewer', 'sdr-administrator', 'sdr-viewer', 'hydrus-collection-creator', 'hydrus-collection-manager', 'hydrus-collection-depositor', 'hydrus-collection-item-depositor', 'hydrus-collection-reviewer', 'hydrus-collection-viewer')
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Provides the template of access settings that is copied to the items goverend by an AdminPolicy. This is almost the same as DROAccess, but it provides no defaults and has no embargo.
|
5
6
|
class AdminPolicyAccessTemplate < Struct
|
6
7
|
attribute? :view, Types::Strict::String.enum('world', 'stanford', 'location-based', 'citation-only', 'dark')
|
7
8
|
# Available for controlled digital lending.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Administrative properties for an AdminPolicy
|
5
6
|
class AdminPolicyAdministrative < Struct
|
6
7
|
attribute(:accessTemplate, AdminPolicyAccessTemplate.default { AdminPolicyAccessTemplate.new })
|
7
8
|
attribute :registrationWorkflow, Types::Strict::Array.of(Types::Strict::String).default([].freeze)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Property model for indicating the parts, aspects, or versions of the resource to which a descriptive element is applicable.
|
5
6
|
class AppliesTo < Struct
|
6
7
|
attribute :appliesTo, Types::Strict::Array.of(DescriptiveBasicValue).default([].freeze)
|
7
8
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Property model for describing agents contributing in some way to the creation and history of the resource.
|
5
6
|
class Contributor < Struct
|
6
7
|
attribute :name, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
# Entity type of the contributor (person, organization, etc.). See https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md for valid types.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Information about how to access digital and physical versions of the object.
|
5
6
|
class DescriptiveAccessMetadata < Struct
|
6
7
|
attribute :url, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
attribute :physicalLocation, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Information about this resource description.
|
5
6
|
class DescriptiveAdminMetadata < Struct
|
6
7
|
attribute :contributor, Types::Strict::Array.of(Contributor).default([].freeze)
|
7
8
|
attribute :event, Types::Strict::Array.of(Event).default([].freeze)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Basic value model for descriptive elements. Can only have one of value, parallelValue, groupedValue, or structuredValue.
|
5
6
|
class DescriptiveBasicValue < Struct
|
6
7
|
attribute :structuredValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
attribute :parallelValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Value model for mods geographic extension metadata
|
5
6
|
class DescriptiveGeographicMetadata < Struct
|
6
7
|
attribute :form, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
attribute :subject, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# DEPRECATED
|
6
|
+
# Value model for multiple representations of information about the same contributor (e.g. in different languages).
|
5
7
|
class DescriptiveParallelContributor < Struct
|
6
8
|
attribute :name, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
9
|
# Entity type of the contributor (person, organization, etc.). See https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md for valid types.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Value model for multiple representations of information about the same event (e.g. in different languages).
|
5
6
|
class DescriptiveParallelEvent < Struct
|
6
7
|
attribute :structuredValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
# Description of the event (creation, publication, etc.).
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Value model for multiple representations of the same information (e.g. in different languages).
|
5
6
|
class DescriptiveParallelValue < Struct
|
6
7
|
attribute :parallelValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Default value model for descriptive elements.
|
5
6
|
class DescriptiveValue < Struct
|
6
7
|
attribute :structuredValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
attribute :parallelValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
data/lib/cocina/models/dro.rb
CHANGED
data/lib/cocina/models/event.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Property model for describing events in the history of the resource.
|
5
6
|
class Event < Struct
|
6
7
|
attribute :structuredValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
# Description of the event (creation, publication, etc.).
|
data/lib/cocina/models/file.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Languages, scripts, symbolic systems, and notations used in all or part of a resource or its descriptive metadata.
|
5
6
|
class Language < Struct
|
6
7
|
attribute :appliesTo, Types::Strict::Array.of(DescriptiveBasicValue).default([].freeze)
|
7
8
|
# Code value of the descriptive element.
|
@@ -36,13 +36,13 @@ module Cocina
|
|
36
36
|
|
37
37
|
def build_parallel
|
38
38
|
names = {
|
39
|
-
parallelValue:
|
39
|
+
parallelValue: build_parallel_values,
|
40
40
|
type: type_for(name_elements.first['type']),
|
41
41
|
status: name_elements.filter_map { |name_element| name_element['usage'] }.first
|
42
42
|
}.compact
|
43
43
|
{ name: [names] }.tap do |attrs|
|
44
|
-
|
45
|
-
attrs[:
|
44
|
+
attrs[:role] = name_elements.flat_map { |name_node| build_roles(name_node) }.compact.uniq.presence
|
45
|
+
attrs[:note] = name_elements.flat_map { |name_node| build_affiliation_notes(name_node) }.compact.uniq.presence
|
46
46
|
end.compact
|
47
47
|
end
|
48
48
|
|
@@ -51,8 +51,8 @@ module Cocina
|
|
51
51
|
status: name_node['usage']
|
52
52
|
}.compact.merge(common_lang_script(name_node))
|
53
53
|
|
54
|
-
name_attrs = name_attrs.merge(common_name(name_node, name_attrs[:name]))
|
55
|
-
name_parts = build_name_parts(name_node)
|
54
|
+
name_attrs = name_attrs.merge(common_name(name_node, name_attrs[:name], is_parallel: true))
|
55
|
+
name_parts = build_name_parts(name_node, parallel: true)
|
56
56
|
notifier.warn('Missing name/namePart element') if name_parts.all?(&:empty?)
|
57
57
|
name_parts.each { |name_part| name_attrs = name_part.merge(name_attrs) }
|
58
58
|
name_attrs.compact
|
@@ -85,9 +85,9 @@ module Cocina
|
|
85
85
|
}.compact.merge(common_name(name_node, name_parts))
|
86
86
|
end
|
87
87
|
|
88
|
-
def common_name(name_node, name)
|
88
|
+
def common_name(name_node, name, is_parallel: false)
|
89
89
|
{
|
90
|
-
note: build_notes(name_node),
|
90
|
+
note: build_notes(name_node, is_parallel: is_parallel),
|
91
91
|
identifier: build_identifier(name_node)
|
92
92
|
}.tap do |attrs|
|
93
93
|
roles = build_roles(name_node)
|
@@ -106,7 +106,17 @@ module Cocina
|
|
106
106
|
end.compact
|
107
107
|
end
|
108
108
|
|
109
|
-
def
|
109
|
+
def build_parallel_values
|
110
|
+
parallel_values = []
|
111
|
+
name_elements.each do |name_node|
|
112
|
+
parallel_values << build_parallel_name(name_node)
|
113
|
+
display_val = display_value(name_node)
|
114
|
+
parallel_values << display_val if display_val
|
115
|
+
end
|
116
|
+
parallel_values.compact
|
117
|
+
end
|
118
|
+
|
119
|
+
def build_name_parts(name_node, parallel: false)
|
110
120
|
name_part_nodes = name_node.xpath('mods:namePart', mods: Description::DESC_METADATA_NS)
|
111
121
|
alternative_name_nodes = name_node.xpath('mods:alternativeName', mods: Description::DESC_METADATA_NS)
|
112
122
|
|
@@ -116,23 +126,64 @@ module Cocina
|
|
116
126
|
parts << { valueAt: name_node['xlink:href'] } if name_node['xlink:href']
|
117
127
|
parts << common_authority(name_node) if name_node['valueURI']
|
118
128
|
when 1
|
119
|
-
|
120
|
-
|
121
|
-
.merge(common_authority(name_node)).merge(common_lang_script(name_node)).presence
|
129
|
+
name = build_simple_value_name(name_node, name_part_nodes.first, alternative_name_nodes, parallel)
|
130
|
+
parts << name.merge(common_authority(name_node)).merge(common_lang_script(name_node)).presence
|
122
131
|
else
|
123
|
-
|
124
|
-
|
125
|
-
end
|
126
|
-
parts << { structuredValue: vals }.merge(common_authority(name_node)).merge(common_lang_script(name_node))
|
132
|
+
name = build_structured_value_name(name_node, name_part_nodes)
|
133
|
+
parts << name.merge(common_authority(name_node)).merge(common_lang_script(name_node))
|
127
134
|
end
|
128
135
|
|
129
136
|
parts = build_alternative_name(alternative_name_nodes, parts) if alternative_name_nodes.present?
|
130
|
-
|
131
|
-
display_form = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS).first
|
132
|
-
parts << { value: display_form.text, type: 'display' } if display_form
|
133
137
|
parts.compact
|
134
138
|
end
|
135
139
|
|
140
|
+
def build_simple_value_name(name_node, name_part_node, alternative_name_nodes, parallel)
|
141
|
+
name_value_hash = build_name_part(name_node, name_part_node, default_type: alternative_name_nodes.present?)
|
142
|
+
display_form_node = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS).first
|
143
|
+
if display_form_node.present? && !parallel
|
144
|
+
cocina_contrib_name =
|
145
|
+
{
|
146
|
+
parallelValue: [
|
147
|
+
name_value_hash
|
148
|
+
]
|
149
|
+
}
|
150
|
+
add_display_parallel_value(name_node, cocina_contrib_name)
|
151
|
+
else
|
152
|
+
name_value_hash
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def build_structured_value_name(name_node, name_part_nodes)
|
157
|
+
vals = name_part_nodes.filter_map { |name_part_node| build_name_part(name_node, name_part_node, default_type: name_node['type'] != 'corporate').presence }
|
158
|
+
display_form_node = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS).first
|
159
|
+
if display_form_node.present?
|
160
|
+
cocina_contrib_name =
|
161
|
+
{
|
162
|
+
parallelValue: [
|
163
|
+
{ structuredValue: vals }
|
164
|
+
]
|
165
|
+
}
|
166
|
+
add_display_parallel_value(name_node, cocina_contrib_name)
|
167
|
+
else
|
168
|
+
{ structuredValue: vals }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def add_display_parallel_value(name_node, cocina_contrib_name)
|
173
|
+
display_form = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS)&.text
|
174
|
+
return cocina_contrib_name if display_form.blank?
|
175
|
+
|
176
|
+
display_parallel_value = display_value(name_node)
|
177
|
+
cocina_contrib_name[:parallelValue] << display_parallel_value if display_parallel_value && cocina_contrib_name[:parallelValue].present?
|
178
|
+
|
179
|
+
cocina_contrib_name
|
180
|
+
end
|
181
|
+
|
182
|
+
def display_value(name_node)
|
183
|
+
display_form = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS)&.text
|
184
|
+
{ value: display_form, type: 'display' } if display_form.present?
|
185
|
+
end
|
186
|
+
|
136
187
|
def build_name_part(name_node, name_part_node, default_type: true)
|
137
188
|
if name_part_node.content.blank? && !name_part_node['xlink:href']
|
138
189
|
notifier.warn('name/namePart missing value')
|
@@ -224,12 +275,8 @@ module Cocina
|
|
224
275
|
end.presence
|
225
276
|
end
|
226
277
|
|
227
|
-
def build_notes(name_node)
|
278
|
+
def build_notes(name_node, is_parallel:)
|
228
279
|
[].tap do |parts|
|
229
|
-
name_node.xpath('mods:affiliation', mods: Description::DESC_METADATA_NS).each do |affiliation_node|
|
230
|
-
parts << { value: affiliation_node.text, type: 'affiliation' }
|
231
|
-
end
|
232
|
-
|
233
280
|
description = name_node.xpath('mods:description', mods: Description::DESC_METADATA_NS).first
|
234
281
|
if description
|
235
282
|
parts << if description.text == UNCITED_DESCRIPTION
|
@@ -238,9 +285,18 @@ module Cocina
|
|
238
285
|
{ value: description.text, type: 'description' }
|
239
286
|
end
|
240
287
|
end
|
288
|
+
parts.concat(build_affiliation_notes(name_node)) unless is_parallel
|
241
289
|
end.presence
|
242
290
|
end
|
243
291
|
|
292
|
+
def build_affiliation_notes(name_node)
|
293
|
+
[].tap do |parts|
|
294
|
+
name_node.xpath('mods:affiliation', mods: Description::DESC_METADATA_NS).each do |affiliation_node|
|
295
|
+
parts << { value: affiliation_node.text, type: 'affiliation' }
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
244
300
|
def build_roles(name_node)
|
245
301
|
role_nodes = name_node.xpath('mods:role', mods: Description::DESC_METADATA_NS)
|
246
302
|
role_nodes.filter_map { |role_node| role_for(role_node) }.presence
|
@@ -20,6 +20,7 @@ module Cocina
|
|
20
20
|
|
21
21
|
def normalize
|
22
22
|
normalize_parallel_name_role
|
23
|
+
normalize_parallel_affiliation_note
|
23
24
|
normalize_text_role_term
|
24
25
|
normalize_role_term
|
25
26
|
normalize_role # must be after normalize_role_term
|
@@ -35,10 +36,13 @@ module Cocina
|
|
35
36
|
|
36
37
|
attr_reader :ng_xml
|
37
38
|
|
39
|
+
def grouped_name_nodes
|
40
|
+
name_nodes = ng_xml.root.xpath('//mods:name[@altRepGroup]', mods: ModsNormalizer::MODS_NS)
|
41
|
+
name_nodes.group_by { |name_node| name_node['altRepGroup'] }.values.reject { |name_node_group| name_node_group.size == 1 }
|
42
|
+
end
|
43
|
+
|
38
44
|
def normalize_parallel_name_role
|
39
45
|
# For parallel names, all should have the same roles.
|
40
|
-
name_nodes = ng_xml.root.xpath('//mods:name[@altRepGroup]', mods: ModsNormalizer::MODS_NS)
|
41
|
-
grouped_name_nodes = name_nodes.group_by { |name_node| name_node['altRepGroup'] }.values.reject { |name_node_group| name_node_group.size == 1 }
|
42
46
|
grouped_name_nodes.each do |name_node_group|
|
43
47
|
name_node_with_role = name_node_group.find { |name_node| role_node_for(name_node) }
|
44
48
|
next unless name_node_with_role
|
@@ -58,6 +62,27 @@ module Cocina
|
|
58
62
|
name_node.xpath('mods:role', mods: ModsNormalizer::MODS_NS).first
|
59
63
|
end
|
60
64
|
|
65
|
+
def normalize_parallel_affiliation_note
|
66
|
+
# For parallel names, all should have the same affiliation.
|
67
|
+
grouped_name_nodes.each do |name_node_group|
|
68
|
+
name_node_with_affiliation = name_node_group.find { |name_node| affiliation_node_for(name_node) }
|
69
|
+
next unless name_node_with_affiliation
|
70
|
+
|
71
|
+
name_node_group.each do |name_node|
|
72
|
+
next if name_node == name_node_with_affiliation
|
73
|
+
|
74
|
+
existing_affiliation_node = affiliation_node_for(name_node)
|
75
|
+
existing_affiliation_node&.remove
|
76
|
+
|
77
|
+
name_node << affiliation_node_for(name_node_with_affiliation).dup
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def affiliation_node_for(name_node)
|
83
|
+
name_node.xpath('mods:affiliation', mods: ModsNormalizer::MODS_NS).first
|
84
|
+
end
|
85
|
+
|
61
86
|
def normalize_text_role_term
|
62
87
|
# Add the type="text" attribute to roleTerms that don't have a type (seen in MODS 3.3 druid:yy910cj7795)
|
63
88
|
ng_xml.root.xpath('//mods:roleTerm[not(@type)]', mods: ModsNormalizer::MODS_NS).each do |role_term_node|
|
@@ -36,25 +36,11 @@ module Cocina
|
|
36
36
|
if contributor.type == 'unspecified others'
|
37
37
|
write_etal
|
38
38
|
elsif contributor.name.present?
|
39
|
+
# Expect contributor to have a single value for name property
|
39
40
|
contrib_name = contributor.name.first
|
40
|
-
|
41
|
-
if
|
42
|
-
|
43
|
-
parallel_values.each do |parallel_contrib_name|
|
44
|
-
Cocina::Models::Builders::NameTitleGroupBuilder.value_slices(parallel_contrib_name)&.each do |parallel_contrib_name_slice|
|
45
|
-
if name_title_vals_index[parallel_contrib_name_slice]
|
46
|
-
name_title_group = name_title_vals_index[parallel_contrib_name_slice]&.values&.first
|
47
|
-
write_parallel_contributor(contributor, contrib_name, parallel_contrib_name,
|
48
|
-
name_title_group, altrepgroup_id)
|
49
|
-
else
|
50
|
-
# TODO: want a way to notify that we hit a problem - either notifier or HB error (issue #3751)
|
51
|
-
# OR validate for semantic correctness upon creation/update so we can't get here.
|
52
|
-
# notifier.warn("For contributor name '#{parallel_contrib_name_val}', no title matching '#{title_from_contrib}'")
|
53
|
-
write_parallel_contributor(contributor, contrib_name, parallel_contrib_name, nil,
|
54
|
-
altrepgroup_id)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
41
|
+
parallel_name_values = contrib_name.parallelValue
|
42
|
+
if parallel_name_values.present?
|
43
|
+
write_contributor_with_parallel_names(parallel_name_values, contrib_name)
|
58
44
|
else
|
59
45
|
write_contributor(contributor)
|
60
46
|
end
|
@@ -105,7 +91,50 @@ module Cocina
|
|
105
91
|
end
|
106
92
|
end
|
107
93
|
|
108
|
-
def
|
94
|
+
def write_contributor_with_parallel_names(parallel_name_values, contrib_name)
|
95
|
+
display_type_parallel_name = display_type_parallel_name(parallel_name_values)
|
96
|
+
if parallel_name_values.size == 1
|
97
|
+
contrib_name_value_slice = Cocina::Models::Builders::NameTitleGroupBuilder.value_slices(parallel_name_values.first)
|
98
|
+
name_title_group = name_title_vals_index[contrib_name_value_slice]&.values&.first
|
99
|
+
write_name_from_parallel(contributor, contributor.name.first, parallel_name_values, name_title_group, nil)
|
100
|
+
elsif parallel_name_values.size == 2 && display_type_parallel_name
|
101
|
+
contrib_name_value_slice = Cocina::Models::Builders::NameTitleGroupBuilder.value_slices(parallel_name_values.first)
|
102
|
+
name_title_group = name_title_vals_index[contrib_name_value_slice]&.values&.first
|
103
|
+
write_name_with_display_form(contributor, contributor.name.first, parallel_name_values, 0, name_title_group, nil)
|
104
|
+
else
|
105
|
+
write_multiple_parallel_contributors(parallel_name_values, contrib_name)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def write_multiple_parallel_contributors(parallel_name_values, contrib_name)
|
110
|
+
altrepgroup_id = id_generator.next_altrepgroup
|
111
|
+
parallel_name_values.each_with_index do |parallel_contrib_name, index|
|
112
|
+
display_name_present = parallel_name_values[index + 1].present? && parallel_name_values[index + 1].type == 'display'
|
113
|
+
Cocina::Models::Builders::NameTitleGroupBuilder.value_slices(parallel_contrib_name)&.each do |parallel_contrib_name_slice|
|
114
|
+
if name_title_vals_index[parallel_contrib_name_slice]
|
115
|
+
name_title_group = name_title_vals_index[parallel_contrib_name_slice]&.values&.first
|
116
|
+
if display_name_present
|
117
|
+
# associate type 'display' with the previous value
|
118
|
+
write_name_with_display_form(contributor, contrib_name, parallel_name_values, index, name_title_group, altrepgroup_id)
|
119
|
+
else
|
120
|
+
write_name_from_parallel(contributor, contrib_name, parallel_contrib_name, name_title_group, altrepgroup_id)
|
121
|
+
end
|
122
|
+
elsif display_name_present
|
123
|
+
# TODO: want a way to notify that we hit a problem - either notifier or HB error (issue #3751)
|
124
|
+
# OR validate for semantic correctness upon creation/update so we can't get here.
|
125
|
+
# notifier.warn("For contributor name '#{parallel_contrib_name_slice}', no contrib matching")
|
126
|
+
write_name_with_display_form(contributor, contrib_name, parallel_name_values, index, nil, altrepgroup_id)
|
127
|
+
elsif parallel_name_values[index].type == 'display'
|
128
|
+
# we assume we included this as part of a previous name
|
129
|
+
next
|
130
|
+
else
|
131
|
+
write_name_from_parallel(contributor, contrib_name, parallel_contrib_name, nil, altrepgroup_id)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def write_name_from_parallel(contributor, name, parallel_name, name_title_group, altrepgroup_id)
|
109
138
|
attributes = parallel_name_attributes(name, parallel_name, name_title_group, altrepgroup_id)
|
110
139
|
type_attr = NAME_TYPE.fetch(contributor.type, name_title_group ? 'personal' : nil)
|
111
140
|
attributes[:type] = type_attr if type_attr
|
@@ -121,6 +150,37 @@ module Cocina
|
|
121
150
|
end
|
122
151
|
end
|
123
152
|
|
153
|
+
# rubocop:disable Metrics/ParameterLists
|
154
|
+
def write_name_with_display_form(contributor, name, parallel_name_values, index, name_title_group, altrepgroup_id)
|
155
|
+
display_type_parallel_name = display_type_parallel_name(parallel_name_values)
|
156
|
+
parallel_name = parallel_name_values[index]
|
157
|
+
return if parallel_name.blank?
|
158
|
+
|
159
|
+
attributes = if altrepgroup_id.present?
|
160
|
+
parallel_name_attributes(name, parallel_name, name_title_group, altrepgroup_id)
|
161
|
+
else
|
162
|
+
name_attributes(contributor, name, nil)
|
163
|
+
end
|
164
|
+
type_attr = NAME_TYPE.fetch(contributor.type, name_title_group ? 'personal' : nil)
|
165
|
+
attributes[:type] = type_attr if type_attr
|
166
|
+
xml.name attributes do
|
167
|
+
if parallel_name.structuredValue.present?
|
168
|
+
write_structured(parallel_name)
|
169
|
+
else
|
170
|
+
write_basic(parallel_name)
|
171
|
+
end
|
172
|
+
write_display_form(display_type_parallel_name) if display_type_parallel_name.present?
|
173
|
+
write_identifier(contributor) if contributor.identifier.present?
|
174
|
+
write_note(contributor)
|
175
|
+
write_roles(contributor)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
# rubocop:enable Metrics/ParameterLists
|
179
|
+
|
180
|
+
def display_type_parallel_name(parallel_name_values)
|
181
|
+
parallel_name_values.detect { |parallel_value| parallel_value.type == 'display' }
|
182
|
+
end
|
183
|
+
|
124
184
|
def parallel_name_attributes(name, parallel_name, name_title_group, altrepgroup_id)
|
125
185
|
{
|
126
186
|
nameTitleGroup: name_title_group,
|
@@ -199,8 +259,6 @@ module Cocina
|
|
199
259
|
xml.affiliation note.value
|
200
260
|
when 'description'
|
201
261
|
xml.description note.value
|
202
|
-
when 'citation status'
|
203
|
-
xml.description UNCITED_DESCRIPTION if note.value == 'false'
|
204
262
|
end
|
205
263
|
end
|
206
264
|
end
|
@@ -394,7 +394,7 @@ module Cocina
|
|
394
394
|
xml.name name_attrs do
|
395
395
|
write_name_part(subject_value)
|
396
396
|
write_display_form(display_values)
|
397
|
-
write_roles(
|
397
|
+
write_roles(subject_value.note)
|
398
398
|
write_other_notes(subject.note, 'description')
|
399
399
|
write_other_notes(subject.note, 'affiliation')
|
400
400
|
end
|
@@ -410,7 +410,7 @@ module Cocina
|
|
410
410
|
write_display_form(display_values)
|
411
411
|
write_roles(subject.note)
|
412
412
|
write_other_notes(subject.note, 'description')
|
413
|
-
write_other_notes(
|
413
|
+
write_other_notes(subject_value.note, 'affiliation')
|
414
414
|
end
|
415
415
|
write_genres(subject_value)
|
416
416
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# A request to create a DRO. This has the same general structure as a DRO but doesn't have externalIdentifier and doesn't require the access subschema. If no access subschema is provided, these values will be inherited from the AdminPolicy.
|
5
6
|
class RequestDRO < Struct
|
6
7
|
include Validatable
|
7
8
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# A sequence or ordering of resources within a Collection or Object.
|
5
6
|
class Sequence < Struct
|
6
7
|
attribute :members, Types::Strict::Array.of(Types::Strict::String).default([].freeze)
|
7
8
|
# The direction that a sequence of canvases should be displayed to the user
|
data/lib/cocina/models/source.rb
CHANGED
@@ -11,7 +11,7 @@ module Cocina
|
|
11
11
|
|
12
12
|
def initialize(clazz, attributes)
|
13
13
|
@clazz = clazz
|
14
|
-
@attributes = attributes
|
14
|
+
@attributes = attributes
|
15
15
|
@error_paths = []
|
16
16
|
end
|
17
17
|
|
@@ -29,9 +29,7 @@ module Cocina
|
|
29
29
|
attr_reader :clazz, :attributes, :error_paths
|
30
30
|
|
31
31
|
def meets_preconditions?
|
32
|
-
|
33
|
-
titles_with_associated_name_note_for(resource).present?
|
34
|
-
end
|
32
|
+
[Cocina::Models::Description, Cocina::Models::RequestDescription].include?(clazz)
|
35
33
|
end
|
36
34
|
|
37
35
|
def valid?(resource)
|
@@ -11,7 +11,7 @@ module Cocina
|
|
11
11
|
|
12
12
|
def initialize(clazz, attributes)
|
13
13
|
@clazz = clazz
|
14
|
-
@attributes = attributes
|
14
|
+
@attributes = attributes
|
15
15
|
@error_paths = []
|
16
16
|
end
|
17
17
|
|
@@ -30,8 +30,7 @@ module Cocina
|
|
30
30
|
attr_reader :clazz, :attributes, :error_paths
|
31
31
|
|
32
32
|
def meets_preconditions?
|
33
|
-
|
34
|
-
Cocina::Models::RequestDescription].include?(clazz)
|
33
|
+
[Cocina::Models::Description, Cocina::Models::RequestDescription].include?(clazz)
|
35
34
|
end
|
36
35
|
|
37
36
|
def validate_hash(hash, path)
|
@@ -11,7 +11,7 @@ module Cocina
|
|
11
11
|
|
12
12
|
def initialize(clazz, attributes)
|
13
13
|
@clazz = clazz
|
14
|
-
@attributes = attributes
|
14
|
+
@attributes = attributes
|
15
15
|
@error_paths = []
|
16
16
|
end
|
17
17
|
|
@@ -30,8 +30,7 @@ module Cocina
|
|
30
30
|
attr_reader :clazz, :attributes, :error_paths
|
31
31
|
|
32
32
|
def meets_preconditions?
|
33
|
-
|
34
|
-
Cocina::Models::RequestDescription].include?(clazz)
|
33
|
+
[Cocina::Models::Description, Cocina::Models::RequestDescription].include?(clazz)
|
35
34
|
end
|
36
35
|
|
37
36
|
def validate_hash(hash, path)
|
@@ -26,8 +26,7 @@ module Cocina
|
|
26
26
|
# In the meantime, copying code.
|
27
27
|
attributes_hash = deep_transform_values(attributes.to_h) do |value|
|
28
28
|
value.class.name.starts_with?('Cocina::Models') ? value.to_h : value
|
29
|
-
end.with_indifferent_access
|
30
|
-
|
29
|
+
end.deep_symbolize_keys.with_indifferent_access
|
31
30
|
VALIDATORS.each { |validator| validator.validate(clazz, attributes_hash) }
|
32
31
|
end
|
33
32
|
|
data/lib/cocina/models.rb
CHANGED
@@ -119,7 +119,7 @@ module Cocina
|
|
119
119
|
# @param [DROWithMetadata,CollectionWithMetadata,AdminPolicyWithMetadata] cocina_object
|
120
120
|
# @return [DRO,Collection,AdminPolicy]
|
121
121
|
def self.without_metadata(cocina_object)
|
122
|
-
build(cocina_object.to_h.except(:created, :modified, :lock))
|
122
|
+
build(cocina_object.to_h.except(:created, :modified, :lock), validate: false)
|
123
123
|
end
|
124
124
|
|
125
125
|
# Adds metadata to a DRO, Collection, AdminPolicy
|
@@ -144,7 +144,7 @@ module Cocina
|
|
144
144
|
else
|
145
145
|
AdminPolicyWithMetadata
|
146
146
|
end
|
147
|
-
clazz.new(props)
|
147
|
+
clazz.new(props, false, false)
|
148
148
|
end
|
149
149
|
|
150
150
|
def self.type_for(dyn)
|
@@ -45,24 +45,31 @@ module Cocina
|
|
45
45
|
type: Cocina::Models::ObjectType.object,
|
46
46
|
id: 'druid:bc234fg5678',
|
47
47
|
version: 1,
|
48
|
-
label: '
|
49
|
-
title: '
|
48
|
+
label: 'factory DRO label',
|
49
|
+
title: 'factory DRO title',
|
50
50
|
source_id: 'sul:1234',
|
51
51
|
admin_policy_id: 'druid:hv992ry2431'
|
52
52
|
}.freeze
|
53
53
|
|
54
54
|
REQUEST_DRO_DEFAULTS = DRO_DEFAULTS.except(:id)
|
55
55
|
|
56
|
-
COLLECTION_DEFAULTS =
|
56
|
+
COLLECTION_DEFAULTS = {
|
57
|
+
type: Cocina::Models::ObjectType.collection,
|
58
|
+
id: 'druid:bb222ff5555',
|
59
|
+
version: 1,
|
60
|
+
label: 'factory collection label',
|
61
|
+
title: 'factory collection title',
|
62
|
+
admin_policy_id: 'druid:hv992ry2431'
|
63
|
+
}.freeze
|
57
64
|
|
58
65
|
REQUEST_COLLECTION_DEFAULTS = COLLECTION_DEFAULTS.except(:id)
|
59
66
|
|
60
67
|
ADMIN_POLICY_DEFAULTS = {
|
61
68
|
type: Cocina::Models::ObjectType.admin_policy,
|
62
|
-
id: 'druid:
|
69
|
+
id: 'druid:cb432gf8765',
|
63
70
|
version: 1,
|
64
|
-
label: '
|
65
|
-
title: '
|
71
|
+
label: 'factory APO label',
|
72
|
+
title: 'factory APO title',
|
66
73
|
admin_policy_id: 'druid:hv992ry2431',
|
67
74
|
agreement_id: 'druid:hp308wm0436'
|
68
75
|
}.freeze
|
data/openapi.yml
CHANGED
@@ -779,6 +779,7 @@ components:
|
|
779
779
|
$ref: "#/components/schemas/DescriptiveValue"
|
780
780
|
DescriptiveParallelContributor:
|
781
781
|
description: Value model for multiple representations of information about the same contributor (e.g. in different languages).
|
782
|
+
deprecated: true
|
782
783
|
type: object
|
783
784
|
additionalProperties: false
|
784
785
|
properties:
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cocina-models
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.81.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Coyne
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|