cocina_display 1.1.3 → 1.2.1

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.standard.yml +1 -1
  4. data/README.md +21 -2
  5. data/config/i18n-tasks.yml +0 -0
  6. data/config/licenses.yml +59 -0
  7. data/config/locales/en.yml +109 -0
  8. data/config/marc_countries.yml +385 -0
  9. data/config/marc_relators.yml +310 -0
  10. data/config/searchworks_languages.yml +520 -0
  11. data/lib/cocina_display/cocina_record.rb +29 -64
  12. data/lib/cocina_display/concerns/accesses.rb +78 -0
  13. data/lib/cocina_display/concerns/contributors.rb +32 -11
  14. data/lib/cocina_display/concerns/events.rb +19 -6
  15. data/lib/cocina_display/concerns/forms.rb +98 -11
  16. data/lib/cocina_display/concerns/geospatial.rb +9 -5
  17. data/lib/cocina_display/concerns/identifiers.rb +25 -5
  18. data/lib/cocina_display/concerns/languages.rb +6 -2
  19. data/lib/cocina_display/concerns/notes.rb +36 -0
  20. data/lib/cocina_display/concerns/related_resources.rb +20 -0
  21. data/lib/cocina_display/concerns/subjects.rb +25 -8
  22. data/lib/cocina_display/concerns/titles.rb +67 -25
  23. data/lib/cocina_display/concerns/{access.rb → url_helpers.rb} +3 -3
  24. data/lib/cocina_display/concerns.rb +6 -0
  25. data/lib/cocina_display/contributors/contributor.rb +47 -26
  26. data/lib/cocina_display/contributors/name.rb +18 -14
  27. data/lib/cocina_display/contributors/role.rb +31 -13
  28. data/lib/cocina_display/dates/date.rb +55 -14
  29. data/lib/cocina_display/dates/date_range.rb +0 -2
  30. data/lib/cocina_display/description/access.rb +41 -0
  31. data/lib/cocina_display/description/access_contact.rb +11 -0
  32. data/lib/cocina_display/description/url.rb +17 -0
  33. data/lib/cocina_display/display_data.rb +104 -0
  34. data/lib/cocina_display/events/event.rb +8 -4
  35. data/lib/cocina_display/events/imprint.rb +0 -10
  36. data/lib/cocina_display/events/location.rb +9 -3
  37. data/lib/cocina_display/events/note.rb +33 -0
  38. data/lib/cocina_display/forms/form.rb +71 -0
  39. data/lib/cocina_display/forms/genre.rb +12 -0
  40. data/lib/cocina_display/forms/resource_type.rb +38 -0
  41. data/lib/cocina_display/geospatial.rb +1 -1
  42. data/lib/cocina_display/identifier.rb +101 -0
  43. data/lib/cocina_display/json_backed_record.rb +27 -0
  44. data/lib/cocina_display/language.rb +18 -12
  45. data/lib/cocina_display/license.rb +32 -0
  46. data/lib/cocina_display/note.rb +103 -0
  47. data/lib/cocina_display/related_resource.rb +74 -0
  48. data/lib/cocina_display/subjects/subject.rb +32 -9
  49. data/lib/cocina_display/subjects/subject_value.rb +34 -16
  50. data/lib/cocina_display/title.rb +221 -0
  51. data/lib/cocina_display/utils.rb +4 -4
  52. data/lib/cocina_display/version.rb +1 -1
  53. data/lib/cocina_display.rb +32 -2
  54. metadata +46 -12
  55. data/lib/cocina_display/title_builder.rb +0 -397
  56. data/lib/cocina_display/vocabularies/marc_country_codes.rb +0 -393
  57. data/lib/cocina_display/vocabularies/marc_relator_codes.rb +0 -318
  58. data/lib/cocina_display/vocabularies/searchworks_languages.rb +0 -526
