cocina_display 1.1.3 → 1.2.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.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.standard.yml +1 -1
- data/README.md +21 -2
- data/config/i18n-tasks.yml +0 -0
- data/config/licenses.yml +59 -0
- data/config/locales/en.yml +109 -0
- data/lib/cocina_display/cocina_record.rb +27 -63
- data/lib/cocina_display/concerns/accesses.rb +78 -0
- data/lib/cocina_display/concerns/contributors.rb +32 -11
- data/lib/cocina_display/concerns/events.rb +19 -6
- data/lib/cocina_display/concerns/forms.rb +98 -11
- data/lib/cocina_display/concerns/geospatial.rb +9 -5
- data/lib/cocina_display/concerns/identifiers.rb +15 -4
- data/lib/cocina_display/concerns/languages.rb +6 -2
- data/lib/cocina_display/concerns/notes.rb +36 -0
- data/lib/cocina_display/concerns/related_resources.rb +20 -0
- data/lib/cocina_display/concerns/subjects.rb +25 -8
- data/lib/cocina_display/concerns/titles.rb +67 -25
- data/lib/cocina_display/concerns/{access.rb → url_helpers.rb} +3 -3
- data/lib/cocina_display/concerns.rb +6 -0
- data/lib/cocina_display/contributors/contributor.rb +47 -26
- data/lib/cocina_display/contributors/name.rb +18 -14
- data/lib/cocina_display/contributors/role.rb +20 -13
- data/lib/cocina_display/dates/date.rb +55 -14
- data/lib/cocina_display/dates/date_range.rb +0 -2
- data/lib/cocina_display/description/access.rb +41 -0
- data/lib/cocina_display/description/access_contact.rb +11 -0
- data/lib/cocina_display/description/url.rb +17 -0
- data/lib/cocina_display/display_data.rb +104 -0
- data/lib/cocina_display/events/event.rb +8 -4
- data/lib/cocina_display/events/imprint.rb +0 -10
- data/lib/cocina_display/events/location.rb +0 -2
- data/lib/cocina_display/events/note.rb +33 -0
- data/lib/cocina_display/forms/form.rb +71 -0
- data/lib/cocina_display/forms/genre.rb +12 -0
- data/lib/cocina_display/forms/resource_type.rb +38 -0
- data/lib/cocina_display/geospatial.rb +1 -1
- data/lib/cocina_display/identifier.rb +101 -0
- data/lib/cocina_display/json_backed_record.rb +27 -0
- data/lib/cocina_display/language.rb +9 -11
- data/lib/cocina_display/license.rb +32 -0
- data/lib/cocina_display/note.rb +103 -0
- data/lib/cocina_display/related_resource.rb +74 -0
- data/lib/cocina_display/subjects/subject.rb +32 -9
- data/lib/cocina_display/subjects/subject_value.rb +34 -16
- data/lib/cocina_display/title.rb +194 -0
- data/lib/cocina_display/utils.rb +4 -4
- data/lib/cocina_display/version.rb +1 -1
- data/lib/cocina_display.rb +30 -2
- metadata +45 -11
- data/lib/cocina_display/title_builder.rb +0 -397
- /data/lib/cocina_display/vocabularies/{marc_country_codes.rb → marc_country.rb} +0 -0
- /data/lib/cocina_display/vocabularies/{marc_relator_codes.rb → marc_relator.rb} +0 -0
@@ -6,18 +6,24 @@ module CocinaDisplay
|
|
6
6
|
module Forms
|
7
7
|
# Resource types of the object, expressed in SearchWorks controlled vocabulary.
|
8
8
|
# @return [Array<String>]
|
9
|
-
def
|
9
|
+
def searchworks_resource_types
|
10
10
|
mapped_values = resource_type_values.flat_map { |resource_type| searchworks_resource_type(resource_type) }
|
11
11
|
mapped_values << "Dataset" if dataset?
|
12
12
|
mapped_values.uniq
|
13
13
|
end
|
14
14
|
|
15
|
+
# Resource types of the object, expressed in SearchWorks controlled vocabulary.
|
16
|
+
# @return [Array<String>]
|
17
|
+
def mods_resource_types
|
18
|
+
all_resource_types.select { |type| type.mods? }.map(&:to_s)
|
19
|
+
end
|
20
|
+
|
15
21
|
# Physical or digital forms of the object.
|
16
22
|
# @return [Array<String>]
|
17
23
|
# @example GIS dataset (nz187ct8959)
|
18
24
|
# record.forms #=> ["map", "optical disc", "electronic resource"]
|
19
25
|
def forms
|
20
|
-
|
26
|
+
form_forms.map(&:to_s).compact_blank.uniq
|
21
27
|
end
|
22
28
|
|
23
29
|
# Extent of the object, such as "1 audiotape" or "1 map".
|
@@ -25,7 +31,7 @@ module CocinaDisplay
|
|
25
31
|
# @example Oral history interview (sw705fr7011)
|
26
32
|
# record.extents #=> ["1 audiotape", "1 transcript"]
|
27
33
|
def extents
|
28
|
-
|
34
|
+
extent_forms.map(&:to_s).compact_blank.uniq
|
29
35
|
end
|
30
36
|
|
31
37
|
# Genres of the object, capitalized for display.
|
@@ -33,7 +39,7 @@ module CocinaDisplay
|
|
33
39
|
# @example GIS dataset (nz187ct8959)
|
34
40
|
# record.genres #=> ["Cartographic dataset", "Geospatial data", "Geographic information systems data"]
|
35
41
|
def genres
|
36
|
-
|
42
|
+
genre_forms.map(&:to_s).compact_blank.uniq
|
37
43
|
end
|
38
44
|
|
39
45
|
# Genres of the object, with additional values added for search/faceting.
|
@@ -47,6 +53,34 @@ module CocinaDisplay
|
|
47
53
|
end.uniq
|
48
54
|
end
|
49
55
|
|
56
|
+
# All form-related data to be rendered for display.
|
57
|
+
# Includes form, extent, resource type, etc. (but not self-deposit resource types).
|
58
|
+
# @return [Array<DisplayData>]
|
59
|
+
def form_display_data
|
60
|
+
CocinaDisplay::DisplayData.from_objects(all_forms - genre_forms - map_forms - media_forms - self_deposit_resource_types)
|
61
|
+
end
|
62
|
+
|
63
|
+
# All genre-related data to be rendered for display.
|
64
|
+
# Includes both form genres, subject genres, and self-deposit resource types.
|
65
|
+
# @return [Array<DisplayData>]
|
66
|
+
def genre_display_data
|
67
|
+
CocinaDisplay::DisplayData.from_objects(genre_forms + genre_subjects + self_deposit_resource_types)
|
68
|
+
end
|
69
|
+
|
70
|
+
# All map-related data to be rendered for display.
|
71
|
+
# Includes map scale, projection info, and geographic coordinate subjects.
|
72
|
+
# @return [Array<DisplayData>]
|
73
|
+
def map_display_data
|
74
|
+
CocinaDisplay::DisplayData.from_objects(map_forms + coordinate_subjects)
|
75
|
+
end
|
76
|
+
|
77
|
+
# All form notes to be rendered for display.
|
78
|
+
# @return [Array<DisplayData>]
|
79
|
+
def form_note_display_data
|
80
|
+
CocinaDisplay::DisplayData.from_cocina(path("$.description.form[*].note[*]"),
|
81
|
+
label: I18n.t("cocina_display.field_label.form.note"))
|
82
|
+
end
|
83
|
+
|
50
84
|
# Is the object a periodical or serial?
|
51
85
|
# @return [Boolean]
|
52
86
|
def periodical?
|
@@ -73,6 +107,46 @@ module CocinaDisplay
|
|
73
107
|
|
74
108
|
private
|
75
109
|
|
110
|
+
# Collapses all nested form values into an array of {Form} objects.
|
111
|
+
# Preserves resource type without flattening, since it can be structured.
|
112
|
+
# @return [Array<Form>]
|
113
|
+
def all_forms
|
114
|
+
@all_forms ||= path("$.description.form.*")
|
115
|
+
.flat_map { |form| Utils.flatten_nested_values(form, atomic_types: ["resource type"]) }
|
116
|
+
.map { |form| CocinaDisplay::Forms::Form.from_cocina(form) }
|
117
|
+
end
|
118
|
+
|
119
|
+
# {Form} objects with type "form".
|
120
|
+
# @return [Array<Form>]
|
121
|
+
def form_forms
|
122
|
+
all_forms.filter { |form| form.type == "form" }
|
123
|
+
end
|
124
|
+
|
125
|
+
# {Form} objects with type "genre".
|
126
|
+
# @return [Array<Form>]
|
127
|
+
def genre_forms
|
128
|
+
all_forms.filter { |form| form.type == "genre" }
|
129
|
+
end
|
130
|
+
|
131
|
+
# {Form} objects with type "extent".
|
132
|
+
# @return [Array<Form>]
|
133
|
+
def extent_forms
|
134
|
+
all_forms.filter { |form| form.type == "extent" }
|
135
|
+
end
|
136
|
+
|
137
|
+
# {Form} objects with types related to map data.
|
138
|
+
# @return [Array<Form>]
|
139
|
+
def map_forms
|
140
|
+
all_forms.filter { |form| ["map scale", "map projection"].include?(form.type) }
|
141
|
+
end
|
142
|
+
|
143
|
+
# {Form} objects with types that are media-related.
|
144
|
+
# @note These are excluded from the general form display data.
|
145
|
+
# @return [Array<Form>]
|
146
|
+
def media_forms
|
147
|
+
all_forms.filter { |form| ["reformatting quality", "media type"].include?(form.type) }
|
148
|
+
end
|
149
|
+
|
76
150
|
# Map a resource type to SearchWorks format value(s).
|
77
151
|
# @param resource_type [String] The resource type to map.
|
78
152
|
# @return [Array<String>]
|
@@ -113,21 +187,34 @@ module CocinaDisplay
|
|
113
187
|
values.compact_blank
|
114
188
|
end
|
115
189
|
|
190
|
+
# All resource types forms, as {ResourceType}s.
|
191
|
+
# @return [Array<ResourceType>]
|
192
|
+
def all_resource_types
|
193
|
+
all_forms.filter { |form| form.is_a?(CocinaDisplay::Forms::ResourceType) }
|
194
|
+
end
|
195
|
+
|
196
|
+
# {ResourceType} objects that are Stanford self-deposit resource types.
|
197
|
+
# @return [Array<ResourceType>]
|
198
|
+
def self_deposit_resource_types
|
199
|
+
all_resource_types.filter { |resource_type| resource_type.stanford_self_deposit? }
|
200
|
+
end
|
201
|
+
|
202
|
+
# Display values of all resource types.
|
203
|
+
# @return [Array<String>]
|
204
|
+
def resource_type_values
|
205
|
+
all_resource_types.map(&:to_s).uniq
|
206
|
+
end
|
207
|
+
|
116
208
|
# Issuance terms for a work, drawn from the event notes.
|
117
209
|
# @return [Array<String>]
|
118
210
|
def issuance_terms
|
119
|
-
|
211
|
+
events.flat_map(&:notes).filter { |note| note.type == "issuance" }.map { |note| note.to_s.downcase }.uniq
|
120
212
|
end
|
121
213
|
|
122
214
|
# Frequency terms for a periodical, drawn from the event notes.
|
123
215
|
# @return [Array<String>]
|
124
216
|
def frequency
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
# Values of the resource type form field prior to mapping.
|
129
|
-
def resource_type_values
|
130
|
-
path("$.description.form..[?@.type == 'resource type'].value").uniq
|
217
|
+
events.flat_map(&:notes).filter { |note| note.type == "frequency" }.map { |note| note.to_s.downcase }.uniq
|
131
218
|
end
|
132
219
|
end
|
133
220
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "../subjects/subject_value"
|
4
|
-
|
5
3
|
module CocinaDisplay
|
6
4
|
module Concerns
|
7
5
|
# Methods for extracting geospatial metadata, such as coordinates.
|
@@ -10,7 +8,7 @@ module CocinaDisplay
|
|
10
8
|
# @return [Array<String>]
|
11
9
|
# @example ["34°03′08″N 118°14′37″W"]
|
12
10
|
def coordinates
|
13
|
-
|
11
|
+
coordinate_subject_values.map(&:to_s).compact.uniq
|
14
12
|
end
|
15
13
|
|
16
14
|
# All valid coordinate data formatted for indexing into a Solr RPT field.
|
@@ -49,15 +47,21 @@ module CocinaDisplay
|
|
49
47
|
|
50
48
|
private
|
51
49
|
|
50
|
+
# {Subject} objects with types that could contain coordinate information.
|
51
|
+
# @return [Array<Subject>]
|
52
|
+
def coordinate_subjects
|
53
|
+
all_subjects.filter { |subject| subject.type&.include? "coordinates" }
|
54
|
+
end
|
55
|
+
|
52
56
|
# Parsed coordinate values from the coordinate subject values.
|
53
57
|
# @return [Array<Geospatial::Coordinates>]
|
54
58
|
def coordinate_objects
|
55
|
-
|
59
|
+
coordinate_subject_values.filter_map(&:coordinates)
|
56
60
|
end
|
57
61
|
|
58
62
|
# All subject values that could contain parsed coordinates.
|
59
63
|
# @return [Array<Subjects::CoordinatesSubjectValue>]
|
60
|
-
def
|
64
|
+
def coordinate_subject_values
|
61
65
|
subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::CoordinatesSubjectValue }
|
62
66
|
end
|
63
67
|
end
|
@@ -26,11 +26,10 @@ module CocinaDisplay
|
|
26
26
|
# @example
|
27
27
|
# record.doi #=> "10.25740/ppax-bf07"
|
28
28
|
def doi
|
29
|
-
doi_id = path("$.identification.doi").first
|
30
|
-
|
31
|
-
path("$.description.identifier[?search(@.uri, 'doi.org')].uri").first
|
29
|
+
doi_id = path("$.identification.doi").first
|
30
|
+
return URI(doi_id).path.delete_prefix("/") if doi_id.present?
|
32
31
|
|
33
|
-
|
32
|
+
identifiers.find(&:doi?)&.identifier
|
34
33
|
end
|
35
34
|
|
36
35
|
# The DOI as a URL, if there is one. Any valid DOI should resolve via doi.org.
|
@@ -66,6 +65,18 @@ module CocinaDisplay
|
|
66
65
|
def searchworks_id
|
67
66
|
folio_hrid || bare_druid
|
68
67
|
end
|
68
|
+
|
69
|
+
# Identifier objects extracted from the Cocina descriptive metadata.
|
70
|
+
# @return [Array<Identifier>]
|
71
|
+
def identifiers
|
72
|
+
@identifiers ||= path("$.description.identifier[*]").map { |id| Identifier.new(id) }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Labelled display data for identifiers.
|
76
|
+
# @return [Array<DisplayData>]
|
77
|
+
def identifier_display_data
|
78
|
+
CocinaDisplay::DisplayData.from_objects(identifiers)
|
79
|
+
end
|
69
80
|
end
|
70
81
|
end
|
71
82
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative "../language"
|
2
|
-
|
3
1
|
module CocinaDisplay
|
4
2
|
module Concerns
|
5
3
|
# Methods for extracting language information from a Cocina object.
|
@@ -15,6 +13,12 @@ module CocinaDisplay
|
|
15
13
|
def searchworks_language_names
|
16
14
|
languages.filter_map { |lang| lang.to_s if lang.searchworks_language? }.compact_blank.uniq
|
17
15
|
end
|
16
|
+
|
17
|
+
# Language information for display.
|
18
|
+
# @return [Array<CocinaDisplay::DisplayData>]
|
19
|
+
def language_display_data
|
20
|
+
CocinaDisplay::DisplayData.from_objects(languages)
|
21
|
+
end
|
18
22
|
end
|
19
23
|
end
|
20
24
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CocinaDisplay
|
2
|
+
module Concerns
|
3
|
+
# Methods for extracting note information from Cocina.
|
4
|
+
module Notes
|
5
|
+
# Note objects associated with the cocina record.
|
6
|
+
# @return [Array<CocinaDisplay::Note>]
|
7
|
+
def notes
|
8
|
+
@notes ||= path("$.description.note.*").map { |note| CocinaDisplay::Note.new(note) }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Abstract metadata for display.
|
12
|
+
# @return [Array<CocinaDisplay::DisplayData>]
|
13
|
+
def abstract_display_data
|
14
|
+
CocinaDisplay::DisplayData.from_objects(notes.select(&:abstract?))
|
15
|
+
end
|
16
|
+
|
17
|
+
# General note metadata for display.
|
18
|
+
# @return [Array<CocinaDisplay::DisplayData>]
|
19
|
+
def general_note_display_data
|
20
|
+
CocinaDisplay::DisplayData.from_objects(notes.select(&:general_note?))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Preferred citation metadata for display.
|
24
|
+
# @return [Array<CocinaDisplay::DisplayData>]
|
25
|
+
def preferred_citation_display_data
|
26
|
+
CocinaDisplay::DisplayData.from_objects(notes.select(&:preferred_citation?))
|
27
|
+
end
|
28
|
+
|
29
|
+
# Table of contents metadata for display.
|
30
|
+
# @return [Array<CocinaDisplay::DisplayData>]
|
31
|
+
def table_of_contents_display_data
|
32
|
+
CocinaDisplay::DisplayData.from_objects(notes.select(&:table_of_contents?))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module CocinaDisplay
|
2
|
+
module Concerns
|
3
|
+
# Methods for extracting and formatting related resources from Cocina records.
|
4
|
+
module RelatedResources
|
5
|
+
# Resources related to the object.
|
6
|
+
# @return [Array<CocinaDisplay::RelatedResource>]
|
7
|
+
def related_resources
|
8
|
+
@related_resources ||= path("$.description.relatedResource[*]").map { |res| RelatedResource.new(res) }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Display data for related resources.
|
12
|
+
# @note Related resources also have their own nested display data.
|
13
|
+
# @see CocinaDisplay::RelatedResource#display_data
|
14
|
+
# @return [Array<DisplayData>]
|
15
|
+
def related_resource_display_data
|
16
|
+
DisplayData.from_objects(related_resources)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,6 +1,3 @@
|
|
1
|
-
require_relative "../subjects/subject"
|
2
|
-
require_relative "../subjects/subject_value"
|
3
|
-
|
4
1
|
module CocinaDisplay
|
5
2
|
module Concerns
|
6
3
|
# Methods for extracting and formatting subject information.
|
@@ -87,25 +84,45 @@ module CocinaDisplay
|
|
87
84
|
# @see Subject#to_s
|
88
85
|
# @return [Array<String>]
|
89
86
|
def subject_all_display
|
90
|
-
|
87
|
+
all_subjects.map(&:to_s).uniq
|
88
|
+
end
|
89
|
+
|
90
|
+
# Subject data to be rendered for display.
|
91
|
+
# Uses the concatenated form for structured subject values.
|
92
|
+
# @see Subject#to_s
|
93
|
+
# @return [Array<DisplayData>]
|
94
|
+
def subject_display_data
|
95
|
+
CocinaDisplay::DisplayData.from_objects(all_subjects - classification_subjects - genre_subjects - coordinate_subjects)
|
91
96
|
end
|
92
97
|
|
93
98
|
private
|
94
99
|
|
95
|
-
# All subjects, accessible as Subject objects.
|
100
|
+
# All subjects, accessible as {Subject} objects.
|
96
101
|
# Checks both description.subject and description.geographic.subject.
|
97
102
|
# @return [Array<Subject>]
|
98
|
-
def
|
99
|
-
@
|
103
|
+
def all_subjects
|
104
|
+
@all_subjects ||= Enumerator::Chain.new(
|
100
105
|
path("$.description.subject[*]"),
|
101
106
|
path("$.description.geographic.*.subject[*]")
|
102
107
|
).map { |s| CocinaDisplay::Subjects::Subject.new(s) }
|
103
108
|
end
|
104
109
|
|
110
|
+
# {Subject} objects with type "genre".
|
111
|
+
# @return [Array<Subject>]
|
112
|
+
def genre_subjects
|
113
|
+
all_subjects.filter { |subject| subject.type == "genre" }
|
114
|
+
end
|
115
|
+
|
116
|
+
# {Subject} objects with type "classification".
|
117
|
+
# @return [Array<Subject>]
|
118
|
+
def classification_subjects
|
119
|
+
all_subjects.filter { |subject| subject.type == "classification" }
|
120
|
+
end
|
121
|
+
|
105
122
|
# All subject values, flattened from all subjects.
|
106
123
|
# @return [Array<SubjectValue>]
|
107
124
|
def subject_values
|
108
|
-
@subject_values ||=
|
125
|
+
@subject_values ||= all_subjects.flat_map(&:subject_values)
|
109
126
|
end
|
110
127
|
|
111
128
|
# All subject values that are named places.
|
@@ -1,48 +1,77 @@
|
|
1
|
-
require_relative "../title_builder"
|
2
|
-
|
3
1
|
module CocinaDisplay
|
4
2
|
module Concerns
|
5
3
|
# Methods for finding and formatting titles.
|
6
4
|
module Titles
|
7
5
|
# The main title for the object, without subtitle, part name, etc.
|
8
|
-
# If there are multiple titles, uses the first.
|
9
|
-
# @see CocinaDisplay::
|
10
|
-
# @
|
11
|
-
# @return [String]
|
6
|
+
# If there are multiple primary titles, uses the first.
|
7
|
+
# @see CocinaDisplay::Title#main_title
|
8
|
+
# @return [String, nil]
|
12
9
|
def main_title
|
13
|
-
|
10
|
+
primary_title&.short_title
|
14
11
|
end
|
15
12
|
|
16
13
|
# The full title for the object, including subtitle, part name, etc.
|
17
|
-
# If there are multiple titles, uses the first.
|
18
|
-
# @see CocinaDisplay::
|
19
|
-
# @
|
20
|
-
# @return [String]
|
14
|
+
# If there are multiple primary titles, uses the first.
|
15
|
+
# @see CocinaDisplay::Title#full_title
|
16
|
+
# @return [String, nil]
|
21
17
|
def full_title
|
22
|
-
|
18
|
+
primary_title&.full_title
|
23
19
|
end
|
24
20
|
|
25
21
|
# The full title, joined together with additional punctuation.
|
26
|
-
# If there are multiple titles, uses the first.
|
27
|
-
# @see CocinaDisplay::
|
28
|
-
# @return [String]
|
22
|
+
# If there are multiple primary titles, uses the first.
|
23
|
+
# @see CocinaDisplay::Title#display_title
|
24
|
+
# @return [String, nil]
|
29
25
|
def display_title
|
30
|
-
|
26
|
+
primary_title&.display_title
|
27
|
+
end
|
28
|
+
|
29
|
+
# A string value for sorting by title that sorts missing values last.
|
30
|
+
# If there are multiple primary titles, uses the first.
|
31
|
+
# @see CocinaDisplay::Title#sort_title
|
32
|
+
# @return [String]
|
33
|
+
def sort_title
|
34
|
+
primary_title&.sort_title || "\u{10FFFF}"
|
31
35
|
end
|
32
36
|
|
33
|
-
# Any additional titles for the object excluding the
|
37
|
+
# Any additional titles for the object excluding the primary title.
|
34
38
|
# @return [Array<String>]
|
35
|
-
# @see CocinaDisplay::
|
39
|
+
# @see CocinaDisplay::Title#display_title
|
36
40
|
def additional_titles
|
37
|
-
|
41
|
+
secondary_titles.map(&:display_title).compact_blank
|
38
42
|
end
|
39
43
|
|
40
|
-
#
|
41
|
-
#
|
42
|
-
# @
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
# All {Title} objects, grouped by their label for display.
|
45
|
+
# @note All primary titles are included under "Title", not just the first.
|
46
|
+
# @return [Array<DisplayData>]
|
47
|
+
def title_display_data
|
48
|
+
DisplayData.from_objects(all_titles)
|
49
|
+
end
|
50
|
+
|
51
|
+
# The first title marked primary, or the first without a type.
|
52
|
+
# @return [Array<Title>]
|
53
|
+
def primary_title
|
54
|
+
all_titles.find { |title| title.primary? }.presence || all_titles.find { |title| !title.type? }
|
55
|
+
end
|
56
|
+
|
57
|
+
# All titles except the primary title.
|
58
|
+
# @return [Array<Title>]
|
59
|
+
def secondary_titles
|
60
|
+
all_titles - [primary_title]
|
61
|
+
end
|
62
|
+
|
63
|
+
# All {Title} objects built from the Cocina titles.
|
64
|
+
# Flattens parallel values into separate titles.
|
65
|
+
# @return [Array<Title>]
|
66
|
+
def all_titles
|
67
|
+
@all_titles ||= cocina_titles.flat_map do |cocina_title|
|
68
|
+
(Array(cocina_title["parallelValue"]).presence || [cocina_title]).map do |value|
|
69
|
+
Title.new(value, part_label: part_label, part_numbers: part_numbers).tap do |title|
|
70
|
+
title.type ||= cocina_title["type"]
|
71
|
+
title.status ||= cocina_title["status"]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
46
75
|
end
|
47
76
|
|
48
77
|
private
|
@@ -59,6 +88,19 @@ module CocinaDisplay
|
|
59
88
|
def catalog_links
|
60
89
|
@catalog_links ||= Array(cocina_doc.dig("identification", "catalogLinks"))
|
61
90
|
end
|
91
|
+
|
92
|
+
# Part label for digital serials display from FOLIO, if any.
|
93
|
+
# @return [String, nil]
|
94
|
+
def part_label
|
95
|
+
catalog_links.find { |link| link["catalog"] == "folio" }&.fetch("partLabel", nil)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Part numbers from notes, if any.
|
99
|
+
# @note This is used for RelatedResource title display.
|
100
|
+
# @return [Array<String>]
|
101
|
+
def part_numbers
|
102
|
+
notes.filter(&:part?).flat_map { |note| note.values_by_type["number"] }.compact_blank
|
103
|
+
end
|
62
104
|
end
|
63
105
|
end
|
64
106
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module CocinaDisplay
|
2
2
|
module Concerns
|
3
3
|
# Methods that generate URLs to access an object.
|
4
|
-
module
|
4
|
+
module UrlHelpers
|
5
5
|
# The PURL URL for this object.
|
6
6
|
# @return [String]
|
7
7
|
# @example
|
8
8
|
# record.purl_url #=> "https://purl.stanford.edu/bx658jh7339"
|
9
9
|
def purl_url
|
10
|
-
cocina_doc.dig("description", "purl")
|
10
|
+
cocina_doc.dig("description", "purl")
|
11
11
|
end
|
12
12
|
|
13
13
|
# The oEmbed URL for the object, optionally with additional parameters.
|
@@ -18,7 +18,7 @@ module CocinaDisplay
|
|
18
18
|
# @example Generate an oEmbed URL for the viewer and hide the title
|
19
19
|
# record.oembed_url(hide_title: true) #=> "https://purl.stanford.edu/bx658jh7339/embed.json?hide_title=true"
|
20
20
|
def oembed_url(params: {})
|
21
|
-
return if collection? || purl_url.blank?
|
21
|
+
return if (!is_a?(CocinaDisplay::RelatedResource) && collection?) || purl_url.blank?
|
22
22
|
|
23
23
|
params[:url] ||= purl_url
|
24
24
|
"#{purl_base_url}/embed.json?#{params.to_query}"
|
@@ -1,13 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support"
|
4
|
-
require "active_support/core_ext/object/blank"
|
5
|
-
require "active_support/core_ext/array/conversions"
|
6
|
-
|
7
|
-
require_relative "../utils"
|
8
|
-
require_relative "name"
|
9
|
-
require_relative "role"
|
10
|
-
|
11
3
|
module CocinaDisplay
|
12
4
|
module Contributors
|
13
5
|
# A contributor to a work, such as an author or publisher.
|
@@ -20,11 +12,22 @@ module CocinaDisplay
|
|
20
12
|
@cocina = cocina
|
21
13
|
end
|
22
14
|
|
23
|
-
# String representation of the contributor,
|
24
|
-
#
|
25
|
-
# @return [String]
|
15
|
+
# String representation of the contributor, using display name with date.
|
16
|
+
# @return [String, nil]
|
26
17
|
def to_s
|
27
|
-
|
18
|
+
display_name(with_date: true)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Support equality based on the underlying Cocina data.
|
22
|
+
# @param other [Object]
|
23
|
+
def ==(other)
|
24
|
+
other.is_a?(Contributor) && other.cocina == cocina
|
25
|
+
end
|
26
|
+
|
27
|
+
# Identifiers for the contributor.
|
28
|
+
# @return [Array<Identifier>]
|
29
|
+
def identifiers
|
30
|
+
Array(cocina["identifier"]).map { |id| Identifier.new(id) }
|
28
31
|
end
|
29
32
|
|
30
33
|
# Is this contributor a human?
|
@@ -75,39 +78,57 @@ module CocinaDisplay
|
|
75
78
|
roles.any?
|
76
79
|
end
|
77
80
|
|
78
|
-
# The display name for the contributor as a string.
|
79
|
-
# Uses the first name if multiple names are present.
|
81
|
+
# The primary display name for the contributor as a string.
|
80
82
|
# @param with_date [Boolean] Include life dates, if present
|
81
|
-
# @return [String]
|
83
|
+
# @return [String, nil]
|
82
84
|
def display_name(with_date: false)
|
83
|
-
|
85
|
+
primary_name&.to_s(with_date: with_date)
|
84
86
|
end
|
85
87
|
|
86
|
-
#
|
88
|
+
# String renderings of all names for the contributor.
|
89
|
+
# @param with_date [Boolean] Include life dates, if present
|
90
|
+
# @return [Array<String>]
|
91
|
+
def display_names(with_date: false)
|
92
|
+
names.map { |name| name.to_s(with_date: with_date) }.compact_blank
|
93
|
+
end
|
94
|
+
|
95
|
+
# A single primary name for the contributor.
|
96
|
+
# Prefers a name of type "display" or one marked primary.
|
97
|
+
# @return [Contributor::Name, nil]
|
98
|
+
def primary_name
|
99
|
+
names.find { |name| name.type == "display" }.presence ||
|
100
|
+
names.find(&:primary?).presence ||
|
101
|
+
names.first
|
102
|
+
end
|
103
|
+
|
104
|
+
# The forename for the contributor, if structured name info is available.
|
87
105
|
# @see Contributor::Name::forename_str
|
88
106
|
# @return [String, nil]
|
89
107
|
def forename
|
90
108
|
names.map(&:forename_str).first.presence
|
91
109
|
end
|
92
110
|
|
93
|
-
# The
|
111
|
+
# The surname for the contributor, if structured name info is available.
|
94
112
|
# @see Contributor::Name::surname_str
|
95
113
|
# @return [String, nil]
|
96
114
|
def surname
|
97
115
|
names.map(&:surname_str).first.presence
|
98
116
|
end
|
99
117
|
|
100
|
-
# A string representation of the contributor's roles, formatted for display.
|
101
|
-
# If there are multiple roles, they are joined with commas.
|
102
|
-
# @return [String]
|
103
|
-
def display_role
|
104
|
-
roles.map(&:to_s).to_sentence
|
105
|
-
end
|
106
|
-
|
107
118
|
# All names in the Cocina as Name objects.
|
119
|
+
# Flattens parallel values into separate Name objects.
|
108
120
|
# @return [Array<Name>]
|
109
121
|
def names
|
110
|
-
@names ||= Array(cocina["name"]).
|
122
|
+
@names ||= Array(cocina["name"]).flat_map do |name|
|
123
|
+
(Array(name["parallelValue"]).presence || [name]).filter_map do |name_value|
|
124
|
+
unless name_value.blank?
|
125
|
+
Name.new(name_value).tap do |name_obj|
|
126
|
+
name_obj.type ||= name["type"]
|
127
|
+
name_obj.status ||= name["status"]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
111
132
|
end
|
112
133
|
|
113
134
|
# All roles in the Cocina structured data.
|