cocina-models 0.74.1 → 0.77.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +40 -11
- data/.rubocop_todo.yml +71 -2
- data/README.md +19 -3
- data/cocina-models.gemspec +2 -0
- data/description_types.yml +168 -39
- data/docs/description_types.md +471 -216
- data/lib/cocina/generator/generator.rb +7 -15
- data/lib/cocina/generator/schema.rb +1 -3
- data/lib/cocina/generator/schema_base.rb +0 -8
- data/lib/cocina/generator/schema_ref.rb +1 -1
- data/lib/cocina/generator/schema_value.rb +14 -4
- data/lib/cocina/models/access.rb +4 -4
- data/lib/cocina/models/admin_policy.rb +1 -1
- data/lib/cocina/models/admin_policy_access_template.rb +7 -7
- data/lib/cocina/models/admin_policy_administrative.rb +1 -1
- data/lib/cocina/models/admin_policy_with_metadata.rb +3 -3
- data/lib/cocina/models/builders/dro_rights_description_builder.rb +69 -0
- data/lib/cocina/models/builders/name_title_group_builder.rb +130 -0
- data/lib/cocina/models/builders/rights_description_builder.rb +83 -0
- data/lib/cocina/models/builders/title_builder.rb +211 -0
- data/lib/cocina/models/citation_only_access.rb +2 -2
- data/lib/cocina/models/collection_access.rb +4 -4
- data/lib/cocina/models/collection_identification.rb +1 -1
- data/lib/cocina/models/collection_with_metadata.rb +2 -2
- data/lib/cocina/models/contributor.rb +4 -4
- data/lib/cocina/models/controlled_digital_lending_access.rb +2 -2
- data/lib/cocina/models/dark_access.rb +4 -4
- data/lib/cocina/models/description.rb +3 -3
- data/lib/cocina/models/descriptive_basic_value.rb +13 -13
- data/lib/cocina/models/descriptive_parallel_contributor.rb +5 -5
- data/lib/cocina/models/descriptive_parallel_event.rb +3 -3
- data/lib/cocina/models/descriptive_value.rb +13 -13
- data/lib/cocina/models/descriptive_value_language.rb +6 -6
- data/lib/cocina/models/dro.rb +1 -1
- data/lib/cocina/models/dro_access.rb +8 -8
- data/lib/cocina/models/dro_with_metadata.rb +3 -3
- data/lib/cocina/models/embargo.rb +5 -5
- data/lib/cocina/models/event.rb +3 -3
- data/lib/cocina/models/file.rb +4 -4
- data/lib/cocina/models/file_access.rb +4 -4
- data/lib/cocina/models/identification.rb +2 -2
- data/lib/cocina/models/language.rb +12 -12
- data/lib/cocina/models/location_based_access.rb +1 -1
- data/lib/cocina/models/location_based_download_access.rb +1 -1
- data/lib/cocina/models/mapping/error_notifier.rb +36 -0
- data/lib/cocina/models/mapping/from_mods/access.rb +177 -0
- data/lib/cocina/models/mapping/from_mods/admin_metadata.rb +217 -0
- data/lib/cocina/models/mapping/from_mods/alt_rep_group.rb +26 -0
- data/lib/cocina/models/mapping/from_mods/authority.rb +51 -0
- data/lib/cocina/models/mapping/from_mods/contributor.rb +161 -0
- data/lib/cocina/models/mapping/from_mods/description.rb +98 -0
- data/lib/cocina/models/mapping/from_mods/description_builder.rb +61 -0
- data/lib/cocina/models/mapping/from_mods/event.rb +543 -0
- data/lib/cocina/models/mapping/from_mods/form.rb +381 -0
- data/lib/cocina/models/mapping/from_mods/geographic.rb +219 -0
- data/lib/cocina/models/mapping/from_mods/hydrus_default_title_builder.rb +28 -0
- data/lib/cocina/models/mapping/from_mods/identifier.rb +51 -0
- data/lib/cocina/models/mapping/from_mods/identifier_builder.rb +71 -0
- data/lib/cocina/models/mapping/from_mods/identifier_type.rb +292 -0
- data/lib/cocina/models/mapping/from_mods/language.rb +36 -0
- data/lib/cocina/models/mapping/from_mods/language_script.rb +30 -0
- data/lib/cocina/models/mapping/from_mods/language_term.rb +106 -0
- data/lib/cocina/models/mapping/from_mods/name_builder.rb +307 -0
- data/lib/cocina/models/mapping/from_mods/note.rb +162 -0
- data/lib/cocina/models/mapping/from_mods/part_builder.rb +147 -0
- data/lib/cocina/models/mapping/from_mods/primary.rb +27 -0
- data/lib/cocina/models/mapping/from_mods/purl.rb +53 -0
- data/lib/cocina/models/mapping/from_mods/related_resource.rb +105 -0
- data/lib/cocina/models/mapping/from_mods/subject.rb +413 -0
- data/lib/cocina/models/mapping/from_mods/subject_authority_codes.rb +794 -0
- data/lib/cocina/models/mapping/from_mods/title.rb +160 -0
- data/lib/cocina/models/mapping/from_mods/title_builder.rb +106 -0
- data/lib/cocina/models/mapping/from_mods/title_builder_strategy.rb +19 -0
- data/lib/cocina/models/mapping/from_mods/value_uri.rb +25 -0
- data/lib/cocina/models/mapping/normalizers/base.rb +16 -0
- data/lib/cocina/models/mapping/normalizers/mods/geo_extension_normalizer.rb +69 -0
- data/lib/cocina/models/mapping/normalizers/mods/name_normalizer.rb +191 -0
- data/lib/cocina/models/mapping/normalizers/mods/origin_info_normalizer.rb +157 -0
- data/lib/cocina/models/mapping/normalizers/mods/subject_normalizer.rb +296 -0
- data/lib/cocina/models/mapping/normalizers/mods/title_normalizer.rb +91 -0
- data/lib/cocina/models/mapping/normalizers/mods_normalizer.rb +409 -0
- data/lib/cocina/models/mapping/purl.rb +27 -0
- data/lib/cocina/models/mapping/to_mods/access.rb +155 -0
- data/lib/cocina/models/mapping/to_mods/admin_metadata.rb +129 -0
- data/lib/cocina/models/mapping/to_mods/contributor.rb +49 -0
- data/lib/cocina/models/mapping/to_mods/description.rb +63 -0
- data/lib/cocina/models/mapping/to_mods/event.rb +200 -0
- data/lib/cocina/models/mapping/to_mods/form.rb +292 -0
- data/lib/cocina/models/mapping/to_mods/geographic.rb +151 -0
- data/lib/cocina/models/mapping/to_mods/id_generator.rb +25 -0
- data/lib/cocina/models/mapping/to_mods/identifier.rb +57 -0
- data/lib/cocina/models/mapping/to_mods/language.rb +82 -0
- data/lib/cocina/models/mapping/to_mods/mods_writer.rb +38 -0
- data/lib/cocina/models/mapping/to_mods/name_title_group.rb +29 -0
- data/lib/cocina/models/mapping/to_mods/name_writer.rb +228 -0
- data/lib/cocina/models/mapping/to_mods/note.rb +105 -0
- data/lib/cocina/models/mapping/to_mods/part_writer.rb +115 -0
- data/lib/cocina/models/mapping/to_mods/related_resource.rb +108 -0
- data/lib/cocina/models/mapping/to_mods/role_writer.rb +50 -0
- data/lib/cocina/models/mapping/to_mods/subject.rb +486 -0
- data/lib/cocina/models/mapping/to_mods/title.rb +260 -0
- data/lib/cocina/models/object_metadata.rb +2 -2
- data/lib/cocina/models/presentation.rb +2 -2
- data/lib/cocina/models/related_resource.rb +9 -9
- data/lib/cocina/models/release_tag.rb +4 -4
- data/lib/cocina/models/request_admin_policy.rb +1 -1
- data/lib/cocina/models/request_administrative.rb +1 -1
- data/lib/cocina/models/request_collection.rb +2 -2
- data/lib/cocina/models/request_description.rb +3 -3
- data/lib/cocina/models/request_dro.rb +4 -4
- data/lib/cocina/models/request_file.rb +5 -5
- data/lib/cocina/models/request_identification.rb +1 -1
- data/lib/cocina/models/sequence.rb +1 -1
- data/lib/cocina/models/source.rb +4 -4
- data/lib/cocina/models/standard.rb +5 -5
- data/lib/cocina/models/stanford_access.rb +2 -2
- data/lib/cocina/models/title.rb +13 -13
- data/lib/cocina/models/validators/associated_name_validator.rb +77 -0
- data/lib/cocina/models/validators/dark_validator.rb +4 -2
- data/lib/cocina/models/validators/open_api_validator.rb +0 -4
- data/lib/cocina/models/validators/validator.rb +1 -0
- data/lib/cocina/models/version.rb +1 -1
- data/lib/cocina/models/world_access.rb +2 -2
- data/lib/cocina/models.rb +4 -0
- data/lib/cocina/rspec/factories.rb +205 -0
- data/lib/cocina/rspec.rb +2 -0
- data/openapi.yml +4 -4
- metadata +97 -24
- data/docs/_config.yml +0 -1
- data/docs/maps/Agent.json +0 -18
- data/docs/maps/Collection.json +0 -240
- data/docs/maps/DRO.json +0 -316
- data/docs/maps/Description.json +0 -17
- data/docs/maps/File.json +0 -196
- data/docs/maps/Fileset.json +0 -143
- data/docs/maps/README.md +0 -7
- data/docs/maps/ReleaseTag.json +0 -39
- data/docs/maps/Sequence.json +0 -46
- data/docs/maps/Title.json +0 -18
- data/docs/sampleETD/foxml-export.xml +0 -935
- data/docs/sampleETD/foxml.xml +0 -3475
- data/docs/sampleETD/xn109qc9773_bibframe.ttl +0 -95
- data/docs/sampleETD/xn109qc9773_taco.json +0 -158
- data/lib/cocina/models/dro_rights_description_builder.rb +0 -67
- data/lib/cocina/models/rights_description_builder.rb +0 -81
- data/lib/cocina/models/title_builder.rb +0 -208
@@ -0,0 +1,486 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cocina
|
4
|
+
module Models
|
5
|
+
module Mapping
|
6
|
+
module ToMods
|
7
|
+
# Maps subjects from cocina to MODS XML
|
8
|
+
# rubocop:disable Metrics/ClassLength
|
9
|
+
class Subject
|
10
|
+
TAG_NAME = {
|
11
|
+
'time' => :temporal,
|
12
|
+
'genre' => :genre,
|
13
|
+
'occupation' => :occupation
|
14
|
+
}.freeze
|
15
|
+
DEORDINAL_REGEX = /(?<=[0-9])(?:st|nd|rd|th)/.freeze
|
16
|
+
|
17
|
+
# @params [Nokogiri::XML::Builder] xml
|
18
|
+
# @params [Array<Cocina::Models::DescriptiveValue>] subjects
|
19
|
+
# @params [Array<Cocina::Models::DescriptiveValue>] forms
|
20
|
+
# @params [IdGenerator] id_generator
|
21
|
+
def self.write(xml:, subjects:, id_generator:, forms: [])
|
22
|
+
new(xml: xml, subjects: subjects, forms: forms, id_generator: id_generator).write
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(xml:, subjects:, forms:, id_generator:)
|
26
|
+
@xml = xml
|
27
|
+
@subjects = Array(subjects)
|
28
|
+
@forms = forms || []
|
29
|
+
@id_generator = id_generator
|
30
|
+
end
|
31
|
+
|
32
|
+
def write
|
33
|
+
subjects.each do |subject|
|
34
|
+
next if subject.type == 'map coordinates'
|
35
|
+
|
36
|
+
parallel_subject_values = Array(subject.parallelValue)
|
37
|
+
subject_value = subject
|
38
|
+
type = nil
|
39
|
+
|
40
|
+
# Make adjustments for a parallel person.
|
41
|
+
if parallel_subject_values.present? && Cocina::Models::Mapping::FromMods::Contributor::ROLES.value?(subject.type)
|
42
|
+
display_values, parallel_subject_values = parallel_subject_values.partition do |value|
|
43
|
+
value.type == 'display'
|
44
|
+
end
|
45
|
+
if parallel_subject_values.size == 1
|
46
|
+
subject_value = parallel_subject_values.first
|
47
|
+
parallel_subject_values = []
|
48
|
+
type = subject.type
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if parallel_subject_values.size > 1
|
53
|
+
write_parallel(subject, parallel_subject_values, alt_rep_group: id_generator.next_altrepgroup,
|
54
|
+
display_values: display_values)
|
55
|
+
else
|
56
|
+
write_subject(subject, subject_value, display_values: display_values, type: type)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
write_cartographic
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
attr_reader :xml, :subjects, :forms, :id_generator
|
65
|
+
|
66
|
+
def write_subject(subject, subject_value, alt_rep_group: nil, type: nil, display_values: nil)
|
67
|
+
if subject_value.structuredValue.present? || subject_value.groupedValue.present?
|
68
|
+
write_structured_or_grouped(subject, subject_value, alt_rep_group: alt_rep_group, type: type,
|
69
|
+
display_values: display_values)
|
70
|
+
else
|
71
|
+
write_basic(subject, subject_value, alt_rep_group: alt_rep_group, type: type,
|
72
|
+
display_values: display_values)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def write_parallel(subject, subject_values, alt_rep_group:, display_values: nil)
|
77
|
+
# A geographic and geographicCode get written as a single subject.
|
78
|
+
if geographic_and_geographic_code?(subject, subject_values)
|
79
|
+
xml.subject do
|
80
|
+
subject_values.each do |geo|
|
81
|
+
geographic(subject, geo, is_parallel: true)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
subject_values.each do |subject_value|
|
86
|
+
write_subject(subject, subject_value, alt_rep_group: alt_rep_group, type: subject.type,
|
87
|
+
display_values: display_values)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def geographic_and_geographic_code?(subject, subject_values)
|
93
|
+
subject.type == 'place' &&
|
94
|
+
subject_values.count(&:value) == 1 &&
|
95
|
+
subject_values.count(&:code) == 1
|
96
|
+
end
|
97
|
+
|
98
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
99
|
+
def write_structured_or_grouped(subject, subject_value, alt_rep_group: nil, type: nil, display_values: nil)
|
100
|
+
type ||= subject_value.type || subject.type
|
101
|
+
xml.subject(structured_attributes_for(subject_value, type, alt_rep_group: alt_rep_group)) do
|
102
|
+
if type == 'place' && subject_value.structuredValue.present?
|
103
|
+
hierarchical_geographic(subject_value)
|
104
|
+
elsif type == 'time'
|
105
|
+
time_range(subject_value)
|
106
|
+
elsif type == 'title'
|
107
|
+
write_title(subject_value)
|
108
|
+
elsif Cocina::Models::Mapping::FromMods::Contributor::ROLES.value?(type)
|
109
|
+
write_structured_person(subject, subject_value, type: type, display_values: display_values)
|
110
|
+
else
|
111
|
+
values = subject_value.structuredValue.presence || subject_value.groupedValue
|
112
|
+
values.each do |value|
|
113
|
+
if Cocina::Models::Mapping::FromMods::Contributor::ROLES.value?(value.type)
|
114
|
+
if value.structuredValue.present?
|
115
|
+
write_structured_person(subject, value, display_values: display_values)
|
116
|
+
elsif value.parallelValue.present?
|
117
|
+
write_parallel_structured_person(value)
|
118
|
+
else
|
119
|
+
write_person(subject, value, display_values: display_values)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
write_topic(subject, value, is_parallel: alt_rep_group.present?, type: type,
|
123
|
+
subject_values_have_same_authority: all_values_have_same_authority?(values))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
130
|
+
|
131
|
+
def write_title(subject_value)
|
132
|
+
title = subject_value.to_h
|
133
|
+
title.delete(:type)
|
134
|
+
title.delete(:source)
|
135
|
+
title.delete(:valueLanguage)
|
136
|
+
Title.write(xml: xml, titles: [Cocina::Models::DescriptiveValue.new(title)], id_generator: id_generator)
|
137
|
+
end
|
138
|
+
|
139
|
+
def structured_attributes_for(subject_value, type, alt_rep_group: nil)
|
140
|
+
values = subject_value.structuredValue.presence || subject_value.groupedValue
|
141
|
+
{
|
142
|
+
altRepGroup: alt_rep_group,
|
143
|
+
displayLabel: subject_value.displayLabel,
|
144
|
+
lang: subject_value.valueLanguage&.code,
|
145
|
+
script: subject_value.valueLanguage&.valueScript&.code
|
146
|
+
}.tap do |attrs|
|
147
|
+
if type == 'person'
|
148
|
+
attrs[:authority] = authority_for(subject_value) # unless subject.source&.code == 'naf' && subject_value.source&.code == 'naf'
|
149
|
+
else
|
150
|
+
attrs[:valueURI] = subject_value.uri
|
151
|
+
if subject_value.source
|
152
|
+
# If all values in structuredValue have uri, then authority only.
|
153
|
+
attrs[:authority] = authority_for(subject_value)
|
154
|
+
if !all_values_have_uri?(values) || subject_value.uri
|
155
|
+
attrs[:authorityURI] =
|
156
|
+
subject_value.source.uri
|
157
|
+
end
|
158
|
+
elsif all_values_have_lcsh_authority?(values)
|
159
|
+
# No source, but all values in structuredValue are lcsh or naf then add authority
|
160
|
+
attrs[:authority] = 'lcsh'
|
161
|
+
elsif subject_value.type == 'place' && all_values_have_same_authority?(values)
|
162
|
+
attrs[:authority] = authority_for(values.first)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end.compact
|
166
|
+
end
|
167
|
+
|
168
|
+
def all_values_have_uri?(values)
|
169
|
+
values.present? && Array(values).all?(&:uri)
|
170
|
+
end
|
171
|
+
|
172
|
+
def all_values_have_lcsh_authority?(values)
|
173
|
+
values.present? && Array(values).all? { |value| authority_for(value) == 'lcsh' }
|
174
|
+
end
|
175
|
+
|
176
|
+
def all_values_have_same_authority?(values)
|
177
|
+
return false if values.blank?
|
178
|
+
|
179
|
+
check_authority = authority_for(values.first)
|
180
|
+
return false if check_authority.nil?
|
181
|
+
|
182
|
+
values.all? { |value| authority_for(value) == check_authority }
|
183
|
+
end
|
184
|
+
|
185
|
+
def write_basic(subject, subject_value, alt_rep_group: nil, type: nil, display_values: nil)
|
186
|
+
subject_attributes = subject_attributes_for(subject_value, alt_rep_group)
|
187
|
+
type ||= subject_value.type
|
188
|
+
|
189
|
+
if type == 'classification'
|
190
|
+
write_classification(subject_value.value, subject_attributes)
|
191
|
+
elsif Cocina::Models::Mapping::FromMods::Contributor::ROLES.value?(type) || type == 'name'
|
192
|
+
xml.subject(subject_attributes) do
|
193
|
+
write_person(subject, subject_value, display_values: display_values)
|
194
|
+
end
|
195
|
+
elsif !type && !subject_value.value
|
196
|
+
# For subject only (no children).
|
197
|
+
xml.subject subject_attributes.merge(topic_attributes_for(subject, subject_value, type))
|
198
|
+
else
|
199
|
+
xml.subject(subject_attributes) do
|
200
|
+
write_topic(subject, subject_value, type: type)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def subject_attributes_for(subject, alt_rep_group)
|
206
|
+
{
|
207
|
+
altRepGroup: alt_rep_group,
|
208
|
+
authority: authority_for(subject),
|
209
|
+
lang: subject.valueLanguage&.code,
|
210
|
+
script: subject.valueLanguage&.valueScript&.code,
|
211
|
+
usage: subject.status
|
212
|
+
}.tap do |attrs|
|
213
|
+
attrs[:displayLabel] = subject.displayLabel unless subject.type == 'genre'
|
214
|
+
attrs[:edition] = edition(subject.source.version) if subject.source&.version
|
215
|
+
attrs['xlink:href'] = subject.valueAt
|
216
|
+
end.compact
|
217
|
+
end
|
218
|
+
|
219
|
+
def authority_for(subject)
|
220
|
+
# Both lcsh and naf map to lcsh for the subject.
|
221
|
+
return 'lcsh' if %w[lcsh naf].include?(subject.source&.code)
|
222
|
+
|
223
|
+
subject.source&.code
|
224
|
+
end
|
225
|
+
|
226
|
+
def write_classification(value, attrs)
|
227
|
+
xml.classification value, attrs
|
228
|
+
end
|
229
|
+
|
230
|
+
# Write nodes within MODS subject
|
231
|
+
def write_topic(subject, subject_value, is_parallel: false, type: nil, subject_values_have_same_authority: true)
|
232
|
+
type ||= subject_value.type
|
233
|
+
topic_attributes = topic_attributes_for(subject, subject_value, type, is_parallel: is_parallel,
|
234
|
+
subject_values_have_same_authority: subject_values_have_same_authority)
|
235
|
+
case type
|
236
|
+
when 'person'
|
237
|
+
xml.name topic_attributes.merge(type: 'personal') do
|
238
|
+
xml.namePart(subject_value.value) if subject_value.value
|
239
|
+
end
|
240
|
+
when 'name'
|
241
|
+
xml.name topic_attributes do
|
242
|
+
xml.namePart(subject_value.value) if subject_value.value
|
243
|
+
end
|
244
|
+
when 'title'
|
245
|
+
title = subject_value.to_h
|
246
|
+
title.delete(:type)
|
247
|
+
title.delete(:valueLanguage)
|
248
|
+
title[:source].delete(:code) if subject_value.source&.code && !topic_attributes[:authority]
|
249
|
+
Title.write(xml: xml, titles: [Cocina::Models::DescriptiveValue.new(title)],
|
250
|
+
id_generator: id_generator, additional_attrs: topic_attributes)
|
251
|
+
when 'place'
|
252
|
+
geographic(subject, subject_value, is_parallel: is_parallel)
|
253
|
+
else
|
254
|
+
xml.public_send(TAG_NAME.fetch(subject_value.type, :topic), subject_value.value, topic_attributes)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def topic_attributes_for(subject, subject_value, type, is_parallel: false, subject_values_have_same_authority: true)
|
259
|
+
{
|
260
|
+
authority: authority_for_topic(subject, subject_value, type, is_parallel,
|
261
|
+
subject_values_have_same_authority),
|
262
|
+
authorityURI: subject_value.source&.uri,
|
263
|
+
encoding: subject_value.encoding&.code,
|
264
|
+
valueURI: subject_value.uri
|
265
|
+
}.tap do |topic_attributes|
|
266
|
+
if subject_value.type == 'genre'
|
267
|
+
topic_attributes[:displayLabel] = subject_value.displayLabel
|
268
|
+
topic_attributes[:usage] = subject_value.status
|
269
|
+
end
|
270
|
+
end.compact
|
271
|
+
end
|
272
|
+
|
273
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
274
|
+
def authority_for_topic(subject, subject_value, type, is_parallel, subject_values_have_same_authority)
|
275
|
+
return nil unless subject_value.source&.uri ||
|
276
|
+
subject_value.uri ||
|
277
|
+
(type == 'place' && is_parallel) ||
|
278
|
+
(subject_value.source&.code && subject.source&.code && subject.source.code != subject_value.source.code) ||
|
279
|
+
(subject.source&.code == 'naf' && subject_value.source&.code == 'naf' && type == 'person') ||
|
280
|
+
(subject_value.source&.code && !subject_values_have_same_authority)
|
281
|
+
|
282
|
+
subject_value.source&.code
|
283
|
+
end
|
284
|
+
|
285
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
286
|
+
def geographic(subject, subject_value, is_parallel: false)
|
287
|
+
if subject_value.code
|
288
|
+
xml.geographicCode subject_value.code,
|
289
|
+
topic_attributes_for(subject, subject_value, 'place', is_parallel: is_parallel)
|
290
|
+
else
|
291
|
+
xml.geographic subject_value.value,
|
292
|
+
topic_attributes_for(subject, subject_value, 'place', is_parallel: is_parallel)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def time_range(subject)
|
297
|
+
subject.structuredValue.each do |point|
|
298
|
+
xml.temporal point.value, point: point.type, encoding: subject.encoding.code
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def write_cartographic
|
303
|
+
parallel_forms, other_forms = forms.partition { |form| form.parallelValue.present? }
|
304
|
+
|
305
|
+
parallel_forms.each do |parallel_form|
|
306
|
+
alt_rep_group = id_generator.next_altrepgroup
|
307
|
+
parallel_form.parallelValue.each do |form|
|
308
|
+
write_parallel_cartographic_without_authority([form], alt_rep_group: alt_rep_group)
|
309
|
+
write_cartographic_with_authority([form], alt_rep_group: alt_rep_group)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
write_cartographic_without_authority(other_forms)
|
314
|
+
write_cartographic_with_authority(other_forms)
|
315
|
+
end
|
316
|
+
|
317
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
318
|
+
def write_cartographic_without_authority(forms)
|
319
|
+
# With all subject/forms without authorities.
|
320
|
+
scale_forms = forms.select do |form|
|
321
|
+
form.type == 'map scale'
|
322
|
+
end.flat_map { |form| form.groupedValue.presence || form }
|
323
|
+
projection_forms = forms.select { |form| form.type == 'map projection' && form.source.nil? }
|
324
|
+
carto_subjects = subjects.select { |subject| subject.type == 'map coordinates' }
|
325
|
+
return unless scale_forms.present? || projection_forms.present? || carto_subjects.present?
|
326
|
+
|
327
|
+
xml.subject do
|
328
|
+
xml.cartographics do
|
329
|
+
scale_forms.each { |scale_form| xml.scale scale_form.value }
|
330
|
+
projection_forms.each { |projection_form| xml.projection projection_form.value }
|
331
|
+
carto_subjects.each { |carto_subject| xml.coordinates carto_subject.value }
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
336
|
+
|
337
|
+
def write_parallel_cartographic_without_authority(forms, alt_rep_group:)
|
338
|
+
# With all subject/forms without authorities.
|
339
|
+
scale_forms = forms.select do |form|
|
340
|
+
form.type == 'map scale'
|
341
|
+
end.flat_map { |form| form.groupedValue.presence || form }
|
342
|
+
projection_forms = forms.select { |form| form.type == 'map projection' && form.source.nil? }
|
343
|
+
return unless scale_forms.present? || projection_forms.present?
|
344
|
+
|
345
|
+
subject_attrs = { altRepGroup: alt_rep_group }
|
346
|
+
xml.subject subject_attrs do
|
347
|
+
xml.cartographics do
|
348
|
+
scale_forms.each { |scale_form| xml.scale scale_form.value }
|
349
|
+
projection_forms.each { |projection_form| xml.projection projection_form.value }
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def write_cartographic_with_authority(forms, alt_rep_group: nil)
|
355
|
+
# Each for form with authority.
|
356
|
+
projection_forms_with_authority = forms.select do |form|
|
357
|
+
form.type == 'map projection' && form.source.present?
|
358
|
+
end
|
359
|
+
projection_forms_with_authority.each do |projection_form|
|
360
|
+
xml.subject carto_subject_attributes_for(projection_form, alt_rep_group: alt_rep_group) do
|
361
|
+
xml.cartographics do
|
362
|
+
xml.projection projection_form.value
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
def carto_subject_attributes_for(form, alt_rep_group: nil)
|
369
|
+
{
|
370
|
+
displayLabel: form.displayLabel,
|
371
|
+
authority: form.source&.code,
|
372
|
+
authorityURI: form.source&.uri,
|
373
|
+
valueURI: form.uri,
|
374
|
+
altRepGroup: alt_rep_group
|
375
|
+
}.compact
|
376
|
+
end
|
377
|
+
|
378
|
+
def hierarchical_geographic(subject)
|
379
|
+
xml.hierarchicalGeographic do
|
380
|
+
subject.structuredValue.each do |structured_value|
|
381
|
+
xml.send(camelize(structured_value.type), structured_value.value)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def camelize(str)
|
387
|
+
str.tr(' ', '_').camelize(:lower)
|
388
|
+
end
|
389
|
+
|
390
|
+
def write_person(subject, subject_value, display_values: nil)
|
391
|
+
name_attrs = topic_attributes_for(subject, subject_value, 'person').tap do |attrs|
|
392
|
+
attrs[:type] = name_type_for(subject.type || subject_value.type)
|
393
|
+
end.compact
|
394
|
+
xml.name name_attrs do
|
395
|
+
write_name_part(subject_value)
|
396
|
+
write_display_form(display_values)
|
397
|
+
write_roles(subject.note)
|
398
|
+
write_other_notes(subject.note, 'description')
|
399
|
+
write_other_notes(subject.note, 'affiliation')
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def write_structured_person(subject, subject_value, type: nil, display_values: nil)
|
404
|
+
type ||= subject_value.type
|
405
|
+
name_attrs = topic_attributes_for(subject, subject_value, type).tap do |attrs|
|
406
|
+
attrs[:type] = name_type_for(type)
|
407
|
+
end.compact
|
408
|
+
xml.name name_attrs do
|
409
|
+
write_name_parts(subject_value)
|
410
|
+
write_display_form(display_values)
|
411
|
+
write_roles(subject.note)
|
412
|
+
write_other_notes(subject.note, 'description')
|
413
|
+
write_other_notes(subject.note, 'affiliation')
|
414
|
+
end
|
415
|
+
write_genres(subject_value)
|
416
|
+
end
|
417
|
+
|
418
|
+
def write_display_form(display_values)
|
419
|
+
Array(display_values).each do |display_value|
|
420
|
+
xml.displayForm display_value.value
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def write_roles(notes)
|
425
|
+
Array(notes).filter do |note|
|
426
|
+
note.type == 'role'
|
427
|
+
end.each { |role| RoleWriter.write(xml: xml, role: role) }
|
428
|
+
end
|
429
|
+
|
430
|
+
def write_other_notes(notes, type)
|
431
|
+
Array(notes).filter { |note| note.type == type }.each { |note| xml.public_send(type, note.value) }
|
432
|
+
end
|
433
|
+
|
434
|
+
def write_name_parts(descriptive_value)
|
435
|
+
descriptive_value
|
436
|
+
.structuredValue
|
437
|
+
.reject { |value| value.type == 'genre' }
|
438
|
+
.each { |value| write_name_part(value) }
|
439
|
+
end
|
440
|
+
|
441
|
+
def write_genres(descriptive_value)
|
442
|
+
descriptive_value
|
443
|
+
.structuredValue
|
444
|
+
.select { |value| value.type == 'genre' }
|
445
|
+
.each { |genre| xml.genre genre.value }
|
446
|
+
end
|
447
|
+
|
448
|
+
def write_name_part(name_part)
|
449
|
+
return unless name_part.value
|
450
|
+
|
451
|
+
attributes = {}.tap do |attrs|
|
452
|
+
attrs[:type] = NameWriter::NAME_PART[name_part.type]
|
453
|
+
end.compact
|
454
|
+
xml.namePart name_part.value, attributes
|
455
|
+
write_other_notes(name_part.note, 'affiliation')
|
456
|
+
end
|
457
|
+
|
458
|
+
def name_type_for(type)
|
459
|
+
Cocina::Models::Mapping::FromMods::Contributor::ROLES.invert[type]
|
460
|
+
end
|
461
|
+
|
462
|
+
def edition(version)
|
463
|
+
version.split.first.gsub(DEORDINAL_REGEX, '')
|
464
|
+
end
|
465
|
+
|
466
|
+
def write_parallel_structured_person(value)
|
467
|
+
parallel_subject_values = Array(value.parallelValue)
|
468
|
+
display_values, parallel_subject_values = parallel_subject_values.partition do |par_value|
|
469
|
+
par_value.type == 'display'
|
470
|
+
end
|
471
|
+
|
472
|
+
# there will not be more than one parallelValue within a structuredValue
|
473
|
+
parallel_subject_value = parallel_subject_values.first
|
474
|
+
if parallel_subject_value.structuredValue.present?
|
475
|
+
write_structured_person(value, parallel_subject_value, type: value.type,
|
476
|
+
display_values: display_values)
|
477
|
+
else
|
478
|
+
write_person(value, parallel_subject_value, display_values: display_values)
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
# rubocop:enable Metrics/ClassLength
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|