@@ -0,0 +1,78 @@
1
+ module CocinaDisplay
2
+ module Concerns
3
+ # Methods for extracting access/location information from a Cocina object.
4
+ module Accesses
5
+ # Display data for all access metadata except contact emails
6
+ # @return [Array<DisplayData>]
7
+ def access_display_data
8
+ CocinaDisplay::DisplayData.from_objects(accesses +
9
+ access_contacts.reject(&:contact_email?) +
10
+ purls +
11
+ urls)
12
+ end
13
+
14
+ # Display data for all access contact email metadata
15
+ # @return [Array<DisplayData>]
16
+ def contact_email_display_data
17
+ CocinaDisplay::DisplayData.from_objects(access_contacts.select(&:contact_email?))
18
+ end
19
+
20
+ # Display data for the use and reproduction statement.
21
+ # Exhibits and EarthWorks handle useAndReproductionStatement like descriptive metadata.
22
+ # @return [Array<CocinaDisplay::DisplayData>]
23
+ def use_and_reproduction_display_data
24
+ CocinaDisplay::DisplayData.from_strings([use_and_reproduction],
25
+ label: I18n.t("cocina_display.field_label.use_and_reproduction"))
26
+ end
27
+
28
+ # Display data for the copyright statement.
29
+ # Exhibits and EarthWorks handle copyright like descriptive metadata.
30
+ # @return [Array<CocinaDisplay::DisplayData>]
31
+ def copyright_display_data
32
+ CocinaDisplay::DisplayData.from_strings([copyright],
33
+ label: I18n.t("cocina_display.field_label.copyright"))
34
+ end
35
+
36
+ def license_display_data
37
+ CocinaDisplay::DisplayData.from_strings([license_description],
38
+ label: I18n.t("cocina_display.field_label.license"))
39
+ end
40
+
41
+ # All access metadata except contact emails and URLs
42
+ # @return [Array<Description::Access>]
43
+ def accesses
44
+ @accesses ||= Enumerator::Chain.new(
45
+ path("$.description.access.physicalLocation.*"),
46
+ path("$.description.access.digitalLocation.*"),
47
+ path("$.description.access.digitalRepository.*")
48
+ ).map { |a| CocinaDisplay::Description::Access.new(a) }
49
+ end
50
+
51
+ # All access contact metadata
52
+ # @return [Array<Description::AccessContact>]
53
+ def access_contacts
54
+ path("$.description.access.accessContact.*").map do |contact|
55
+ CocinaDisplay::Description::AccessContact.new(contact)
56
+ end
57
+ end
58
+
59
+ # All access URL metadata
60
+ # @return [Array<Description::Url>]
61
+ def urls
62
+ path("$.description.access.url.*").map do |url|
63
+ CocinaDisplay::Description::Url.new(url)
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ # The Purl URL to combine with other access metadata
70
+ # @return [Array<DescriptiveValue>]
71
+ def purls
72
+ return [] unless purl_url.present?
73
+
74
+ CocinaDisplay::DisplayData.descriptive_values_from_strings([purl_url], label: I18n.t("cocina_display.field_label.purl"))
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,5 +1,3 @@
1
- require_relative "../contributors/contributor"
2
-
3
1
  module CocinaDisplay
4
2
  module Concerns
5
3
  # Methods for finding and formatting names for contributors
@@ -20,20 +18,20 @@ module CocinaDisplay
20
18
  # @param with_date [Boolean] Include life dates, if present
21
19
  # @return [Array<String>]
22
20
  def additional_contributor_names(with_date: false)
23
- additional_contributors.map { |c| c.display_name(with_date: with_date) }.compact
21
+ additional_contributors.flat_map { |c| c.display_names(with_date: with_date) }.compact
24
22
  end
25
23
 
26
24
  # All names of publishers, formatted for display.
27
25
  # @return [Array<String>]
28
26
  def publisher_names
29
- publisher_contributors.map(&:display_name).compact
27
+ publisher_contributors.flat_map(&:display_names).compact
30
28
  end
31
29
 
32
30
  # All names of contributors who are people, formatted for display.
33
31
  # @param with_date [Boolean] Include life dates, if present
34
32
  # @return [Array<String>]
35
33
  def person_contributor_names(with_date: false)
