cocina-models 0.75.0 → 0.76.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +40 -12
  3. data/.rubocop_todo.yml +71 -2
  4. data/README.md +10 -3
  5. data/cocina-models.gemspec +2 -0
  6. data/description_types.yml +165 -38
  7. data/docs/description_types.md +469 -216
  8. data/lib/cocina/generator/generator.rb +7 -12
  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/name_title_group_builder.rb +0 -4
  19. data/lib/cocina/models/builders/title_builder.rb +0 -2
  20. data/lib/cocina/models/citation_only_access.rb +2 -2
  21. data/lib/cocina/models/collection_access.rb +4 -4
  22. data/lib/cocina/models/collection_identification.rb +1 -1
  23. data/lib/cocina/models/collection_with_metadata.rb +2 -2
  24. data/lib/cocina/models/contributor.rb +4 -4
  25. data/lib/cocina/models/controlled_digital_lending_access.rb +2 -2
  26. data/lib/cocina/models/dark_access.rb +4 -4
  27. data/lib/cocina/models/description.rb +3 -3
  28. data/lib/cocina/models/descriptive_basic_value.rb +13 -13
  29. data/lib/cocina/models/descriptive_parallel_contributor.rb +5 -5
  30. data/lib/cocina/models/descriptive_parallel_event.rb +3 -3
  31. data/lib/cocina/models/descriptive_value.rb +13 -13
  32. data/lib/cocina/models/descriptive_value_language.rb +6 -6
  33. data/lib/cocina/models/dro.rb +1 -1
  34. data/lib/cocina/models/dro_access.rb +8 -8
  35. data/lib/cocina/models/dro_with_metadata.rb +3 -3
  36. data/lib/cocina/models/embargo.rb +5 -5
  37. data/lib/cocina/models/event.rb +3 -3
  38. data/lib/cocina/models/file.rb +4 -4
  39. data/lib/cocina/models/file_access.rb +4 -4
  40. data/lib/cocina/models/identification.rb +2 -2
  41. data/lib/cocina/models/language.rb +12 -12
  42. data/lib/cocina/models/location_based_access.rb +1 -1
  43. data/lib/cocina/models/location_based_download_access.rb +1 -1
  44. data/lib/cocina/models/mapping/error_notifier.rb +36 -0
  45. data/lib/cocina/models/mapping/from_mods/access.rb +177 -0
  46. data/lib/cocina/models/mapping/from_mods/admin_metadata.rb +217 -0
  47. data/lib/cocina/models/mapping/from_mods/alt_rep_group.rb +26 -0
  48. data/lib/cocina/models/mapping/from_mods/authority.rb +51 -0
  49. data/lib/cocina/models/mapping/from_mods/contributor.rb +161 -0
  50. data/lib/cocina/models/mapping/from_mods/description.rb +99 -0
  51. data/lib/cocina/models/mapping/from_mods/description_builder.rb +61 -0
  52. data/lib/cocina/models/mapping/from_mods/event.rb +543 -0
  53. data/lib/cocina/models/mapping/from_mods/form.rb +381 -0
  54. data/lib/cocina/models/mapping/from_mods/geographic.rb +219 -0
  55. data/lib/cocina/models/mapping/from_mods/hydrus_default_title_builder.rb +28 -0
  56. data/lib/cocina/models/mapping/from_mods/identifier.rb +51 -0
  57. data/lib/cocina/models/mapping/from_mods/identifier_builder.rb +71 -0
  58. data/lib/cocina/models/mapping/from_mods/identifier_type.rb +292 -0
  59. data/lib/cocina/models/mapping/from_mods/language.rb +36 -0
  60. data/lib/cocina/models/mapping/from_mods/language_script.rb +30 -0
  61. data/lib/cocina/models/mapping/from_mods/language_term.rb +106 -0
  62. data/lib/cocina/models/mapping/from_mods/name_builder.rb +307 -0
  63. data/lib/cocina/models/mapping/from_mods/note.rb +162 -0
  64. data/lib/cocina/models/mapping/from_mods/part_builder.rb +147 -0
  65. data/lib/cocina/models/mapping/from_mods/primary.rb +27 -0
  66. data/lib/cocina/models/mapping/from_mods/purl.rb +53 -0
  67. data/lib/cocina/models/mapping/from_mods/related_resource.rb +105 -0
  68. data/lib/cocina/models/mapping/from_mods/subject.rb +413 -0
  69. data/lib/cocina/models/mapping/from_mods/subject_authority_codes.rb +794 -0
  70. data/lib/cocina/models/mapping/from_mods/title.rb +160 -0
  71. data/lib/cocina/models/mapping/from_mods/title_builder.rb +106 -0
  72. data/lib/cocina/models/mapping/from_mods/title_builder_strategy.rb +19 -0
  73. data/lib/cocina/models/mapping/from_mods/value_uri.rb +25 -0
  74. data/lib/cocina/models/mapping/normalizers/base.rb +16 -0
  75. data/lib/cocina/models/mapping/normalizers/mods/geo_extension_normalizer.rb +69 -0
  76. data/lib/cocina/models/mapping/normalizers/mods/name_normalizer.rb +191 -0
  77. data/lib/cocina/models/mapping/normalizers/mods/origin_info_normalizer.rb +157 -0
  78. data/lib/cocina/models/mapping/normalizers/mods/subject_normalizer.rb +296 -0
  79. data/lib/cocina/models/mapping/normalizers/mods/title_normalizer.rb +91 -0
  80. data/lib/cocina/models/mapping/normalizers/mods_normalizer.rb +409 -0
  81. data/lib/cocina/models/mapping/purl.rb +28 -0
  82. data/lib/cocina/models/mapping/to_mods/access.rb +155 -0
  83. data/lib/cocina/models/mapping/to_mods/admin_metadata.rb +129 -0
  84. data/lib/cocina/models/mapping/to_mods/contributor.rb +49 -0
  85. data/lib/cocina/models/mapping/to_mods/description.rb +63 -0
  86. data/lib/cocina/models/mapping/to_mods/event.rb +200 -0
  87. data/lib/cocina/models/mapping/to_mods/form.rb +292 -0
  88. data/lib/cocina/models/mapping/to_mods/geographic.rb +151 -0
  89. data/lib/cocina/models/mapping/to_mods/id_generator.rb +25 -0
  90. data/lib/cocina/models/mapping/to_mods/identifier.rb +57 -0
  91. data/lib/cocina/models/mapping/to_mods/language.rb +82 -0
  92. data/lib/cocina/models/mapping/to_mods/mods_writer.rb +38 -0
  93. data/lib/cocina/models/mapping/to_mods/name_title_group.rb +29 -0
  94. data/lib/cocina/models/mapping/to_mods/name_writer.rb +228 -0
  95. data/lib/cocina/models/mapping/to_mods/note.rb +105 -0
  96. data/lib/cocina/models/mapping/to_mods/part_writer.rb +115 -0
  97. data/lib/cocina/models/mapping/to_mods/related_resource.rb +108 -0
  98. data/lib/cocina/models/mapping/to_mods/role_writer.rb +50 -0
  99. data/lib/cocina/models/mapping/to_mods/subject.rb +486 -0
  100. data/lib/cocina/models/mapping/to_mods/title.rb +260 -0
  101. data/lib/cocina/models/object_metadata.rb +2 -2
  102. data/lib/cocina/models/presentation.rb +2 -2
  103. data/lib/cocina/models/related_resource.rb +9 -9
  104. data/lib/cocina/models/release_tag.rb +4 -4
  105. data/lib/cocina/models/request_admin_policy.rb +1 -1
  106. data/lib/cocina/models/request_administrative.rb +1 -1
  107. data/lib/cocina/models/request_collection.rb +2 -2
  108. data/lib/cocina/models/request_description.rb +3 -3
  109. data/lib/cocina/models/request_dro.rb +4 -4
  110. data/lib/cocina/models/request_file.rb +5 -5
  111. data/lib/cocina/models/request_identification.rb +1 -1
  112. data/lib/cocina/models/sequence.rb +1 -1
  113. data/lib/cocina/models/source.rb +4 -4
  114. data/lib/cocina/models/standard.rb +5 -5
  115. data/lib/cocina/models/stanford_access.rb +2 -2
  116. data/lib/cocina/models/title.rb +13 -13
  117. data/lib/cocina/models/validators/dark_validator.rb +4 -2
  118. data/lib/cocina/models/validators/open_api_validator.rb +0 -4
  119. data/lib/cocina/models/version.rb +1 -1
  120. data/lib/cocina/models/world_access.rb +2 -2
  121. data/lib/cocina/models.rb +4 -0
  122. data/lib/cocina/rspec/factories.rb +157 -0
  123. data/lib/cocina/rspec.rb +2 -0
  124. data/openapi.yml +4 -4
  125. metadata +88 -3
  126. data/docs/_config.yml +0 -1
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module FromMods
7
+ # Builds cocina identifier
8
+ class IdentifierBuilder
9
+ # @param [Nokogiri::XML::Element] identifier_element identifier element
10
+ # @return [Hash] a hash that can be mapped to a cocina model
11
+ def self.build_from_identifier(identifier_element:)
12
+ new(identifier_element: identifier_element, type: identifier_element[:type]).build
13
+ end
14
+
15
+ # @param [Nokogiri::XML::Element] identifier_element recordIdentifier element
16
+ # @return [Hash] a hash that can be mapped to a cocina model
17
+ def self.build_from_record_identifier(identifier_element:)
18
+ new(identifier_element: identifier_element, type: identifier_element[:source]).build
19
+ end
20
+
21
+ # @param [Nokogiri::XML::Element] identifier_element nameIdentifier element
22
+ # @return [Hash] a hash that can be mapped to a cocina model
23
+ def self.build_from_name_identifier(identifier_element:)
24
+ new(identifier_element: identifier_element, type: identifier_element[:type]).build
25
+ end
26
+
27
+ def initialize(identifier_element:, type:)
28
+ @identifier_element = identifier_element
29
+ @with_note = with_note
30
+ @cocina_type, @mods_type, = types_for(type)
31
+ end
32
+
33
+ def build
34
+ return if identifier_element.text.blank? && identifier_element.attributes.size.zero?
35
+
36
+ {
37
+ displayLabel: identifier_element['displayLabel']
38
+ }.tap do |attrs|
39
+ if cocina_type == 'uri'
40
+ attrs[:uri] = identifier_element.text
41
+ else
42
+ attrs[:type] = cocina_type
43
+ attrs[:value] = identifier_element.text
44
+ attrs[:source] = build_source
45
+ end
46
+ attrs[:status] = 'invalid' if identifier_element['invalid'] == 'yes'
47
+ end.compact
48
+ end
49
+
50
+ private
51
+
52
+ attr_reader :identifier_element, :with_note, :cocina_type, :mods_type
53
+
54
+ def types_for(type)
55
+ return ['uri', 'uri', IdentifierType::STANDARD_IDENTIFIER_SCHEMES] if type == 'uri'
56
+
57
+ IdentifierType.cocina_type_for_mods_type(type)
58
+ end
59
+
60
+ def build_source
61
+ {
62
+ uri: identifier_element['typeURI']
63
+ }.tap do |props|
64
+ props[:code] = mods_type unless props[:uri]
65
+ end.compact.presence
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,292 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module FromMods
7
+ # Maps identifier types
8
+ class IdentifierType # rubocop:disable Metrics/ClassLength
9
+ COCINA_TO_STANDARD_IDENTIFIER_SCHEMES = {
10
+ 'AGROVOC ID' => 'agrovoc',
11
+ 'AllMovie ID' => 'allmovie',
12
+ 'AllMusic ID' => 'allmusic',
13
+ 'AlloCiné ID' => 'allocine',
14
+ 'American National Biography Online ID' => 'amnbo',
15
+ 'ANSI' => 'ansi',
16
+ 'Apis ID' => 'apis',
17
+ 'Artsy ID' => 'artsy',
18
+ 'BFI ID' => 'bfi',
19
+ 'Biblioteca Nacional de España ID' => 'datoses',
20
+ 'Biographical Directory of the United States Congress ID' => 'bdusc',
21
+ 'BnF catalogue général ID' => 'bnfcg',
22
+ 'CANTIC ID' => 'cantic',
23
+ 'CGNDB ID' => 'cgndb',
24
+ 'Danacode ID' => 'danacode',
25
+ 'Det Danske Filminstitut Filmdatabasen ID' => 'dkfilm',
26
+ 'Discogs ID' => 'discogs',
27
+ 'DOI' => 'doi',
28
+ 'EAN' => 'ean',
29
+ 'EIDR ID' => 'eidr',
30
+ 'FAST' => 'fast',
31
+ 'Filmportal ID' => 'filmport',
32
+ 'Find a Grave ID' => 'findagr',
33
+ 'Freebase ID' => 'freebase',
34
+ 'Geographic Names Database ID' => 'geogndb',
35
+ 'GeoNames ID' => 'geonames',
36
+ 'GND ID' => 'gnd',
37
+ 'GNIS ID' => 'gnis',
38
+ 'Gran enciclopèdia catalana ID' => 'gec',
39
+ 'GTIN-14 ID' => 'gtin-14',
40
+ 'Handle' => 'hdl',
41
+ 'IBDB ID' => 'ibdb',
42
+ 'IdRef' => 'idref',
43
+ 'IMDb ID' => 'imdb',
44
+ 'ISAN' => 'isan',
45
+ 'ISBN' => 'isbn',
46
+ 'ISBN registrant element ID' => 'isbnre',
47
+ 'ISIL' => 'isil',
48
+ 'ISMN' => 'ismn',
49
+ 'ISNI' => 'isni',
50
+ 'ISO' => 'iso',
51
+ 'ISRC' => 'isrc',
52
+ 'ISSN' => 'issn',
53
+ 'ISSN-L' => 'issn-l',
54
+ 'ISTC' => 'istc',
55
+ 'ISWC' => 'iswc',
56
+ 'ITAR' => 'itar',
57
+ 'KinoPoisk ID' => 'kinipo',
58
+ 'LC Manuscript Division ID' => 'lcmd',
59
+ 'LCCN' => 'lccn',
60
+ 'Libraries Australia ID' => 'libaus',
61
+ 'local' => 'local',
62
+ 'matrix number' => 'matrix-number',
63
+ 'MOMA ID' => 'moma',
64
+ 'Munzinger ID' => 'munzing',
65
+ 'music plate' => 'music-plate',
66
+ 'music publisher' => 'music-publisher',
67
+ 'MusicBrainz ID' => 'musicb',
68
+ 'Número de Identificación de las Publicaciones Oficiales ID' => 'nipo',
69
+ 'National Gallery of Art ID' => 'nga',
70
+ 'National Portrait Gallery ID' => 'npg',
71
+ 'NNDB ID' => 'nndb',
72
+ 'ODNB ID' => 'odnb',
73
+ 'OpenStreetMap ID' => 'opensm',
74
+ 'ORCID' => 'orcid',
75
+ 'Oxford DNB Index ID' => 'oxforddnb',
76
+ 'PORT.hu ID' => 'porthu',
77
+ 'RBMS Binding Term' => 'rbmsbt',
78
+ 'RBMS Genre Terms ID' => 'rbmsgt',
79
+ 'RBMS Paper Term' => 'rbmspt',
80
+ 'RBMS Printing and Publishing Term' => 'rbmsppe',
81
+ 'RBMS Provenance Evidence' => 'rbmspe',
82
+ 'RBMS Relationship Designator' => 'rbmsrd',
83
+ 'RBMS Type Evidence' => 'rbmste',
84
+ 'ResearcherID' => 'rid',
85
+ 'RKDartists ID' => 'rkda',
86
+ 'Scholar Universe ID' => 'scholaru',
87
+ 'Scope ID' => 'scope',
88
+ 'Scopus Author ID' => 'scopus',
89
+ 'SICI' => 'sici',
90
+ 'Smithsonian American Art Museum ID' => 'saam',
91
+ 'sound recording issue number' => 'issue-number',
92
+ 'Sports Reference: Baseball ID' => 'sprfbsb',
93
+ 'Sports Reference: Basketball ID' => 'sprfbsk',
94
+ 'Sports Reference: College Basketball ID' => 'sprfcbb',
95
+ 'Sports Reference: College Football ID' => 'sprfcfb',
96
+ 'Sports Reference: Hockey ID' => 'sprfhoc',
97
+ 'Sports Reference: Olympic Sports ID' => 'sprfoly',
98
+ 'Sports Reference: Pro Football ID' => 'sprfpfb',
99
+ 'Spotify ID' => 'spotify',
100
+ 'Standard Technical Report Number' => 'strn',
101
+ 'stock number' => 'stock-number',
102
+ 'Svensk Filmdatabas ID' => 'svfilm',
103
+ 'Swets (Netherlands) ID' => 'swets',
104
+ 'Tate Artist ID' => 'tatearid',
105
+ 'TGN' => 'gettytgn',
106
+ 'Theatricalia ID' => 'theatr',
107
+ 'Trove ID' => 'trove',
108
+ 'U.S. National Gazetteer Feature Name ID' => 'natgazfid',
109
+ 'ULAN' => 'gettyulan',
110
+ 'UPC' => 'upc',
111
+ 'URI' => 'uri',
112
+ 'URN' => 'urn',
113
+ 'VIAF' => 'viaf',
114
+ 'video recording number' => 'videorecording-identifier',
115
+ 'Web NDL Authority' => 'wndla',
116
+ 'Wikidata' => 'wikidata'
117
+ }.freeze
118
+
119
+ COCINA_TO_STANDARD_IDENTIFIER_SOURCE_CODES = {
120
+ 'AAT' => 'gettyaat',
121
+ 'actionable ISBN' => 'isbn-a',
122
+ 'AGORHA ID' => 'agorha',
123
+ 'Agricultural Thesaurus and Glossary' => 'atg',
124
+ 'archINFORM location ID' => 'archinl',
125
+ 'archINFORM person ID' => 'archinpe',
126
+ 'archINFORM project ID' => 'archinpr',
127
+ 'Archnet authority' => 'archna',
128
+ 'Archnet site ID' => 'archns',
129
+ 'ARK' => 'ark',
130
+ 'ARK ID' => 'arkid',
131
+ 'Art UK Artists ID' => 'artukart',
132
+ 'Art UK Artworks ID' => 'artukaw',
133
+ 'BALaT People & Institutions ID' => 'balat',
134
+ 'BBC Things ID' => 'bbcth',
135
+ 'Belvedere Artist ID' => 'belvku',
136
+ 'Belvedere Work ID' => 'belvwrk',
137
+ 'Benezit Dictionary of Artists ID' => 'benezit',
138
+ 'BIBBI authority' => 'bibbi',
139
+ 'Biographies of the Entomologists of the World ID' => 'bew',
140
+ 'Biography portal of the Netherlands ID' => 'bpn',
141
+ 'British Standards Institution ID' => 'bsi',
142
+ 'CABI Thesaurus ID' => 'cabt',
143
+ 'Canadiana Authority' => 'cana',
144
+ 'CERL Thesaurus term' => 'cerl',
145
+ 'Cesko-Slovenská filmová databáze ID' => 'csfdcz',
146
+ 'Clara: Database of Women Artists ID' => 'clara',
147
+ 'Collective Biographies of Women Persons ID' => 'cbwpid',
148
+ 'Currículo Lattes ID' => 'lattes',
149
+ 'Dictionnaire des peintres belges ID' => 'dpb',
150
+ 'Digital atlas of the Roman Empire ID' => 'darome',
151
+ 'Early Modern Letters Online ID' => 'emlo',
152
+ 'European Case Law Identifier ID' => 'ecli',
153
+ 'Fide Chess Profile ID' => 'fidecp',
154
+ 'Film Affinity ID' => 'filmaff',
155
+ 'FIS Athlete ID' => 'fisa',
156
+ 'Gemeenschappelijke Thesaurus Audiovisuele Archieven ID' => 'gtaa',
157
+ 'Global Agricultural Concept Scheme ID' => 'gacsch',
158
+ 'Goodreads Author ID' => 'goodra',
159
+ 'Great Britain National Archives ID' => 'nagb',
160
+ 'Great Russian Encyclopedia ID' => 'bigenc',
161
+ 'IAAF Athletes ID' => 'iaafa',
162
+ 'Iconography Authority' => 'iconauth',
163
+ 'Identificativo SBN (Servizio bibliotecario nazionale)' => 'isbnsbn',
164
+ 'ISFDB author directory ID' => 'isfdbau',
165
+ 'ISFDB award directory ID' => 'isfdbaw',
166
+ 'ISFDB magazine directory ID' => 'isfdbma',
167
+ 'ISFDB publisher directory ID' => 'isfdbpu',
168
+ 'J. Paul Getty Museum Artist ID' => 'gettyart',
169
+ 'J. Paul Getty Museum Object ID' => 'gettyobj',
170
+ 'Kunstindeks Danmark Artist ID' => 'kda',
171
+ 'Kunstindeks Danmark Work ID' => 'kdw',
172
+ 'Legal entity identifier' => 'lei',
173
+ 'Marine Gazetteer ID' => 'margaz',
174
+ 'MESH' => 'mesh',
175
+ 'MovieMeter Film ID' => 'moviemetf',
176
+ 'MovieMeter Regisseur ID' => 'moviemetr',
177
+ "Musée d'Orsay Catalogue des oeuvres fiche oeuvre" => 'mocofo',
178
+ "Musée d'Orsay Répertoire des artistes notice artiste" => 'morana',
179
+ 'Music Sales Classical ID' => 'muscl',
180
+ 'National Gallery of Victoria Artist ID' => 'ngva',
181
+ 'National Gallery of Victoria Work ID' => 'ngvw',
182
+ 'OFDb ID' => 'ofdb',
183
+ 'ONIX ID' => 'onix',
184
+ 'Pacific Coast Architecture Database building ID' => 'pcadbu',
185
+ 'Pacific Coast Architecture Database firm ID' => 'pcadpf',
186
+ 'Pacific Coast Architecture Database person ID' => 'pcadpe',
187
+ 'PermID' => 'permid',
188
+ 'Personen uit de Nederlandse Thesaurus van Auteursnamen ID' => 'pnta',
189
+ 'PIC ID' => 'picnypl',
190
+ 'Pleiades ID' => 'pleiades',
191
+ 'Prabook ID' => 'prabook',
192
+ 'Quan Guo Bao Kan Suo Yin ID' => 'cnbksy',
193
+ 'ROR' => 'ror',
194
+ 'Russian National Heritage Registry for Books ID' => 'knpam',
195
+ 'S2A3 Biographical Database of Southern African Science ID' => 's2a3bd',
196
+ 'Science Museum Group People ID' => 'smgp',
197
+ 'Semantic Scholar author ID' => 'ssaut',
198
+ 'SNAC' => 'snac',
199
+ 'STW thesaurus ID' => 'stw',
200
+ 'U.S. National Archives Catalog ID' => 'nacat',
201
+ 'UNESCO thesaurus ID' => 'unescot',
202
+ 'Verzeichnis der Drucke des 16. Jahrhunderts ID' => 'vd16',
203
+ 'Verzeichnis der Drucke des 17. Jahrhunderts ID' => 'vd17',
204
+ 'Verzeichnis der Drucke des 18. Jahrhunderts ID' => 'vd18',
205
+ 'VGMdb artist ID' => 'vgmdb',
206
+ 'X Games Athlete ID' => 'xgamea',
207
+ 'ZooBank author ID' => 'zbaut'
208
+ }.freeze
209
+
210
+ OTHER_COCINA_TYPES = [
211
+ 'arXiv',
212
+ 'audio issue number',
213
+ 'audio take',
214
+ 'award number',
215
+ 'barcode',
216
+ 'BIBCODE',
217
+ 'CODEN',
218
+ 'copyright number',
219
+ 'CrossRef Funder ID',
220
+ 'EAN13',
221
+ 'EISSN',
222
+ 'ETD ID',
223
+ 'fingerprint',
224
+ 'GRID',
225
+ 'IGSN',
226
+ 'LC Overseas Acquisition ID',
227
+ 'LISID',
228
+ 'LISSN',
229
+ 'music distributor number',
230
+ 'music opus number',
231
+ 'music serial number',
232
+ 'music thematic number',
233
+ 'NBN',
234
+ 'OCLC',
235
+ 'other system control number',
236
+ 'PMID',
237
+ 'postal registration',
238
+ 'publisher number',
239
+ 'PURL',
240
+ 'report number',
241
+ 'SIRSI',
242
+ 'STM',
243
+ 'study number',
244
+ 'Symphony authority file identifier',
245
+ 'Symphony catkey',
246
+ 'URL',
247
+ 'W3 ID'
248
+ ].freeze
249
+
250
+ ALL_COCINA_TYPES = OTHER_COCINA_TYPES + COCINA_TO_STANDARD_IDENTIFIER_SOURCE_CODES.keys + COCINA_TO_STANDARD_IDENTIFIER_SCHEMES.keys
251
+
252
+ STANDARD_IDENTIFIER_SOURCE_CODES = 'standard identifier source codes'
253
+ STANDARD_IDENTIFIER_SCHEMES = 'standard identifier schemes'
254
+ COCINA = 'cocina'
255
+
256
+ # @param [String] mods_type
257
+ # @return [String, String, String] cocina type or nil, matched mods type (with correct case) or nil, identifier source or nil
258
+ def self.cocina_type_for_mods_type(mods_type)
259
+ return [nil, nil, nil] if mods_type.blank?
260
+
261
+ # Try to find a standard identifier scheme type (case insensitive)
262
+ COCINA_TO_STANDARD_IDENTIFIER_SCHEMES.each_pair do |cocina_type, check_mods_type|
263
+ if check_mods_type&.downcase == mods_type.downcase
264
+ return [cocina_type, check_mods_type,
265
+ STANDARD_IDENTIFIER_SCHEMES]
266
+ end
267
+ end
268
+
269
+ # Try to find a standard identifier scheme type (case insensitive)
270
+ COCINA_TO_STANDARD_IDENTIFIER_SOURCE_CODES.each_pair do |cocina_type, check_mods_type|
271
+ if check_mods_type&.downcase == mods_type.downcase
272
+ return [cocina_type, check_mods_type,
273
+ STANDARD_IDENTIFIER_SOURCE_CODES]
274
+ end
275
+ end
276
+
277
+ # Try to find a Cocina type (case insensitive)
278
+ ALL_COCINA_TYPES.each do |cocina_type|
279
+ return [cocina_type, nil, COCINA] if cocina_type.casecmp(mods_type).zero?
280
+ end
281
+
282
+ [mods_type, nil, nil]
283
+ end
284
+
285
+ def self.mods_type_for_cocina_type(cocina_type)
286
+ COCINA_TO_STANDARD_IDENTIFIER_SCHEMES[cocina_type] || COCINA_TO_STANDARD_IDENTIFIER_SOURCE_CODES[cocina_type] || cocina_type
287
+ end
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module FromMods
7
+ # Maps languages
8
+ class Language
9
+ # @param [Nokogiri::XML::Element] resource_element mods or relatedItem element
10
+ # @param [Cocina::Models::Mapping::FromMods::DescriptionBuilder] description_builder
11
+ # @param [String] purl
12
+ # @return [Hash] a hash that can be mapped to a cocina model
13
+ def self.build(resource_element:, description_builder:, purl: nil)
14
+ new(resource_element: resource_element, description_builder: description_builder).build
15
+ end
16
+
17
+ def initialize(resource_element:, description_builder:)
18
+ @resource_element = resource_element
19
+ @notifier = description_builder.notifier
20
+ end
21
+
22
+ def build
23
+ languages = resource_element.xpath('mods:language', mods: Description::DESC_METADATA_NS).map do |lang_node|
24
+ Cocina::Models::Mapping::FromMods::LanguageTerm.build(language_element: lang_node, notifier: notifier)
25
+ end
26
+ Primary.adjust(languages, 'language', notifier)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :resource_element, :notifier
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module FromMods
7
+ # Maps lang and script attributes
8
+ class LanguageScript
9
+ # @param [Nokogiri::XML::Element] element that may have lang or script attributes
10
+ # @param [Cocina::Models::Mapping::FromMods::DescriptionBuilder] description_builder
11
+ # @return [Hash] a hash that can be mapped to a cocina model for a valueLanguage
12
+ def self.build(node:)
13
+ return nil unless node['lang'].present? || node['script'].present?
14
+
15
+ {}.tap do |value_language|
16
+ if node['lang'].present?
17
+ value_language[:code] = node['lang']
18
+ value_language[:source] = { code: 'iso639-2b' }
19
+ end
20
+ if node['script'].present?
21
+ value_language[:valueScript] =
22
+ { code: node['script'], source: { code: 'iso15924' } }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module FromMods
7
+ # Maps language term attributes
8
+ class LanguageTerm
9
+ # @param [Nokogiri::XML::Element] language_element language or languageOfCataloging element
10
+ # @param [Cocina::Models::Mapping::ErrorNotifier] notifier
11
+ # @return [Hash] a hash that can be mapped to a cocina model
12
+ def self.build(language_element:, notifier:)
13
+ new(language_element: language_element, notifier: notifier).build
14
+ end
15
+
16
+ def initialize(language_element:, notifier:)
17
+ @language_element = language_element
18
+ @notifier = notifier
19
+ end
20
+
21
+ def build
22
+ attribs = lang_term_attributes
23
+ attribs[:status] = status
24
+ attribs[:script] = script_term_attributes
25
+ attribs.compact
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :language_element, :notifier
31
+
32
+ def lang_term_attributes
33
+ code_language_term = language_element.xpath('./mods:languageTerm[@type="code"]',
34
+ mods: Description::DESC_METADATA_NS).first
35
+ text_language_term = language_element.xpath('./mods:languageTerm[@type="text"]',
36
+ mods: Description::DESC_METADATA_NS).first
37
+ if code_language_term.nil? && text_language_term.nil?
38
+ notifier.warn('languageTerm missing type')
39
+ code_language_term = language_element.xpath('./mods:languageTerm', mods: Description::DESC_METADATA_NS).first
40
+ end
41
+
42
+ {
43
+ code: code_language_term&.text,
44
+ value: text_language_term&.text,
45
+ uri: ValueURI.sniff(language_value_uri_for(code_language_term, text_language_term), notifier),
46
+ appliesTo: language_applies_to,
47
+ displayLabel: language_element['displayLabel']
48
+ }.tap do |attrs|
49
+ source = language_source_for(code_language_term, text_language_term)
50
+ attrs[:source] = source if source.present?
51
+ end
52
+ end
53
+
54
+ def script_term_attributes
55
+ script_term_nodes = language_element.xpath('mods:scriptTerm', mods: Description::DESC_METADATA_NS)
56
+
57
+ return if script_term_nodes.blank?
58
+
59
+ code, value, authority = nil
60
+ script_term_nodes.each do |script_term_node|
61
+ code ||= script_term_node.content if script_term_node['type'] == 'code'
62
+ value ||= script_term_node.content if script_term_node['type'] == 'text'
63
+ authority ||= script_term_node['authority']
64
+ end
65
+ source = { code: authority } if authority
66
+ {
67
+ code: code,
68
+ value: value,
69
+ source: source
70
+ }.compact
71
+ end
72
+
73
+ # this can be present for type text and/or code, but we only want one.
74
+ def language_value_uri_for(code_language_term, text_language_term)
75
+ code_language_term&.attribute('valueURI')&.to_s || text_language_term&.attribute('valueURI')&.to_s
76
+ end
77
+
78
+ def language_applies_to
79
+ value = language_element['objectPart']
80
+ [value: value] if value.present?
81
+ end
82
+
83
+ def language_source_for(code_language_term, text_language_term)
84
+ {
85
+ code: code_language_term&.attribute('authority')&.to_s || text_language_term&.attribute('authority')&.to_s,
86
+ uri: code_language_term&.attribute('authorityURI')&.to_s || text_language_term&.attribute('authorityURI')&.to_s
87
+
88
+ }.compact
89
+ end
90
+
91
+ def status
92
+ status_value = language_element[:usage] || language_element[:status]
93
+ return unless status_value
94
+
95
+ status_value.downcase.tap do |value|
96
+ if status_value != value
97
+ notifier.warn("#{language_element.name} usage attribute not downcased",
98
+ { value: language_element[:usage] })
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end