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.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +40 -11
  3. data/.rubocop_todo.yml +71 -2
  4. data/README.md +19 -3
  5. data/cocina-models.gemspec +2 -0
  6. data/description_types.yml +168 -39
  7. data/docs/description_types.md +471 -216
  8. data/lib/cocina/generator/generator.rb +7 -15
  9. data/lib/cocina/generator/schema.rb +1 -3
  10. data/lib/cocina/generator/schema_base.rb +0 -8
  11. data/lib/cocina/generator/schema_ref.rb +1 -1
  12. data/lib/cocina/generator/schema_value.rb +14 -4
  13. data/lib/cocina/models/access.rb +4 -4
  14. data/lib/cocina/models/admin_policy.rb +1 -1
  15. data/lib/cocina/models/admin_policy_access_template.rb +7 -7
  16. data/lib/cocina/models/admin_policy_administrative.rb +1 -1
  17. data/lib/cocina/models/admin_policy_with_metadata.rb +3 -3
  18. data/lib/cocina/models/builders/dro_rights_description_builder.rb +69 -0
  19. data/lib/cocina/models/builders/name_title_group_builder.rb +130 -0
  20. data/lib/cocina/models/builders/rights_description_builder.rb +83 -0
  21. data/lib/cocina/models/builders/title_builder.rb +211 -0
  22. data/lib/cocina/models/citation_only_access.rb +2 -2
  23. data/lib/cocina/models/collection_access.rb +4 -4
  24. data/lib/cocina/models/collection_identification.rb +1 -1
  25. data/lib/cocina/models/collection_with_metadata.rb +2 -2
  26. data/lib/cocina/models/contributor.rb +4 -4
  27. data/lib/cocina/models/controlled_digital_lending_access.rb +2 -2
  28. data/lib/cocina/models/dark_access.rb +4 -4
  29. data/lib/cocina/models/description.rb +3 -3
  30. data/lib/cocina/models/descriptive_basic_value.rb +13 -13
  31. data/lib/cocina/models/descriptive_parallel_contributor.rb +5 -5
  32. data/lib/cocina/models/descriptive_parallel_event.rb +3 -3
  33. data/lib/cocina/models/descriptive_value.rb +13 -13
  34. data/lib/cocina/models/descriptive_value_language.rb +6 -6
  35. data/lib/cocina/models/dro.rb +1 -1
  36. data/lib/cocina/models/dro_access.rb +8 -8
  37. data/lib/cocina/models/dro_with_metadata.rb +3 -3
  38. data/lib/cocina/models/embargo.rb +5 -5
  39. data/lib/cocina/models/event.rb +3 -3
  40. data/lib/cocina/models/file.rb +4 -4
  41. data/lib/cocina/models/file_access.rb +4 -4
  42. data/lib/cocina/models/identification.rb +2 -2
  43. data/lib/cocina/models/language.rb +12 -12
  44. data/lib/cocina/models/location_based_access.rb +1 -1
  45. data/lib/cocina/models/location_based_download_access.rb +1 -1
  46. data/lib/cocina/models/mapping/error_notifier.rb +36 -0
  47. data/lib/cocina/models/mapping/from_mods/access.rb +177 -0
  48. data/lib/cocina/models/mapping/from_mods/admin_metadata.rb +217 -0
  49. data/lib/cocina/models/mapping/from_mods/alt_rep_group.rb +26 -0
  50. data/lib/cocina/models/mapping/from_mods/authority.rb +51 -0
  51. data/lib/cocina/models/mapping/from_mods/contributor.rb +161 -0
  52. data/lib/cocina/models/mapping/from_mods/description.rb +98 -0
  53. data/lib/cocina/models/mapping/from_mods/description_builder.rb +61 -0
  54. data/lib/cocina/models/mapping/from_mods/event.rb +543 -0
  55. data/lib/cocina/models/mapping/from_mods/form.rb +381 -0
  56. data/lib/cocina/models/mapping/from_mods/geographic.rb +219 -0
  57. data/lib/cocina/models/mapping/from_mods/hydrus_default_title_builder.rb +28 -0
  58. data/lib/cocina/models/mapping/from_mods/identifier.rb +51 -0
  59. data/lib/cocina/models/mapping/from_mods/identifier_builder.rb +71 -0
  60. data/lib/cocina/models/mapping/from_mods/identifier_type.rb +292 -0
  61. data/lib/cocina/models/mapping/from_mods/language.rb +36 -0
  62. data/lib/cocina/models/mapping/from_mods/language_script.rb +30 -0
  63. data/lib/cocina/models/mapping/from_mods/language_term.rb +106 -0
  64. data/lib/cocina/models/mapping/from_mods/name_builder.rb +307 -0
  65. data/lib/cocina/models/mapping/from_mods/note.rb +162 -0
  66. data/lib/cocina/models/mapping/from_mods/part_builder.rb +147 -0
  67. data/lib/cocina/models/mapping/from_mods/primary.rb +27 -0
  68. data/lib/cocina/models/mapping/from_mods/purl.rb +53 -0
  69. data/lib/cocina/models/mapping/from_mods/related_resource.rb +105 -0
  70. data/lib/cocina/models/mapping/from_mods/subject.rb +413 -0
  71. data/lib/cocina/models/mapping/from_mods/subject_authority_codes.rb +794 -0
  72. data/lib/cocina/models/mapping/from_mods/title.rb +160 -0
  73. data/lib/cocina/models/mapping/from_mods/title_builder.rb +106 -0
  74. data/lib/cocina/models/mapping/from_mods/title_builder_strategy.rb +19 -0
  75. data/lib/cocina/models/mapping/from_mods/value_uri.rb +25 -0
  76. data/lib/cocina/models/mapping/normalizers/base.rb +16 -0
  77. data/lib/cocina/models/mapping/normalizers/mods/geo_extension_normalizer.rb +69 -0
  78. data/lib/cocina/models/mapping/normalizers/mods/name_normalizer.rb +191 -0
  79. data/lib/cocina/models/mapping/normalizers/mods/origin_info_normalizer.rb +157 -0
  80. data/lib/cocina/models/mapping/normalizers/mods/subject_normalizer.rb +296 -0
  81. data/lib/cocina/models/mapping/normalizers/mods/title_normalizer.rb +91 -0
  82. data/lib/cocina/models/mapping/normalizers/mods_normalizer.rb +409 -0
  83. data/lib/cocina/models/mapping/purl.rb +27 -0
  84. data/lib/cocina/models/mapping/to_mods/access.rb +155 -0
  85. data/lib/cocina/models/mapping/to_mods/admin_metadata.rb +129 -0
  86. data/lib/cocina/models/mapping/to_mods/contributor.rb +49 -0
  87. data/lib/cocina/models/mapping/to_mods/description.rb +63 -0
  88. data/lib/cocina/models/mapping/to_mods/event.rb +200 -0
  89. data/lib/cocina/models/mapping/to_mods/form.rb +292 -0
  90. data/lib/cocina/models/mapping/to_mods/geographic.rb +151 -0
  91. data/lib/cocina/models/mapping/to_mods/id_generator.rb +25 -0
  92. data/lib/cocina/models/mapping/to_mods/identifier.rb +57 -0
  93. data/lib/cocina/models/mapping/to_mods/language.rb +82 -0
  94. data/lib/cocina/models/mapping/to_mods/mods_writer.rb +38 -0
  95. data/lib/cocina/models/mapping/to_mods/name_title_group.rb +29 -0
  96. data/lib/cocina/models/mapping/to_mods/name_writer.rb +228 -0
  97. data/lib/cocina/models/mapping/to_mods/note.rb +105 -0
  98. data/lib/cocina/models/mapping/to_mods/part_writer.rb +115 -0
  99. data/lib/cocina/models/mapping/to_mods/related_resource.rb +108 -0
  100. data/lib/cocina/models/mapping/to_mods/role_writer.rb +50 -0
  101. data/lib/cocina/models/mapping/to_mods/subject.rb +486 -0
  102. data/lib/cocina/models/mapping/to_mods/title.rb +260 -0
  103. data/lib/cocina/models/object_metadata.rb +2 -2
  104. data/lib/cocina/models/presentation.rb +2 -2
  105. data/lib/cocina/models/related_resource.rb +9 -9
  106. data/lib/cocina/models/release_tag.rb +4 -4
  107. data/lib/cocina/models/request_admin_policy.rb +1 -1
  108. data/lib/cocina/models/request_administrative.rb +1 -1
  109. data/lib/cocina/models/request_collection.rb +2 -2
  110. data/lib/cocina/models/request_description.rb +3 -3
  111. data/lib/cocina/models/request_dro.rb +4 -4
  112. data/lib/cocina/models/request_file.rb +5 -5
  113. data/lib/cocina/models/request_identification.rb +1 -1
  114. data/lib/cocina/models/sequence.rb +1 -1
  115. data/lib/cocina/models/source.rb +4 -4
  116. data/lib/cocina/models/standard.rb +5 -5
  117. data/lib/cocina/models/stanford_access.rb +2 -2
  118. data/lib/cocina/models/title.rb +13 -13
  119. data/lib/cocina/models/validators/associated_name_validator.rb +77 -0
  120. data/lib/cocina/models/validators/dark_validator.rb +4 -2
  121. data/lib/cocina/models/validators/open_api_validator.rb +0 -4
  122. data/lib/cocina/models/validators/validator.rb +1 -0
  123. data/lib/cocina/models/version.rb +1 -1
  124. data/lib/cocina/models/world_access.rb +2 -2
  125. data/lib/cocina/models.rb +4 -0
  126. data/lib/cocina/rspec/factories.rb +205 -0
  127. data/lib/cocina/rspec.rb +2 -0
  128. data/openapi.yml +4 -4
  129. metadata +97 -24
  130. data/docs/_config.yml +0 -1
  131. data/docs/maps/Agent.json +0 -18
  132. data/docs/maps/Collection.json +0 -240
  133. data/docs/maps/DRO.json +0 -316
  134. data/docs/maps/Description.json +0 -17
  135. data/docs/maps/File.json +0 -196
  136. data/docs/maps/Fileset.json +0 -143
  137. data/docs/maps/README.md +0 -7
  138. data/docs/maps/ReleaseTag.json +0 -39
  139. data/docs/maps/Sequence.json +0 -46
  140. data/docs/maps/Title.json +0 -18
  141. data/docs/sampleETD/foxml-export.xml +0 -935
  142. data/docs/sampleETD/foxml.xml +0 -3475
  143. data/docs/sampleETD/xn109qc9773_bibframe.ttl +0 -95
  144. data/docs/sampleETD/xn109qc9773_taco.json +0 -158
  145. data/lib/cocina/models/dro_rights_description_builder.rb +0 -67
  146. data/lib/cocina/models/rights_description_builder.rb +0 -81
  147. data/lib/cocina/models/title_builder.rb +0 -208
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module ToMods
7
+ # Maps adminMetadata from cocina to MODS XML recordInfo
8
+ class AdminMetadata
9
+ # @params [Nokogiri::XML::Builder] xml
10
+ # @params [Cocina::Models::DescriptiveAdminMetadata] admin_metadata
11
+ def self.write(xml:, admin_metadata:)
12
+ new(xml: xml, admin_metadata: admin_metadata).write
13
+ end
14
+
15
+ def initialize(xml:, admin_metadata:)
16
+ @xml = xml
17
+ @admin_metadata = admin_metadata
18
+ end
19
+
20
+ def write
21
+ return unless admin_metadata
22
+
23
+ xml.recordInfo do
24
+ build_language
25
+ build_content_source
26
+ build_description_standard
27
+ build_record_origin
28
+ build_record_info_notes
29
+ build_event
30
+ build_identifier
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :xml, :admin_metadata
37
+
38
+ def build_record_origin
39
+ Array(admin_metadata.note).select { |note| note.type == 'record origin' }.each do |note|
40
+ xml.recordOrigin note.value
41
+ end
42
+ end
43
+
44
+ def build_record_info_notes
45
+ record_info_notes = Array(admin_metadata.note)
46
+ # return unless record_info_notes
47
+ return if record_info_notes.empty?
48
+
49
+ record_info_notes.each do |info_note|
50
+ if info_note.valueAt.present?
51
+ xml.recordInfoNote nil, { 'xlink:href' => info_note.valueAt }
52
+ elsif info_note.type == 'record information'
53
+ xml.recordInfoNote info_note.value
54
+ end
55
+ end
56
+ end
57
+
58
+ def build_content_source
59
+ Array(admin_metadata.contributor).each do |contributor|
60
+ source = contributor.name.first
61
+ xml.recordContentSource source.code || source.value, with_uri_info(source)
62
+ end
63
+ end
64
+
65
+ def build_description_standard
66
+ return if admin_metadata.metadataStandard.blank?
67
+
68
+ admin_metadata.metadataStandard.each do |standard|
69
+ xml.descriptionStandard standard.code || standard.value, with_uri_info(standard)
70
+ end
71
+ end
72
+
73
+ def build_event
74
+ build_event_for('creation', 'recordCreationDate')
75
+ build_event_for('modification', 'recordChangeDate')
76
+ end
77
+
78
+ def build_event_for(type, tag)
79
+ Array(admin_metadata.event).select { |note| note.type == type }.each do |event|
80
+ event.date.each do |date|
81
+ attributes = {}.tap do |attrs|
82
+ attrs[:encoding] = date.encoding&.code
83
+ end.compact
84
+
85
+ xml.public_send(tag, date.value, attributes)
86
+ end
87
+ end
88
+ end
89
+
90
+ def build_identifier
91
+ Array(admin_metadata.identifier).each do |identifier|
92
+ id_attributes = {
93
+ displayLabel: identifier.displayLabel,
94
+ source: identifier.uri ? 'uri' : Cocina::Models::Mapping::FromMods::IdentifierType.mods_type_for_cocina_type(identifier.type)
95
+ }.tap do |attrs|
96
+ attrs[:invalid] = 'yes' if identifier.status == 'invalid'
97
+ end.compact
98
+ xml.recordIdentifier identifier.value || identifier.uri, id_attributes
99
+ end
100
+ end
101
+
102
+ def build_language
103
+ Array(admin_metadata.language).each do |language|
104
+ language_of_cataloging_attrs = {}
105
+ language_of_cataloging_attrs[:usage] = language.status if language.status
106
+ xml.languageOfCataloging language_of_cataloging_attrs do
107
+ language_attrs = with_uri_info(language, {})
108
+ xml.languageTerm language.value, language_attrs.merge(type: 'text') if language.value
109
+ xml.languageTerm language.code, language_attrs.merge(type: 'code') if language.code
110
+ if language.script
111
+ script_attrs = with_uri_info(language.script, {})
112
+ xml.scriptTerm language.script.value, script_attrs.merge(type: 'text')
113
+ xml.scriptTerm language.script.code, script_attrs.merge(type: 'code')
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ def with_uri_info(cocina, xml_attrs = {})
120
+ xml_attrs[:valueURI] = cocina.uri
121
+ xml_attrs[:authorityURI] = cocina.source&.uri
122
+ xml_attrs[:authority] = cocina.source&.code
123
+ xml_attrs.compact
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module ToMods
7
+ # Maps contributors from cocina to MODS XML
8
+ class Contributor
9
+ # one way mapping: MODS 'corporate' already maps to Cocina 'organization'
10
+ NAME_TYPE = Cocina::Models::Mapping::FromMods::Contributor::ROLES.invert.merge('event' => 'corporate').freeze
11
+ NAME_PART = Cocina::Models::Mapping::FromMods::Contributor::NAME_PART.invert.freeze
12
+
13
+ # NOTE: contributors in nameTitleGroups are output as part of Title.
14
+ # @params [Nokogiri::XML::Builder] xml
15
+ # @params [Array<Cocina::Models::Contributor>] contributors
16
+ # @params [Array<Cocina::Models::Title>] titles
17
+ # @params [IdGenerator] id_generator
18
+ def self.write(xml:, contributors:, titles:, id_generator:)
19
+ new(xml: xml, contributors: contributors, titles: titles, id_generator: id_generator).write
20
+ end
21
+
22
+ def initialize(xml:, contributors:, titles:, id_generator:)
23
+ @xml = xml
24
+ @contributors = contributors
25
+ @titles = titles
26
+ @id_generator = id_generator
27
+ end
28
+
29
+ # NOTE: contributors in nameTitleGroups are output as part of Title.
30
+ def write
31
+ Array(contributors)
32
+ .reject do |contributor|
33
+ NameTitleGroup.in_name_title_group?(contributor: contributor,
34
+ titles: titles)
35
+ end
36
+ .each do |contributor|
37
+ NameWriter.write(xml: xml, contributor: contributor,
38
+ id_generator: id_generator)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :xml, :contributors, :titles, :id_generator
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+
5
+ module Cocina
6
+ module Models
7
+ module Mapping
8
+ module ToMods
9
+ # This transforms the DRO.description schema to MODS xml
10
+ class Description
11
+ # @param [Cocina::Models::Description] description
12
+ # @param [string] druid
13
+ # @return [Nokogiri::XML::Document]
14
+ def self.transform(description, druid)
15
+ new(description, druid).transform
16
+ end
17
+
18
+ def initialize(description, druid)
19
+ @description = description
20
+ @druid = druid
21
+ end
22
+
23
+ # @return [Nokogiri::XML::Document]
24
+ def transform
25
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
26
+ xml.mods(mods_attributes) do
27
+ ModsWriter.write(xml: xml, description: description, druid: druid)
28
+ end
29
+ end.doc
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :description, :druid
35
+
36
+ def mods_version
37
+ @mods_version ||= begin
38
+ notes = description.adminMetadata&.note || []
39
+ notes.select { |note| note.type == 'record origin' }.each do |note|
40
+ match = /MODS version (\d\.\d)/.match(note.value)
41
+ return match[1] if match
42
+ end
43
+ '3.7'
44
+ end
45
+ end
46
+
47
+ def mods_attributes
48
+ {
49
+ 'xmlns' => 'http://www.loc.gov/mods/v3',
50
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
51
+ 'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
52
+ 'xmlns:xlink' => 'http://www.w3.org/1999/xlink',
53
+ 'version' => mods_version,
54
+ 'xsi:schemaLocation' => "http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-#{mods_version.sub(
55
+ '.', '-'
56
+ )}.xsd"
57
+ }
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module ToMods
7
+ # Maps events from cocina to MODS XML
8
+ class Event # rubocop:disable Metrics/ClassLength
9
+ # @params [Nokogiri::XML::Builder] xml
10
+ # @params [Array<Cocina::Models::Event>] events
11
+ # @params [IdGenerator] id_generator
12
+ def self.write(xml:, events:, id_generator:)
13
+ new(xml: xml, events: events, id_generator: id_generator).write
14
+ end
15
+
16
+ def initialize(xml:, events:, id_generator:)
17
+ @xml = xml
18
+ @events = events
19
+ @id_generator = id_generator
20
+ end
21
+
22
+ def write
23
+ Array(events).each do |event|
24
+ if event.parallelEvent.present?
25
+ write_parallel(event, id_generator.next_altrepgroup)
26
+ else
27
+ write_basic(event, event.type)
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :xml, :events, :id_generator
35
+
36
+ def write_basic(event, event_type)
37
+ names = Array(event.contributor).map { |contributor| contributor.name.first }
38
+ attributes = {
39
+ displayLabel: event.displayLabel,
40
+ eventType: event_type,
41
+ script: event.valueLanguage&.valueScript&.code,
42
+ lang: event.valueLanguage&.code
43
+ }.compact
44
+
45
+ write_event(event.type, event.date, event.location, names, event.note, attributes)
46
+ end
47
+
48
+ def write_parallel(event, alt_rep_group)
49
+ event.parallelEvent.each do |parallel_event|
50
+ attributes = {}.tap do |attrs|
51
+ attrs[:script] = parallel_event.valueLanguage&.valueScript&.code
52
+ attrs[:lang] = parallel_event.valueLanguage&.code
53
+ attrs[:altRepGroup] = alt_rep_group
54
+ attrs[:eventType] = event.type || parallel_event.type
55
+ attrs[:displayLabel] = event.displayLabel || parallel_event.displayLabel
56
+ end.compact
57
+
58
+ names = Array(parallel_event.contributor).map(&:name).flatten
59
+ write_event(parallel_event.type,
60
+ parallel_event.date,
61
+ parallel_event.location,
62
+ names,
63
+ parallel_event.note,
64
+ attributes)
65
+ end
66
+ end
67
+
68
+ # rubocop:disable Metrics/ParameterLists
69
+ def write_event(cocina_event_type, dates, locations, names, notes, attributes, is_parallel: false)
70
+ return if dates.blank? && locations.blank? && names.blank? && notes.blank?
71
+
72
+ xml.originInfo attributes do
73
+ Array(dates).each do |date|
74
+ write_basic_date(date, cocina_event_type)
75
+ end
76
+ Array(locations).each do |loc|
77
+ write_location(loc)
78
+ end
79
+ Array(names).each do |name|
80
+ xml.publisher name.value, name_attributes(name)
81
+ end
82
+ Array(notes).each do |note|
83
+ write_note(note)
84
+ end
85
+ end
86
+ end
87
+ # rubocop:enable Metrics/ParameterLists
88
+
89
+ def write_note(note)
90
+ attributes = {}
91
+ attributes[:authority] = note.source.code if note&.source&.code
92
+ xml.send(note_tag_for(note), note.value, attributes)
93
+ end
94
+
95
+ def note_tag_for(note)
96
+ return 'copyrightDate' if note.type == 'copyright statement'
97
+ return note.type if note.type
98
+
99
+ 'edition'
100
+ end
101
+
102
+ def write_location(location)
103
+ place_attrs = {}.tap do |attrs|
104
+ attrs[:supplied] = 'yes' if location.type == 'supplied'
105
+ end
106
+ xml.place place_attrs do
107
+ placeterm_attrs = {}
108
+ placeterm_attrs[:authority] = location.source.code if location.source&.code
109
+ placeterm_attrs[:authorityURI] = location.source.uri if location.source&.uri
110
+ placeterm_attrs[:valueURI] = location.uri if location.uri
111
+
112
+ placeterm_text_attrs = placeterm_attrs.merge({ type: 'text' })
113
+ xml.placeTerm location.value, placeterm_text_attrs if location.value
114
+
115
+ placeterm_code_attrs = placeterm_attrs.merge({ type: 'code' })
116
+ xml.placeTerm location.code, placeterm_code_attrs if location.code
117
+ end
118
+ end
119
+
120
+ def write_basic_date(date, cocina_event_type)
121
+ if date.structuredValue.present?
122
+ structured_val_attribs = {
123
+ encoding: date.encoding,
124
+ qualifier: date.qualifier,
125
+ type: date.type
126
+ }
127
+ date_type = cocina_event_type
128
+ date_type = date.type if date.type.present?
129
+ date_range(date.structuredValue, date_type, structured_val_attribs)
130
+ else
131
+ write_date_element(date, cocina_event_type)
132
+ end
133
+ end
134
+
135
+ # @param [Hash] structured_val_attribs - populated when structuredValue parent has attributes for individual date elements
136
+ def write_date_element(date, cocina_event_type, structured_val_attribs = {})
137
+ value = date.value
138
+ element_name = date_element_name(date, cocina_event_type, structured_val_attribs[:type])
139
+ attributes = {}.tap do |attrs|
140
+ attrs[:encoding] = date.encoding&.code || structured_val_attribs[:encoding]&.code
141
+ attrs[:qualifier] = date.qualifier || structured_val_attribs[:qualifier]
142
+ attrs[:keyDate] = 'yes' if date.status == 'primary'
143
+ attrs[:type] = date_type_attr(date, element_name)
144
+ attrs[:calendar] = calendar_attr(date)
145
+ attrs[:point] = date.type if %w[start end].include?(date.type)
146
+ end.compact
147
+ xml.public_send(element_name, value, attributes)
148
+ end
149
+
150
+ def date_element_name(date, cocina_event_type, structured_val_type)
151
+ el_name = Cocina::Models::Mapping::FromMods::Event::DATE_ELEMENTS_2_TYPE.key(structured_val_type)
152
+ return el_name if el_name.present?
153
+
154
+ el_name = Cocina::Models::Mapping::FromMods::Event::DATE_ELEMENTS_2_TYPE.key(date.type)
155
+ return el_name if el_name.present?
156
+
157
+ # the date type overrides the eventType for choosing date flavor, even if it ends up with dateOther
158
+ return 'dateOther' if date.type.present?
159
+
160
+ if cocina_event_type.present?
161
+ el_name = Cocina::Models::Mapping::FromMods::Event::DATE_ELEMENTS_2_TYPE.key(cocina_event_type)
162
+ return el_name if el_name.present?
163
+ end
164
+
165
+ 'dateOther'
166
+ end
167
+
168
+ # MODS only allows a type attr on dateOther; no other date flavors
169
+ def date_type_attr(cocina_date, date_element_name)
170
+ return unless date_element_name == 'dateOther'
171
+
172
+ cocina_date.type if cocina_date.type.present? && %w[start end].exclude?(cocina_date.type)
173
+ end
174
+
175
+ def calendar_attr(cocina_date)
176
+ return if cocina_date.note.blank?
177
+
178
+ cocina_date.note.each do |note|
179
+ return note.value if note.type == 'calendar'
180
+ end
181
+ end
182
+
183
+ def date_range(dates, cocina_event_type, structured_val_attribs)
184
+ dates.each do |date|
185
+ write_date_element(date, cocina_event_type, structured_val_attribs)
186
+ end
187
+ end
188
+
189
+ def name_attributes(cocina_name)
190
+ {
191
+ lang: cocina_name&.valueLanguage&.code,
192
+ script: cocina_name&.valueLanguage&.valueScript&.code,
193
+ transliteration: cocina_name&.standard&.value
194
+ }.compact
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end