36
- contributors.filter(&:person?).map { |c| c.display_name(with_date: with_date) }.compact
34
+ contributors.filter(&:person?).flat_map { |c| c.display_names(with_date: with_date) }.compact
37
35
  end
38
36
 
39
37
  # All names of non-person contributors, formatted for display.
@@ -41,35 +39,58 @@ module CocinaDisplay
41
39
  # @return [Array<String>]
42
40
  # @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#contributor-types
43
41
  def impersonal_contributor_names
44
- contributors.reject(&:person?).map(&:display_name).compact
42
+ contributors.reject(&:person?).flat_map(&:display_names).compact
45
43
  end
46
44
 
47
45
  # All names of contributors that are organizations, formatted for display.
48
46
  # @return [Array<String>]
49
47
  def organization_contributor_names
50
- contributors.filter(&:organization?).map(&:display_name).compact
48
+ contributors.filter(&:organization?).flat_map(&:display_names).compact
51
49
  end
52
50
 
53
51
  # All names of contributors that are conferences, formatted for display.
54
52
  # @return [Array<String>]
55
53
  def conference_contributor_names
56
- contributors.filter(&:conference?).map(&:display_name).compact
54
+ contributors.filter(&:conference?).flat_map(&:display_names).compact
57
55
  end
58
56
 
59
57
  # A hash mapping role names to the names of contributors with that role.
60
58
  # @param with_date [Boolean] Include life dates, if present
61
59
  # @return [Hash<String, Array<String>>]
62
60
  def contributor_names_by_role(with_date: false)
63
- contributors.each_with_object({}) do |contributor, hash|
64
- if (name = contributor.display_name(with_date: with_date)).present?
61
+ contributors_by_role(with_date: with_date)
62
+ .transform_values { |contributor_list| contributor_list.flat_map { |contributor| contributor.display_names(with_date: with_date) }.compact_blank }
63
+ .compact_blank
64
+ end
65
+
66
+ # A hash mapping role names to the names of contributors with that role.
67
+ # @param with_date [Boolean] Include life dates, if present
68
+ # @return [Hash<[String,NilClass], Array<Contributor>>]
69
+ def contributors_by_role(with_date: false)
70
+ @contributors_by_role ||= contributors.each_with_object({}) do |contributor, hash|
71
+ if contributor.roles.empty?
72
+ hash[nil] ||= []
73
+ hash[nil] << contributor
74
+ else
65
75
  contributor.roles.each do |role|
66
76
  hash[role.to_s] ||= []
67
- hash[role.to_s] << name
77
+ hash[role.to_s] << contributor
68
78
  end
69
79
  end
70
80
  end
71
81
  end
72
82
 
83
+ # DisplayData for Contributors, one per role.
84
+ # Contributors with no role are grouped under a default heading.
85
+ # @return [Array<DisplayData>]
86
+ def contributor_display_data
87
+ contributors_by_role.map do |role, contributors|
88
+ label = I18n.t(role, scope: "cocina_display.contributor.role",
89
+ default: role&.capitalize || I18n.t("default", scope: "cocina_display.contributor.role"))
90
+ DisplayData.new(label: label, objects: contributors)
91
+ end
92
+ end
93
+
73
94
  # A string value for sorting by contributor that sorts missing values last.
74
95
  # Appends the sort title to break ties between contributor names.
75
96
  # Ignores punctuation and leading/trailing spaces.
@@ -1,8 +1,3 @@
1
- require_relative "../dates/date"
2
- require_relative "../dates/date_range"
3
- require_relative "../events/event"
4
- require_relative "../events/imprint"
5
-
6
1
  module CocinaDisplay
7
2
  module Concerns
8
3
  module Events
@@ -86,12 +81,18 @@ module CocinaDisplay
86
81
  publication_events.flat_map { |event| event.locations.map(&:to_s) }
87
82
  end
88
83
 
89
- # All events associated with the object.
84
+ # All root level events associated with the object.
90
85
  # @return [Array<CocinaDisplay::Events::Event>]
