cocina-models 0.121.0 → 0.123.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Validators
6
+ # Validates location.source.code values against location_source_codes.yml.
7
+ class DescriptionLocationSourceCodeVisitorValidator < BaseDescriptionVisitorValidator
8
+ def validate!
9
+ return if error_paths.empty?
10
+
11
+ raise ValidationError, "Unrecognized location source codes in description: #{error_paths.join(', ')}"
12
+ end
13
+
14
+ def visit_hash(hash:, path:)
15
+ return unless location_path?(path)
16
+
17
+ source_code = hash.dig(:source, :code)
18
+ return unless source_code
19
+ return if valid_codes.include?(source_code.downcase)
20
+
21
+ error_paths << "#{path_to_s(path)}.source.code (#{source_code})"
22
+ end
23
+
24
+ private
25
+
26
+ def error_paths
27
+ @error_paths ||= []
28
+ end
29
+
30
+ def location_path?(path)
31
+ # Match entries in a location array (e.g., [:location, 0] or [:relatedResource, 0, :location, 0]).
32
+ path.length >= 2 && path[-1].is_a?(Integer) && path[-2].to_s == 'location'
33
+ end
34
+
35
+ # rubocop:disable Style/ClassVars
36
+ def valid_codes
37
+ @@valid_codes ||= YAML.load_file(::File.expand_path('../../../../location_source_codes.yml', __dir__)).to_set(&:downcase)
38
+ end
39
+ # rubocop:enable Style/ClassVars
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Validators
6
+ # Validates contributor.role.source.code values against role_source_codes.yml.
7
+ class DescriptionRoleSourceCodeVisitorValidator < BaseDescriptionVisitorValidator
8
+ def validate!
9
+ return if error_paths.empty?
10
+
11
+ raise ValidationError, "Unrecognized role source codes in description: #{error_paths.join(', ')}"
12
+ end
13
+
14
+ def visit_hash(hash:, path:)
15
+ return unless role_path?(path)
16
+
17
+ source_code = hash.dig(:source, :code)
18
+ return unless source_code
19
+ return if valid_codes.include?(source_code.downcase)
20
+
21
+ error_paths << "#{path_to_s(path)}.source.code (#{source_code})"
22
+ end
23
+
24
+ private
25
+
26
+ def error_paths
27
+ @error_paths ||= []
28
+ end
29
+
30
+ def role_path?(path)
31
+ path.length >= 2 && # ensure we have at least two elements (at minimum role and its index)
32
+ path[-1].is_a?(Integer) &&
33
+ path[-2].to_s == 'role' && # we have a nested role in the path
34
+ path.any? { |part| part.to_s == 'contributor' } # there is a contributor in the path (any? allows for nested roles)
35
+ end
36
+
37
+ # Source codes allowed for contributor.role.source.code
38
+ def valid_codes
39
+ %w[aat lcmpt marcrelator rbmsrel]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Validators
6
+ # Validates encoding.code for subject entries with type "time" against
7
+ # temporal_subject_encoding_codes.yml (union of LOC date-time and temporal source lists).
8
+ class DescriptionSubjectTemporalEncodingVisitorValidator < BaseDescriptionVisitorValidator
9
+ def validate!
10
+ return if error_paths.empty?
11
+
12
+ raise ValidationError, "Unrecognized subject temporal encoding codes in description: #{error_paths.join(', ')}"
13
+ end
14
+
15
+ def visit_hash(hash:, path:)
16
+ return unless in_subject_path?(path)
17
+ return unless hash[:type].to_s == 'time'
18
+
19
+ encoding_code = hash.dig(:encoding, :code)
20
+ return unless encoding_code
21
+
22
+ error_paths << "#{path_to_s(path)}.encoding.code (#{encoding_code})" unless valid_codes.include?(encoding_code.downcase)
23
+ end
24
+
25
+ private
26
+
27
+ def error_paths
28
+ @error_paths ||= []
29
+ end
30
+
31
+ def in_subject_path?(path)
32
+ path.any? { |part| part.to_s == 'subject' }
33
+ end
34
+
35
+ # rubocop:disable Style/ClassVars
36
+ def valid_codes
37
+ @@valid_codes ||= YAML.load_file(::File.expand_path('../../../../temporal_subject_encoding_codes.yml', __dir__)).to_set(&:downcase)
38
+ end
39
+ # rubocop:enable Style/ClassVars
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cocina_display'
4
+
5
+ module Cocina
6
+ module Models
7
+ module Validators
8
+ # Validates that contributor role codes from the marcrelator vocabulary are valid MARC relator codes.
9
+ # Includes discontinued codes. See https://www.loc.gov/marc/relators/relacode.html
10
+ class MarcRelatorRoleValidator
11
+ MARCRELATOR_SOURCE_CODES = %w[marcrelator].freeze
12
+ MARCRELATOR_SOURCE_URI = 'http://id.loc.gov/vocabulary/relators/'
13
+
14
+ def self.validate(clazz, attributes)
15
+ new(clazz, attributes).validate
16
+ end
17
+
18
+ def initialize(clazz, attributes)
19
+ @clazz = clazz
20
+ @attributes = attributes
21
+ @errors = []
22
+ end
23
+
24
+ def validate
25
+ return unless meets_preconditions?
26
+
27
+ resources.each { |resource| validate_resource(resource) }
28
+ raise ValidationError, "Invalid MARC relator codes in description: #{@errors.join(', ')}" if @errors.any?
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :clazz, :attributes
34
+
35
+ def meets_preconditions?
36
+ [Cocina::Models::Description, Cocina::Models::RequestDescription].include?(clazz)
37
+ end
38
+
39
+ def resources
40
+ [description_attributes] + Array(description_attributes[:relatedResource])
41
+ end
42
+
43
+ def description_attributes
44
+ @description_attributes ||= if [Cocina::Models::Description,
45
+ Cocina::Models::RequestDescription].include?(clazz)
46
+ attributes
47
+ else
48
+ attributes[:description] || {}
49
+ end
50
+ end
51
+
52
+ def validate_resource(resource)
53
+ Array(resource[:contributor]).each_with_index do |contributor, contributor_index|
54
+ Array(contributor[:role]).each_with_index do |role, role_index|
55
+ validate_role(role, "contributor#{contributor_index + 1}.role#{role_index + 1}")
56
+ end
57
+ end
58
+ end
59
+
60
+ def validate_role(role, path)
61
+ return unless marc_relator_source?(role)
62
+
63
+ code = role[:code]
64
+ return if code.nil?
65
+
66
+ @errors << "#{path} (#{code})" unless valid_codes.include?(code)
67
+ end
68
+
69
+ def marc_relator_source?(role)
70
+ source = role[:source] || {}
71
+ MARCRELATOR_SOURCE_CODES.include?(source[:code]) ||
72
+ source[:uri]&.start_with?(MARCRELATOR_SOURCE_URI)
73
+ end
74
+
75
+ # rubocop:disable Style/ClassVars
76
+ def valid_codes
77
+ @@valid_codes ||= YAML.load_file(CocinaDisplay.root.join('config/marc_relators.yml')).keys
78
+ end
79
+ # rubocop:enable Style/ClassVars
80
+ end
81
+ end
82
+ end
83
+ end
@@ -11,7 +11,8 @@ module Cocina
11
11
  CompositeStructuralValidator,
