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
@@ -54,29 +54,22 @@ module Cocina
|
|
54
54
|
|
55
55
|
<<~MARKDOWN
|
56
56
|
#{'#' * (field.count('.') + 1)} #{header_markdown}
|
57
|
-
_Path: #{field}
|
57
|
+
_Path: #{field}.type_
|
58
58
|
#{types_markdown}
|
59
59
|
MARKDOWN
|
60
|
-
end.join
|
60
|
+
end.join
|
61
61
|
|
62
62
|
remove_file 'docs/description_types.md'
|
63
|
-
create_file 'docs/description_types.md',
|
63
|
+
create_file 'docs/description_types.md', markdown
|
64
64
|
end
|
65
65
|
|
66
66
|
private
|
67
67
|
|
68
|
-
def h1_markdown
|
69
|
-
<<~MARKDOWN
|
70
|
-
# Description types
|
71
|
-
|
72
|
-
MARKDOWN
|
73
|
-
end
|
74
|
-
|
75
68
|
def field_markdown_from(field)
|
76
69
|
header = field.split('.')
|
77
70
|
.grep_v(/groupedValue|structuredValue/)
|
78
71
|
.join(' ')
|
79
|
-
.
|
72
|
+
.upcase_first
|
80
73
|
|
81
74
|
header_suffix = if field.ends_with?('structuredValue')
|
82
75
|
'part types for structured value'
|
@@ -91,7 +84,9 @@ module Cocina
|
|
91
84
|
def types_markdown_from(types)
|
92
85
|
types.map do |type|
|
93
86
|
" * #{type['value']}".tap do |type_value|
|
94
|
-
type_value << "
|
87
|
+
type_value << "\n * #{type['description']}" if type['description']
|
88
|
+
type_value << "\n * Deprecated." if type['status'] == 'deprecated'
|
89
|
+
type_value << " Preferred usage: #{type['use']}" if type['use']
|
95
90
|
end
|
96
91
|
end.join("\n")
|
97
92
|
end
|
@@ -122,10 +117,7 @@ module Cocina
|
|
122
117
|
|
123
118
|
NO_CLEAN = [
|
124
119
|
'checkable.rb',
|
125
|
-
'dro_rights_description_builder.rb',
|
126
120
|
'license.rb',
|
127
|
-
'rights_description_builder.rb',
|
128
|
-
'title_builder.rb',
|
129
121
|
'validatable.rb',
|
130
122
|
'version.rb',
|
131
123
|
'vocabulary.rb'
|
@@ -101,9 +101,7 @@ module Cocina
|
|
101
101
|
return [] if doc.one_of.nil?
|
102
102
|
|
103
103
|
# All properties must be objects.
|
104
|
-
unless doc.one_of.all? { |schema| schema.type == 'object' }
|
105
|
-
raise 'All properties for oneOf must be objects'
|
106
|
-
end
|
104
|
+
raise 'All properties for oneOf must be objects' unless doc.one_of.all? { |schema| schema.type == 'object' }
|
107
105
|
|
108
106
|
doc.one_of.flat_map do |one_of_doc|
|
109
107
|
one_of_doc.properties.map do |key, properties_doc|
|
@@ -23,14 +23,6 @@ module Cocina
|
|
23
23
|
key || schema_doc.name
|
24
24
|
end
|
25
25
|
|
26
|
-
# Allows non-required values to not be provided. This allows smaller
|
27
|
-
# requests as not every field needs to be present.
|
28
|
-
def omittable
|
29
|
-
return '' if required && !relaxed
|
30
|
-
|
31
|
-
'.meta(omittable: true)'
|
32
|
-
end
|
33
|
-
|
34
26
|
# Allows nillable values to be set to nil. This is useful when doing
|
35
27
|
# an update and you want to clear out a value.
|
36
28
|
def optional
|
@@ -8,7 +8,7 @@ module Cocina
|
|
8
8
|
if required && !relaxed
|
9
9
|
"attribute(:#{name.camelize(:lower)}, #{schema_doc.name}.default { #{schema_doc.name}.new })"
|
10
10
|
else
|
11
|
-
"attribute :#{name.camelize(:lower)}, #{schema_doc.name}.optional
|
11
|
+
"attribute? :#{name.camelize(:lower)}, #{schema_doc.name}.optional"
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -4,15 +4,25 @@ module Cocina
|
|
4
4
|
module Generator
|
5
5
|
# Class for generating from an openapi value
|
6
6
|
class SchemaValue < SchemaBase
|
7
|
-
# rubocop:disable Layout/LineLength
|
8
7
|
def generate
|
9
8
|
# optional has to come before default or the default value that gets set will be nil.
|
10
|
-
|
9
|
+
if required && !relaxed
|
10
|
+
"#{preamble}attribute :#{name.camelize(:lower)}, #{type}"
|
11
|
+
else
|
12
|
+
"#{preamble}attribute? :#{name.camelize(:lower)}, #{type}"
|
13
|
+
end
|
11
14
|
end
|
12
|
-
# rubocop:enable Layout/LineLength
|
13
15
|
|
14
16
|
private
|
15
17
|
|
18
|
+
def type
|
19
|
+
"Types::#{dry_datatype(schema_doc)}#{optional}#{default}#{enum}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def preamble
|
23
|
+
"#{description}#{example}#{relaxed_comment}"
|
24
|
+
end
|
25
|
+
|
16
26
|
def enum
|
17
27
|
return '' if !schema_doc.enum || relaxed
|
18
28
|
|
@@ -32,7 +42,7 @@ module Cocina
|
|
32
42
|
# If type is boolean and default is false, erroneously getting a nil.
|
33
43
|
# Assuming that if required, then default is false.
|
34
44
|
default = schema_doc.default
|
35
|
-
default = false if default.nil? && schema_doc.type == 'boolean'
|
45
|
+
default = false if default.nil? && schema_doc.type == 'boolean'
|
36
46
|
|
37
47
|
return '' if default.nil?
|
38
48
|
|
data/lib/cocina/models/access.rb
CHANGED
@@ -5,15 +5,15 @@ module Cocina
|
|
5
5
|
class Access < Struct
|
6
6
|
# Access level.
|
7
7
|
# Validation of this property is relaxed. See the openapi for full validation.
|
8
|
-
attribute :view, Types::Strict::String.optional.default('dark')
|
8
|
+
attribute? :view, Types::Strict::String.optional.default('dark')
|
9
9
|
# Download access level.
|
10
10
|
# Validation of this property is relaxed. See the openapi for full validation.
|
11
|
-
attribute :download, Types::Strict::String.optional.default('none')
|
11
|
+
attribute? :download, Types::Strict::String.optional.default('none')
|
12
12
|
# Not used for this access type, must be null.
|
13
13
|
# Validation of this property is relaxed. See the openapi for full validation.
|
14
|
-
attribute :location, Types::Strict::String.optional
|
14
|
+
attribute? :location, Types::Strict::String.optional
|
15
15
|
# Validation of this property is relaxed. See the openapi for full validation.
|
16
|
-
attribute :controlledDigitalLending, Types::Strict::Bool.optional.
|
16
|
+
attribute? :controlledDigitalLending, Types::Strict::Bool.optional.default(false)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -18,7 +18,7 @@ module Cocina
|
|
18
18
|
attribute :label, Types::Strict::String
|
19
19
|
attribute :version, Types::Strict::Integer
|
20
20
|
attribute(:administrative, AdminPolicyAdministrative.default { AdminPolicyAdministrative.new })
|
21
|
-
attribute :description, Description.optional
|
21
|
+
attribute? :description, Description.optional
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -3,23 +3,23 @@
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
5
|
class AdminPolicyAccessTemplate < Struct
|
6
|
-
attribute :view, Types::Strict::String.enum('world', 'stanford', 'location-based', 'citation-only', 'dark')
|
6
|
+
attribute? :view, Types::Strict::String.enum('world', 'stanford', 'location-based', 'citation-only', 'dark')
|
7
7
|
# Available for controlled digital lending.
|
8
|
-
attribute :controlledDigitalLending, Types::Strict::Bool.
|
8
|
+
attribute? :controlledDigitalLending, Types::Strict::Bool.default(false)
|
9
9
|
# The human readable copyright statement that applies
|
10
10
|
# example: Copyright World Trade Organization
|
11
|
-
attribute :copyright, Types::Strict::String.optional
|
11
|
+
attribute? :copyright, Types::Strict::String.optional
|
12
12
|
# Download access level. This is used in the transition from Fedora as a way to set a default download level at registration that is copied down to all the files.
|
13
13
|
|
14
|
-
attribute :download, Types::Strict::String.enum('world', 'stanford', 'location-based', 'none')
|
14
|
+
attribute? :download, Types::Strict::String.enum('world', 'stanford', 'location-based', 'none')
|
15
15
|
# If access or download is "location-based", this indicates which location should have access. This is used in the transition from Fedora as a way to set a default location at registration that is copied down to all the files.
|
16
16
|
|
17
|
-
attribute :location, Types::Strict::String.optional.enum('spec', 'music', 'ars', 'art', 'hoover', 'm&m')
|
17
|
+
attribute? :location, Types::Strict::String.optional.enum('spec', 'music', 'ars', 'art', 'hoover', 'm&m')
|
18
18
|
# The human readable use and reproduction statement that applies
|
19
19
|
# example: Property rights reside with the repository. Literary rights reside with the creators of the documents or their heirs. To obtain permission to publish or reproduce, please contact the Public Services Librarian of the Dept. of Special Collections (http://library.stanford.edu/spc).
|
20
|
-
attribute :useAndReproductionStatement, Types::Strict::String.optional
|
20
|
+
attribute? :useAndReproductionStatement, Types::Strict::String.optional
|
21
21
|
# The license governing reuse of the Collection. Should be an IRI for known licenses (i.e. CC, RightsStatement.org URI, etc.).
|
22
|
-
attribute :license, Types::Strict::String.optional
|
22
|
+
attribute? :license, Types::Strict::String.optional
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -7,7 +7,7 @@ module Cocina
|
|
7
7
|
attribute :registrationWorkflow, Types::Strict::Array.of(Types::Strict::String).default([].freeze)
|
8
8
|
# An additional workflow to start for objects managed by this admin policy once the end-accession workflow step is complete
|
9
9
|
# example: wasCrawlPreassemblyWF
|
10
|
-
attribute :disseminationWorkflow, Types::Strict::String
|
10
|
+
attribute? :disseminationWorkflow, Types::Strict::String
|
11
11
|
attribute :collectionsForRegistration, Types::Strict::Array.of(Types::Strict::String).default([].freeze)
|
12
12
|
# example: druid:bc123df4567
|
13
13
|
attribute :hasAdminPolicy, Types::Strict::String
|
@@ -18,11 +18,11 @@ module Cocina
|
|
18
18
|
attribute :label, Types::Strict::String
|
19
19
|
attribute :version, Types::Strict::Integer
|
20
20
|
attribute(:administrative, AdminPolicyAdministrative.default { AdminPolicyAdministrative.new })
|
21
|
-
attribute :description, Description.optional
|
21
|
+
attribute? :description, Description.optional
|
22
22
|
# When the object was created.
|
23
|
-
attribute :created, Types::Params::DateTime
|
23
|
+
attribute? :created, Types::Params::DateTime
|
24
24
|
# When the object was modified.
|
25
|
-
attribute :modified, Types::Params::DateTime
|
25
|
+
attribute? :modified, Types::Params::DateTime
|
26
26
|
# Key for optimistic locking. The contents of the key is not specified.
|
27
27
|
attribute :lock, Types::Strict::String
|
28
28
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cocina
|
4
|
+
module Models
|
5
|
+
module Builders
|
6
|
+
# Rights description builder for items
|
7
|
+
class DroRightsDescriptionBuilder < RightsDescriptionBuilder
|
8
|
+
# @param [Cocina::Models::DRO] cocina_item
|
9
|
+
|
10
|
+
# This overrides the superclass
|
11
|
+
# @return [Cocina::Models::DROAccess]
|
12
|
+
def object_access
|
13
|
+
@object_access ||= cocina.access
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def object_level_access
|
19
|
+
super + access_level_from_files.uniq.map { |str| "#{str} (file)" }
|
20
|
+
end
|
21
|
+
|
22
|
+
def access_level_from_files
|
23
|
+
# dark access doesn't permit any file access
|
24
|
+
return [] if object_access.view == 'dark'
|
25
|
+
|
26
|
+
file_access_nodes.reject { |fa| same_as_object_access?(fa) }.flat_map do |fa|
|
27
|
+
file_access_from_file(fa)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# rubocop:disable Metrics/MethodLength
|
32
|
+
def file_access_from_file(file_access)
|
33
|
+
basic_access = if file_access[:view] == 'location-based'
|
34
|
+
"location: #{file_access[:location]}"
|
35
|
+
else
|
36
|
+
file_access[:view]
|
37
|
+
end
|
38
|
+
|
39
|
+
return [basic_access] if file_access[:view] == file_access[:download]
|
40
|
+
|
41
|
+
basic_access += ' (no-download)' if file_access[:view] != 'dark'
|
42
|
+
|
43
|
+
case file_access[:download]
|
44
|
+
when 'stanford'
|
45
|
+
[basic_access, 'stanford']
|
46
|
+
when 'location-based'
|
47
|
+
# Here we're using location to mean download location.
|
48
|
+
[basic_access, "location: #{file_access[:location]}"]
|
49
|
+
else
|
50
|
+
[basic_access]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
# rubocop:enable Metrics/MethodLength
|
54
|
+
|
55
|
+
def same_as_object_access?(file_access)
|
56
|
+
(file_access[:view] == object_access.view && file_access[:download] == object_access.download) ||
|
57
|
+
(object_access.view == 'citation-only' && file_access[:view] == 'dark')
|
58
|
+
end
|
59
|
+
|
60
|
+
def file_access_nodes
|
61
|
+
Array(cocina.structural.contains)
|
62
|
+
.flat_map { |fs| Array(fs.structural.contains) }
|
63
|
+
.map { |file| file.access.to_h }
|
64
|
+
.uniq
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cocina
|
4
|
+
module Models
|
5
|
+
module Builders
|
6
|
+
# Helpers for MODS nameTitleGroups.
|
7
|
+
# MODS titles need to know if they match a contributor and thus need a nameTitleGroup
|
8
|
+
# MODS contributors need to know if they match a title and thus need a nameTitleGroup
|
9
|
+
# If there is a match, the nameTitleGroup number has to be consistent for the matching title(s)
|
10
|
+
# and the contributor(s)
|
11
|
+
class NameTitleGroupBuilder
|
12
|
+
# When to assign nameTitleGroup to MODS from cocina:
|
13
|
+
# for cocina title of type "uniform",
|
14
|
+
# look for cocina title properties :value or :structuredValue (recurse down through :parallelValue
|
15
|
+
# as needed), and look for associated :note with :type of "associated name" at the level of the
|
16
|
+
# non-empty title [value|structuredValue]
|
17
|
+
# The note of type "associated name" will have [value|structuredValue] which will match
|
18
|
+
# [value|structuredValue] for a contributor (possibly after recursing through :parallelValue).
|
19
|
+
# Thus, a title [value|structuredValue] and a contributor [value|structuredValue] are associated in
|
20
|
+
# cocina.
|
21
|
+
#
|
22
|
+
# If those criteria not met in Cocina, do not assign nameTitleGroup in MODS
|
23
|
+
#
|
24
|
+
# @params [Cocina::Models::Title] title
|
25
|
+
# @return [Hash<Hash, Hash>] key: hash of value or structuredValue property for title
|
26
|
+
# value: hash of value or structuredValue property for contributor
|
27
|
+
# e.g. {{:value=>"Portrait of the artist as a young man"}=>{:value=>"James Joyce"}}
|
28
|
+
# e.g. {{:value=>"Portrait of the artist as a young man"}=>{:structuredValue=>
|
29
|
+
# [{:value=>"Joyce, James", :type=>"name"},{:value=>"1882-1941", :type=>"life dates"}]}}
|
30
|
+
# e.g. {{:structuredValue=>[{:value=>"Demanding Food", :type=>"main"},
|
31
|
+
# {:value=>"A Cat's Life", :type=>"subtitle"}]}=>{:value=>"James Joyce"}}
|
32
|
+
# this complexity is needed for multilingual titles mapping to multilingual names.
|
33
|
+
def self.build_title_values_to_contributor_name_values(title)
|
34
|
+
result = {}
|
35
|
+
return result if title.blank?
|
36
|
+
|
37
|
+
# pair title value with contributor name value
|
38
|
+
title_value_note_slices(title).each do |value_note_slice|
|
39
|
+
title_val_slice = slice_of_value_or_structured_value(value_note_slice)
|
40
|
+
next if title_val_slice.blank?
|
41
|
+
|
42
|
+
associated_name_note = value_note_slice[:note]&.detect { |note| note[:type] == 'associated name' }
|
43
|
+
next if associated_name_note.blank?
|
44
|
+
|
45
|
+
# relevant note will be Array of either
|
46
|
+
# {
|
47
|
+
# value: 'string contributor name',
|
48
|
+
# type: 'associated name'
|
49
|
+
# }
|
50
|
+
# OR
|
51
|
+
# {
|
52
|
+
# structuredValue: [ structuredValue contributor name ],
|
53
|
+
# type: 'associated name'
|
54
|
+
# }
|
55
|
+
# and we want the hash without the :type attribute
|
56
|
+
result[title_val_slice] = slice_of_value_or_structured_value(associated_name_note)
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.contributor_for_contributor_name_value_slice(contributor_name_value_slice:, contributors:)
|
62
|
+
Array(contributors).find do |contributor|
|
63
|
+
contrib_name_value_slices = contributor_name_value_slices(contributor)
|
64
|
+
contrib_name_value_slices.include?(contributor_name_value_slice)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @params [Cocina::Models::Contributor] contributor
|
69
|
+
# @return [Hash] where we are only interested in
|
70
|
+
# hashes containing (either :value or :structureValue)
|
71
|
+
def self.contributor_name_value_slices(contributor)
|
72
|
+
return if contributor&.name.blank?
|
73
|
+
|
74
|
+
slices = []
|
75
|
+
Array(contributor.name).each do |contrib_name|
|
76
|
+
slices << value_slices(contrib_name)
|
77
|
+
end
|
78
|
+
slices.flatten
|
79
|
+
end
|
80
|
+
|
81
|
+
# @params [Cocina::Models::DescriptiveValue] desc_value
|
82
|
+
# @return [Array<Cocina::Models::DescriptiveValue>] where we are only interested in
|
83
|
+
# hashes containing (either :value or :structuredValue)
|
84
|
+
def self.value_slices(desc_value)
|
85
|
+
slices = []
|
86
|
+
desc_value_slice = desc_value.to_h.slice(:value, :structuredValue, :parallelValue)
|
87
|
+
if desc_value_slice[:value].present? || desc_value_slice[:structuredValue].present?
|
88
|
+
slices << desc_value_slice.select { |_k, value| value.present? }
|
89
|
+
elsif desc_value_slice[:parallelValue].present?
|
90
|
+
desc_value_slice[:parallelValue].each { |parallel_val| slices << value_slices(parallel_val) }
|
91
|
+
end
|
92
|
+
# ignoring groupedValue
|
93
|
+
slices.flatten
|
94
|
+
end
|
95
|
+
# private_class_method :value_slices
|
96
|
+
|
97
|
+
# for a given Hash (from a Cocina DescriptiveValue or Title or Name or ...)
|
98
|
+
# result will be either
|
99
|
+
# { value: 'string value' }
|
100
|
+
# OR
|
101
|
+
# { structuredValue: [ some structuredValue ] }
|
102
|
+
def self.slice_of_value_or_structured_value(hash)
|
103
|
+
if hash[:value].present?
|
104
|
+
hash.slice(:value).select { |_k, value| value.present? }
|
105
|
+
elsif hash[:structuredValue].present?
|
106
|
+
hash.slice(:structuredValue).select { |_k, value| value.present? }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# reduce parallelValues down to value or structuredValue for these slices
|
111
|
+
# @params [Cocina::Models::Title] title
|
112
|
+
# @return [Array<Cocina::Models::DescriptiveValue>] where we are only interested in
|
113
|
+
# hashes containing (either :value or :structureValue) and :note if present
|
114
|
+
def self.title_value_note_slices(title)
|
115
|
+
slices = []
|
116
|
+
title_slice = title.to_h.slice(:value, :structuredValue, :parallelValue, :note)
|
117
|
+
if title_slice[:value].present? || title_slice[:structuredValue].present?
|
118
|
+
slices << title_slice.select { |_k, value| value.present? }
|
119
|
+
elsif title_slice[:parallelValue].present?
|
120
|
+
title_slice[:parallelValue].each do |parallel_val|
|
121
|
+
slices << title_value_note_slices(parallel_val)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
# ignoring groupedValue
|
125
|
+
slices.flatten
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cocina
|
4
|
+
module Models
|
5
|
+
module Builders
|
6
|
+
# RightsDescriptionBuilder
|
7
|
+
class RightsDescriptionBuilder
|
8
|
+
# @param [Cocina::Models::AdminPolicy, Cocina::Models::DRO] cocina_object
|
9
|
+
def self.build(cocina_object)
|
10
|
+
new(cocina_object).build
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(cocina_object)
|
14
|
+
@cocina = cocina_object
|
15
|
+
end
|
16
|
+
|
17
|
+
# This is set up to work for APOs, but this method is to be overridden on sub classes
|
18
|
+
# @return [Cocina::Models::AdminPolicyDefaultAccess]
|
19
|
+
def object_access
|
20
|
+
@object_access ||= cocina.administrative.accessTemplate
|
21
|
+
end
|
22
|
+
|
23
|
+
def build
|
24
|
+
return 'controlled digital lending' if object_access.controlledDigitalLending
|
25
|
+
|
26
|
+
return ['dark'] if object_access.view == 'dark'
|
27
|
+
|
28
|
+
object_level_access
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :cocina
|
34
|
+
|
35
|
+
# rubocop:disable Metrics/MethodLength
|
36
|
+
def object_level_access
|
37
|
+
case object_access.view
|
38
|
+
when 'citation-only'
|
39
|
+
['citation']
|
40
|
+
when 'world'
|
41
|
+
world_object_access
|
42
|
+
when 'location-based'
|
43
|
+
case object_access.download
|
44
|
+
when 'none'
|
45
|
+
["location: #{object_access.location} (no-download)"]
|
46
|
+
else
|
47
|
+
["location: #{object_access.location}"]
|
48
|
+
end
|
49
|
+
when 'stanford'
|
50
|
+
stanford_object_access
|
51
|
+
end
|
52
|
+
end
|
53
|
+
# rubocop:enable Metrics/MethodLength
|
54
|
+
|
55
|
+
def stanford_object_access
|
56
|
+
case object_access.download
|
57
|
+
when 'none'
|
58
|
+
['stanford (no-download)']
|
59
|
+
when 'location-based'
|
60
|
+
# this is an odd case we might want to move away from. See https://github.com/sul-dlss/cocina-models/issues/258
|
61
|
+
['stanford (no-download)', "location: #{object_access.location}"]
|
62
|
+
else
|
63
|
+
['stanford']
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def world_object_access
|
68
|
+
case object_access.download
|
69
|
+
when 'stanford'
|
70
|
+
['stanford', 'world (no-download)']
|
71
|
+
when 'none'
|
72
|
+
['world (no-download)']
|
73
|
+
when 'world'
|
74
|
+
['world']
|
75
|
+
when 'location-based'
|
76
|
+
# this is an odd case we might want to move away from. See https://github.com/sul-dlss/cocina-models/issues/258
|
77
|
+
['world (no-download)', "location: #{object_access.location}"]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|