91
86
  def events
92
87
  @events ||= path("$.description.event.*").map { |event| CocinaDisplay::Events::Event.new(event) }
93
88
  end
94
89
 
90
+ # The adminMetadata creation event (When was it was deposited?)
91
+ # @return <CocinaDisplay::Events::Event>
92
+ def admin_creation_event
93
+ @admin_events ||= path("$.description.adminMetadata.event[?(@.type==\"creation\")]").map { |event| CocinaDisplay::Events::Event.new(event) }.first
94
+ end
95
+
95
96
  # All events that could be used to select a publication date.
96
97
  # Includes publication, creation, and capture events.
97
98
  # Considers event types as well as date types if the event is untyped.
@@ -121,6 +122,18 @@ module CocinaDisplay
121
122
  @event_dates ||= events.flat_map(&:dates)
122
123
  end
123
124
 
125
+ # DisplayData for all notes associated with events.
126
+ # @return [Array<CocinaDisplay::DisplayData>]
127
+ def event_note_display_data
128
+ CocinaDisplay::DisplayData.from_objects(events.flat_map(&:notes))
129
+ end
130
+
131
+ # DisplayData for all dates associated with events.
132
+ # @return [Array<CocinaDisplay::DisplayData>]
133
+ def event_date_display_data
134
+ CocinaDisplay::DisplayData.from_objects(event_dates)
135
+ end
136
+
124
137
  # The earliest preferred publication date as a CocinaDisplay::Dates::Date object.
125
138
  # Considers publication, creation, and capture dates in that order.
126
139
  # Prefers dates marked as primary and those with a declared encoding.
@@ -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 resource_types
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
- path("$.description.form..[?@.type == 'form'].value").uniq
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
- path("$.description.form..[?@.type == 'extent'].value").uniq
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
- path("$.description.form..[?@.type == 'genre'].value").map(&:upcase_first).uniq
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
- path("$.description.event.*.note[?@.type == 'issuance'].value").map(&:downcase).uniq
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
- path("$.description.event.*.note[?@.type == 'frequency'].value").map(&:downcase).uniq
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
- coordinate_subjects.map(&:to_s).compact.uniq
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
- coordinate_subjects.filter_map(&:coordinates)
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 coordinate_subjects
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,7 @@ 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
- path("$.description.identifier[?match(@.type, 'doi|DOI')].value").first ||
31
- path("$.description.identifier[?search(@.uri, 'doi.org')].uri").first
32
-
33
- URI(doi_id).path.delete_prefix("/") if doi_id.present?
29
+ identifiers.find(&:doi?)&.identifier
34
30
  end
35
31
 
36
32
  # The DOI as a URL, if there is one. Any valid DOI should resolve via doi.org.
@@ -66,6 +62,30 @@ module CocinaDisplay
66
62
  def searchworks_id
67
63
  folio_hrid || bare_druid
68
64
  end
65
+
66
+ # Identifier objects extracted from the Cocina metadata.
67
+ # @return [Array<Identifier>]
68
+ def identifiers
69
+ @identifiers ||= path("$.description.identifier[*]").map { |id| Identifier.new(id) } + Array(doi_from_identification)
70
+ end
71
+
72
+ # Labelled display data for identifiers.
73
+ # @return [Array<DisplayData>]
74
+ def identifier_display_data
75
+ CocinaDisplay::DisplayData.from_objects(identifiers)
76
+ end
77
+
78
+ private
79
+
80
+ # Synthetic Identifier object for a DOI in the identification block.
81
+ # @return [Array<Identifier>]
82
+ def doi_from_identification
83
+ id = path("$.identification.doi").first
84
+ return if id.blank?
85
+
86
+ value_key = id.start_with?("http") ? "uri" : "value"
87
+ Identifier.new({value_key => id, "type" => "doi"})
88
+ end
69
89
  end
70
90
  end
71
91
  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
- subjects.map(&:to_s).uniq
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 subjects
99
- @subjects ||= Enumerator::Chain.new(
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 ||= subjects.flat_map(&: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.