12
12
  PurlValidator,
13
13
  CatalogLinksValidator,
14
- AssociatedNameValidator
14
+ AssociatedNameValidator,
15
+ MarcRelatorRoleValidator
15
16
  ].freeze
16
17
 
17
18
  def self.validate(clazz, attributes, validators: VALIDATORS)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Cocina
4
4
  module Models
5
- VERSION = '0.121.0'
5
+ VERSION = '0.123.0'
6
6
  end
7
7
  end
@@ -0,0 +1,483 @@
1
+ ---
2
+ # Location source codes from https://www.loc.gov/standards/sourcelist/subject.html
3
+ # Plus additional codes specified in issue #1094
4
+ - aass
5
+ - aat
6
+ - aatnor
7
+ - abne
8
+ - aedoml
9
+ - afo
10
+ - afset
11
+ - agrifors
12
+ - agrovoc
13
+ - agrovocf
14
+ - agrovocs
15
+ - ahecc
16
+ - aiatsisl
17
+ - aiatsisp
18
+ - aiatsiss
19
+ - aktp
20
+ - albt
21
+ - allars
22
+ - anzsic
23
+ - anzsoc
24
+ - anzsrc
25
+ - apaist
26
+ - armarc
27
+ - ascdc
28
+ - asced
29
+ - ascl
30
+ - asft
31
+ - ashlnl
32
+ - asrcrfcd
33
+ - asrcseo
34
+ - asrctoa
35
+ - asth
36
+ - ated
37
+ - atg
38
+ - atla
39
+ - aucsh
40
+ - ausext
41
+ - bare
42
+ - barn
43
+ - bdrc
44
+ - bella
45
+ - bet
46
+ - bhammf
47
+ - bhashe
48
+ - bhb
49
+ - bib1814
50
+ - bibalex
51
+ - bibbi
52
+ - biccbmc
53
+ - bicssc
54
+ - bidex
55
+ - bisacmt
56
+ - bisacrt
57
+ - bisacsh
58
+ - bjornson
59
+ - blcpss
60
+ - blmlsh
61
+ - blnpn
62
+ - bokbas
63
+ - bt
64
+ - btr
65
+ - buscem
66
+ - cccv
67
+ - cabt
68
+ - cagraq
69
+ - cash
70
+ - cbktrf
71
+ - cck
72
+ - cckthema
73
+ - ccga
74
+ - ccsa
75
+ - cct
76
+ - ccte
77
+ - cctf
78
+ - cdcng
79
+ - ceeus
80
+ - cgpa
81
+ - cgndb
82
+ - chirosh
83
+ - cht
84
+ - ciesiniv
85
+ - cilla
86
+ - collett
87
+ - conorsi
88
+ - conorsr
89
+ - csahssa
90
+ - csalsct
91
+ - csapa
92
+ - csh
93
+ - csht
94
+ - cstud
95
+ - cyac
96
+ - czenas
97
+ - czmesh
98
+ - dbcsh
99
+ - dbn
100
+ - dcs
101
+ - ddcri
102
+ - ddcrit
103
+ - ddcut
104
+ - dhb-jdg
105
+ - dicgenam
106
+ - dicgenes
107
+ - dicgentop
108
+ - dissao
109
+ - dit
110
+ - dltlt
111
+ - dltt
112
+ - drama
113
+ - dtict
114
+ - dugfr
115
+ - ebfem
116
+ - eclas
117
+ - eet
118
+ - eflch
119
+ - eks
120
+ - elsst
121
+ - embehu
122
+ - embiaecid
123
+ - embne
124
+ - embucm
125
+ - embus
126
+ - embuz
127
+ - emnmus
128
+ - ept
129
+ - erfemn
130
+ - ericd
131
+ - esar
132
+ - est
133
+ - etiras
134
+ - etuesh
135
+ - etuturkob
136
+ - eum
137
+ - eurovoc
138
+ - eurovocen
139
+ - eurovoces
140
+ - eurovocfr
141
+ - eurovocsl
142
+ - fast
143
+ - fcb
144
+ - fes
145
+ - finmesh
146
+ - fire
147
+ - flgeo
148
+ - fmesh
149
+ - fnhl
150
+ - francis
151
+ - frst
152
+ - fssh
153
+ - galestne
154
+ - gbd
155
+ - gccst
156
+ - gcipmedia
157
+ - gcipplatform
158
+ - gem
159
+ - gemet
160
+ - geni
161
+ - geonames
162
+ - geonet
163
+ - georeft
164
+ - gnd
165
+ - gnis
166
+ - gpn
167
+ - gsso
168
+ - gst
169
+ - gtt
170
+ - habibe
171
+ - habich
172
+ - habifr
173
+ - habiit
174
+ - hamsun
175
+ - hapi
176
+ - helecon
177
+ - henn
178
+ - hkcan
179
+ - hlasstg
180
+ - hoidokki
181
+ - homoit
182
+ - hrvmesh
183
+ - hrvmr
184
+ - huc
185
+ - humord
186
+ - ibsen
187
+ - ica
188
+ - iconauth
189
+ - icpsr
190
+ - idas
191
+ - idref
192
+ - idsbb
193
+ - idszbz
194
+ - idszbzes
195
+ - idszbzna
196
+ - idszbzzg
197
+ - idszbzzh
198
+ - idszbzzk
199
+ - iescs
200
+ - iest
201
+ - ilot
202
+ - ilpt
203
+ - inist
204
+ - inspect
205
+ - ipat
206
+ - ipsp
207
+ - iptcnc
208
+ - iso3166
209
+ - iso3166-2
210
+ - isis
211
+ - itglit
212
+ - itis
213
+ - itrt
214
+ - jhpb
215
+ - jhpk
216
+ - jlabsh
217
+ - juho
218
+ - jupo
219
+ - jurivoc
220
+ - kaa
221
+ - kaba
222
+ - kao
223
+ - kassu
224
+ - kauno
225
+ - kaunokki
226
+ - kdm
227
+ - khib
228
+ - kito
229
+ - kitu
230
+ - kkts
231
+ - koko
232
+ - kssbar
233
+ - kta
234
+ - kto
235
+ - ktpt
236
+ - ktta
237
+ - kubikat
238
+ - kula
239
+ - kulo
240
+ - kupu
241
+ - labloc
242
+ - lacnaf
243
+ - lapponica
244
+ - larpcal
245
+ - lcac
246
+ - lcdgt
247
+ - lcsh
248
+ - lcshac
249
+ - lcstt
250
+ - lctgm
251
+ - leaubsh
252
+ - lemac
253
+ - lemb
254
+ - liito
255
+ - liv
256
+ - lnmmbr
257
+ - local
258
+ - lst
259
+ - ltcsh
260
+ - lua
261
+ - maaq
262
+ - maknaz
263
+ - maotao
264
+ - mar
265
+ - marcgac
266
+ - marccountry
267
+ - masa
268
+ - mech
269
+ - mero
270
+ - mesh
271
+ - meshscr
272
+ - minecost
273
+ - mipfesd
274
+ - mmm
275
+ - mpirdes
276
+ - msc
277
+ - msh
278
+ - mtirdes
279
+ - mts
280
+ - musa
281
+ - muso
282
+ - muzeukc
283
+ - muzeukn
284
+ - muzvukci
285
+ - naf
286
+ - nal
287
+ - nalnaf
288
+ - nasat
289
+ - nbdbt
290
+ - nbiemnfag
291
+ - ncjt
292
+ - ndlsh
293
+ - neo
294
+ - netc
295
+ - nicem
296
+ - nimacsc
297
+ - niodt
298
+ - nlgaf
299
+ - nlgkk
300
+ - nlgsh
301
+ - nli
302
+ - nlksh
303
+ - nlmnaf
304
+ - nmaict
305
+ - no-ubo-mr
306
+ - noraf
307
+ - noram
308
+ - norbok
309
+ - normesh
310
+ - norvok
311
+ - noubojur
312
+ - noubomn
313
+ - nsbncf
314
+ - nskps
315
+ - nta
316
+ - ntc
317
+ - ntcpsc
318
+ - ntcsd
319
+ - ntids
320
+ - ntissc
321
+ - nzggn
322
+ - nznb
323
+ - odlt
324
+ - ogst
325
+ - opat
326
+ - opms
327
+ - ordnok
328
+ - orgnr
329
+ - pana
330
+ - pascal
331
+ - peakbag
332
+ - pepp
333
+ - peri
334
+ - periodo
335
+ - pha
336
+ - pkk
337
+ - pldi
338
+ - pleiades
339
+ - pmbok
340
+ - pmcsg
341
+ - pmont
342
+ - pmt
343
+ - poha
344
+ - poliscit
345
+ - popinte
346
+ - pplt
347
+ - ppluk
348
+ - precis
349
+ - prnpdi
350
+ - proqsc
351
+ - prvt
352
+ - psh
353
+ - psychit
354
+ - puho
355
+ - qlit
356
+ - qlsp
357
+ - qnaf
358
+ - qnlsh
359
+ - qrma
360
+ - qrmak
361
+ - qtglit
362
+ - quiding
363
+ - ram
364
+ - rasuqam
365
+ - rdm
366
+ - renib
367
+ - reo
368
+ - rero
369
+ - rerovoc
370
+ - rma
371
+ - root
372
+ - rpe
373
+ - rswk
374
+ - rswkaf
375
+ - rugeo
376
+ - rurkp
377
+ - rvm
378
+ - rvmfast
379
+ - rvmgd
380
+ - samisk
381
+ - sanb
382
+ - sao
383
+ - sbaa
384
+ - sbiao
385
+ - sbt
386
+ - scbi
387
+ - scgdst
388
+ - scisshl
389
+ - scot
390
+ - sears
391
+ - sesca
392
+ - sfit
393
+ - sgc
394
+ - sgce
395
+ - shbe
396
+ - she
397
+ - shsples
398
+ - sigle
399
+ - sipri
400
+ - sk
401
+ - skbb
402
+ - skish
403
+ - skon
404
+ - slem
405
+ - smda
406
+ - snt
407
+ - socio
408
+ - solstad
409
+ - sosa
410
+ - soto
411
+ - spines
412
+ - ssg
413
+ - stcv
414
+ - sthus
415
+ - stw
416
+ - swd
417
+ - swdl
418
+ - swemesh
419
+ - taika
420
+ - tasmas
421
+ - taxhs
422
+ - tbjvp
423
+ - tef
424
+ - tekord
425
+ - tero
426
+ - tesa
427
+ - tesbhaecid
428
+ - test
429
+ - tgn
430
+ - tha
431
+ - thema
432
+ - thesoz
433
+ - thia
434
+ - thla
435
+ - tho
436
+ - thub
437
+ - tips
438
+ - tisa
439
+ - tlka
440
+ - tlsh
441
+ - tmdbm
442
+ - tmdbp
443
+ - tmdbtv
444
+ - trfarn
445
+ - trfbmb
446
+ - trfdh
447
+ - trfgr
448
+ - trfoba
449
+ - trfzb
450
+ - trt
451
+ - trtsa
452
+ - tsaij
453
+ - tshd
454
+ - tsht
455
+ - tsr
456
+ - ttka
457
+ - ttll
458
+ - tucua
459
+ - udc
460
+ - ukslc
461
+ - ula
462
+ - ulan
463
+ - umitrist
464
+ - unbisn
465
+ - unbist
466
+ - unescot
467
+ - unicefirc
468
+ - usaidt
469
+ - usgst
470
+ - valo
471
+ - vcaadu
472
+ - vffyl
473
+ - vmj
474
+ - waqaf
475
+ - watrest
476
+ - wgst
477
+ - wikidata
478
+ - worldcat
479
+ - wot
480
+ - wpicsh
481
+ - ysa
482
+ - yso
483
+ - zst