cocina_display 0.7.0 → 1.1.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/lib/cocina_display/cocina_record.rb +25 -10
- data/lib/cocina_display/concerns/contributors.rb +4 -4
- data/lib/cocina_display/concerns/events.rb +7 -7
- data/lib/cocina_display/concerns/geospatial.rb +65 -0
- data/lib/cocina_display/concerns/languages.rb +1 -1
- data/lib/cocina_display/concerns/structural.rb +41 -0
- data/lib/cocina_display/concerns/subjects.rb +22 -9
- data/lib/cocina_display/contributors/contributor.rb +120 -0
- data/lib/cocina_display/contributors/name.rb +100 -0
- data/lib/cocina_display/contributors/role.rb +57 -0
- data/lib/cocina_display/events/event.rb +2 -2
- data/lib/cocina_display/events/imprint.rb +2 -2
- data/lib/cocina_display/events/location.rb +1 -1
- data/lib/cocina_display/geospatial.rb +348 -0
- data/lib/cocina_display/language.rb +2 -2
- data/lib/cocina_display/subjects/subject.rb +4 -18
- data/lib/cocina_display/subjects/subject_value.rb +68 -13
- data/lib/cocina_display/utils.rb +7 -2
- data/lib/cocina_display/version.rb +1 -1
- data/script/deep_compact.rb +13 -0
- metadata +23 -3
- data/lib/cocina_display/contributor.rb +0 -234
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7faaded226886ab37cfbff854b64721c55964959089a1675706ae6160d4a1a5c
|
4
|
+
data.tar.gz: 111b995bfdf70805eb7a613bf5f221bacf101be1ed3f9ad6fb2407266c7a35ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a8586d41059d8880ac29c48f564907a51b718dc7812f2314e564032e1f19722d97620755cd53f52aa927e17e45dbfab79b56936c022255f2c22c3147e2c110c
|
7
|
+
data.tar.gz: 58ddd5de3cfe25d78904c24c1edaed72d4212eb7149b58b3b6e5c216390db75acc0c8ad81c039c9c712da99480842df2cab27c70573c848e2cb9377f0a31b909
|
@@ -15,6 +15,8 @@ require_relative "concerns/access"
|
|
15
15
|
require_relative "concerns/subjects"
|
16
16
|
require_relative "concerns/forms"
|
17
17
|
require_relative "concerns/languages"
|
18
|
+
require_relative "concerns/geospatial"
|
19
|
+
require_relative "concerns/structural"
|
18
20
|
require_relative "utils"
|
19
21
|
|
20
22
|
module CocinaDisplay
|
@@ -28,6 +30,8 @@ module CocinaDisplay
|
|
28
30
|
include CocinaDisplay::Concerns::Subjects
|
29
31
|
include CocinaDisplay::Concerns::Forms
|
30
32
|
include CocinaDisplay::Concerns::Languages
|
33
|
+
include CocinaDisplay::Concerns::Geospatial
|
34
|
+
include CocinaDisplay::Concerns::Structural
|
31
35
|
|
32
36
|
# Fetch a public Cocina document from PURL and create a CocinaRecord.
|
33
37
|
# @note This is intended to be used in development or testing only.
|
@@ -107,16 +111,27 @@ module CocinaDisplay
|
|
107
111
|
content_type == "collection"
|
108
112
|
end
|
109
113
|
|
110
|
-
#
|
111
|
-
#
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
114
|
+
# Resources related to the object.
|
115
|
+
# @return [Array<CocinaDisplay::RelatedResource>]
|
116
|
+
def related_resources
|
117
|
+
@related_resources ||= path("$.description.relatedResource[*]").map { |res| RelatedResource.new(res) }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# A resource related to the record; behaves like a CocinaRecord.
|
122
|
+
# @note Related resources have no structural metadata.
|
123
|
+
class RelatedResource < CocinaRecord
|
124
|
+
# Description of the relation to the source record.
|
125
|
+
# @return [String]
|
126
|
+
# @example "is part of"
|
127
|
+
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#relatedresource-types
|
128
|
+
attr_reader :type
|
129
|
+
|
130
|
+
# Restructure the hash so that everything is under "description" key, since
|
131
|
+
# it's all descriptive metadata. This makes CocinaRecord methods work.
|
132
|
+
def initialize(cocina_doc)
|
133
|
+
@type = cocina_doc["type"]
|
134
|
+
@cocina_doc = {"description" => cocina_doc.except("type")}
|
120
135
|
end
|
121
136
|
end
|
122
137
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative "../contributor"
|
1
|
+
require_relative "../contributors/contributor"
|
2
2
|
|
3
3
|
module CocinaDisplay
|
4
4
|
module Concerns
|
@@ -62,8 +62,8 @@ module CocinaDisplay
|
|
62
62
|
def contributor_names_by_role(with_date: false)
|
63
63
|
contributors.each_with_object({}) do |contributor, hash|
|
64
64
|
contributor.roles.each do |role|
|
65
|
-
hash[role.
|
66
|
-
hash[role.
|
65
|
+
hash[role.to_s] ||= []
|
66
|
+
hash[role.to_s] << contributor.display_name(with_date: with_date)
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -85,7 +85,7 @@ module CocinaDisplay
|
|
85
85
|
@contributors ||= Enumerator::Chain.new(
|
86
86
|
path("$.description.contributor.*"),
|
87
87
|
path("$.description.event.*.contributor.*")
|
88
|
-
).map { |c| Contributor.new(c) }
|
88
|
+
).map { |c| CocinaDisplay::Contributors::Contributor.new(c) }
|
89
89
|
end
|
90
90
|
|
91
91
|
# All contributors with a "publisher" role.
|
@@ -62,8 +62,8 @@ module CocinaDisplay
|
|
62
62
|
# @param ignore_qualified [Boolean] Reject qualified dates (e.g. approximate)
|
63
63
|
# @return [String, nil]
|
64
64
|
# @example Year range
|
65
|
-
# CocinaRecord.fetch('bb099mt5053').
|
66
|
-
def
|
65
|
+
# CocinaRecord.fetch('bb099mt5053').pub_year_str #=> "1932 - 2012"
|
66
|
+
def pub_year_str(ignore_qualified: false)
|
67
67
|
date = pub_date(ignore_qualified: ignore_qualified)
|
68
68
|
return unless date
|
69
69
|
|
@@ -72,18 +72,18 @@ module CocinaDisplay
|
|
72
72
|
|
73
73
|
# String for displaying the imprint statement(s).
|
74
74
|
# @return [String, nil]
|
75
|
-
# @see CocinaDisplay::Imprint#
|
75
|
+
# @see CocinaDisplay::Imprint#to_s
|
76
76
|
# @example
|
77
|
-
# CocinaRecord.fetch('bt553vr2845').
|
78
|
-
def
|
79
|
-
imprint_events.map(&:
|
77
|
+
# CocinaRecord.fetch('bt553vr2845').imprint_str #=> "New York : Meridian Book, 1993, c1967"
|
78
|
+
def imprint_str
|
79
|
+
imprint_events.map(&:to_s).compact_blank.join("; ")
|
80
80
|
end
|
81
81
|
|
82
82
|
# List of places of publication as strings.
|
83
83
|
# Considers locations for all publication, creation, and capture events.
|
84
84
|
# @return [Array<String>]
|
85
85
|
def publication_places
|
86
|
-
publication_events.flat_map { |event| event.locations.map(&:
|
86
|
+
publication_events.flat_map { |event| event.locations.map(&:to_s) }
|
87
87
|
end
|
88
88
|
|
89
89
|
# All events associated with the object.
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../subjects/subject_value"
|
4
|
+
|
5
|
+
module CocinaDisplay
|
6
|
+
module Concerns
|
7
|
+
# Methods for extracting geospatial metadata, such as coordinates.
|
8
|
+
module Geospatial
|
9
|
+
# All coordinate data as DMS format strings.
|
10
|
+
# @return [Array<String>]
|
11
|
+
# @example ["34°03′08″N 118°14′37″W"]
|
12
|
+
def coordinates
|
13
|
+
coordinate_subjects.map(&:to_s).compact.uniq
|
14
|
+
end
|
15
|
+
|
16
|
+
# All valid coordinate data formatted for indexing into a Solr RPT field.
|
17
|
+
# @note This type of field accommodates both points and bounding boxes.
|
18
|
+
# @see https://solr.apache.org/guide/solr/latest/query-guide/spatial-search.html#rpt
|
19
|
+
# @return [Array<String>]
|
20
|
+
# @example ["POINT(34.0522 -118.2437)", "POLYGON((-118.2437 34.0522, -118.2437 34.1996, -117.9522 34.1996, -117.9522 34.0522, -118.2437 34.0522))"]
|
21
|
+
def coordinates_as_wkt
|
22
|
+
coordinate_objects.map(&:as_wkt).uniq
|
23
|
+
end
|
24
|
+
|
25
|
+
# All valid coordinate data formatted for indexing into a Solr BBoxField.
|
26
|
+
# @note Points are not included since they can't be represented as a box.
|
27
|
+
# @see https://solr.apache.org/guide/solr/latest/query-guide/spatial-search.html#bboxfield
|
28
|
+
# @return [Array<String>]
|
29
|
+
# @example ["ENVELOPE(-118.2437, -117.9522, 34.1996, 34.0522)"]
|
30
|
+
def coordinates_as_envelope
|
31
|
+
coordinate_objects.map(&:as_envelope).compact.uniq
|
32
|
+
end
|
33
|
+
|
34
|
+
# All valid coordinate data formatted for indexing into a Solr LatLon field.
|
35
|
+
# @note Bounding boxes are automatically converted to their center point.
|
36
|
+
# @see https://solr.apache.org/guide/solr/latest/query-guide/spatial-search.html#indexing-points
|
37
|
+
# @return [Array<String>]
|
38
|
+
# @example ["34.0522,-118.2437"]
|
39
|
+
def coordinates_as_point
|
40
|
+
coordinate_objects.map(&:as_point).uniq
|
41
|
+
end
|
42
|
+
|
43
|
+
# Identifiers assigned by geonames.org for places related to the object.
|
44
|
+
# @return [Array<String>]
|
45
|
+
# @example ["6252001", "5368361"]
|
46
|
+
def geonames_ids
|
47
|
+
place_subject_values.map { |s| s.geonames_id }.compact.uniq
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Parsed coordinate values from the coordinate subject values.
|
53
|
+
# @return [Array<Geospatial::Coordinates>]
|
54
|
+
def coordinate_objects
|
55
|
+
coordinate_subjects.filter_map(&:coordinates)
|
56
|
+
end
|
57
|
+
|
58
|
+
# All subject values that could contain parsed coordinates.
|
59
|
+
# @return [Array<Subjects::CoordinatesSubjectValue>]
|
60
|
+
def coordinate_subjects
|
61
|
+
subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::CoordinatesSubjectValue }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -13,7 +13,7 @@ module CocinaDisplay
|
|
13
13
|
# Names of languages associated with the object, if recognized by Searchworks.
|
14
14
|
# @return [Array<String>]
|
15
15
|
def searchworks_language_names
|
16
|
-
languages.filter_map { |lang| lang.
|
16
|
+
languages.filter_map { |lang| lang.to_s if lang.searchworks_language? }.compact_blank.uniq
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "active_support/number_helper/number_to_human_size_converter"
|
2
|
+
|
3
|
+
module CocinaDisplay
|
4
|
+
module Concerns
|
5
|
+
# Methods for inspecting structural metadata (e.g. file hierarchy)
|
6
|
+
module Structural
|
7
|
+
# Structured data for all individual files in the object.
|
8
|
+
# Traverses nested FileSet structure to return a flattened array.
|
9
|
+
# @return [Array<Hash>]
|
10
|
+
# @example
|
11
|
+
# record.files.each do |file|
|
12
|
+
# puts file["filename"] #=> "image1.jpg"
|
13
|
+
# puts file["size"] #=> 123456
|
14
|
+
# end
|
15
|
+
def files
|
16
|
+
@files ||= path("$.structural.contains.*.structural.contains.*").search
|
17
|
+
end
|
18
|
+
|
19
|
+
# All unique MIME types of files in this object.
|
20
|
+
# @return [Array<String>]
|
21
|
+
# @example ["image/jpeg", "application/pdf"]
|
22
|
+
def file_mime_types
|
23
|
+
files.pluck("hasMimeType").uniq
|
24
|
+
end
|
25
|
+
|
26
|
+
# Human-readable string representation of {total_file_size_int}.
|
27
|
+
# @return [String]
|
28
|
+
# @example "2.5 MB"
|
29
|
+
def total_file_size_str
|
30
|
+
ActiveSupport::NumberHelper.number_to_human_size(total_file_size_int)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Summed size of all files in bytes.
|
34
|
+
# @return [Integer]
|
35
|
+
# @example 2621440
|
36
|
+
def total_file_size_int
|
37
|
+
files.pluck("size").sum
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -8,31 +8,37 @@ module CocinaDisplay
|
|
8
8
|
# All unique subject values that are topics.
|
9
9
|
# @return [Array<String>]
|
10
10
|
def subject_topics
|
11
|
-
subject_values.filter { |s| s.type == "topic" }.map(&:
|
11
|
+
subject_values.filter { |s| s.type == "topic" }.map(&:to_s).uniq
|
12
12
|
end
|
13
13
|
|
14
14
|
# All unique subject values that are genres.
|
15
15
|
# @return [Array<String>]
|
16
16
|
def subject_genres
|
17
|
-
subject_values.filter { |s| s.type == "genre" }.map(&:
|
17
|
+
subject_values.filter { |s| s.type == "genre" }.map(&:to_s).uniq
|
18
18
|
end
|
19
19
|
|
20
20
|
# All unique subject values that are titles.
|
21
21
|
# @return [Array<String>]
|
22
22
|
def subject_titles
|
23
|
-
subject_values.filter { |s| s.type == "title" }.map(&:
|
23
|
+
subject_values.filter { |s| s.type == "title" }.map(&:to_s).uniq
|
24
24
|
end
|
25
25
|
|
26
26
|
# All unique subject values that are date/time info.
|
27
27
|
# @return [Array<String>]
|
28
28
|
def subject_temporal
|
29
|
-
subject_values.filter { |s| s.type == "time" }.map(&:
|
29
|
+
subject_values.filter { |s| s.type == "time" }.map(&:to_s).uniq
|
30
30
|
end
|
31
31
|
|
32
32
|
# All unique subject values that are occupations.
|
33
33
|
# @return [Array<String>]
|
34
34
|
def subject_occupations
|
35
|
-
subject_values.filter { |s| s.type == "occupation" }.map(&:
|
35
|
+
subject_values.filter { |s| s.type == "occupation" }.map(&:to_s).uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
# All unique subject values that are named geographic places.
|
39
|
+
# @return [Array<String>]
|
40
|
+
def subject_places
|
41
|
+
place_subject_values.map(&:to_s).uniq
|
36
42
|
end
|
37
43
|
|
38
44
|
# All unique subject values that are names of entities.
|
@@ -40,15 +46,16 @@ module CocinaDisplay
|
|
40
46
|
# @see CocinaDisplay::NameSubjectValue
|
41
47
|
# @return [Array<String>]
|
42
48
|
def subject_names
|
43
|
-
subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::NameSubjectValue }.map(&:
|
49
|
+
subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::NameSubjectValue }.map(&:to_s).uniq
|
44
50
|
end
|
45
51
|
|
46
52
|
# Combination of all subject values for searching.
|
47
53
|
# @see #subject_topics_other
|
48
54
|
# @see #subject_temporal_genre
|
55
|
+
# @see #subject_places
|
49
56
|
# @return [Array<String>]
|
50
57
|
def subject_all
|
51
|
-
subject_topics_other + subject_temporal_genre
|
58
|
+
subject_topics_other + subject_temporal_genre + subject_places
|
52
59
|
end
|
53
60
|
|
54
61
|
# Combination of topic, occupation, name, and title subject values for searching.
|
@@ -77,10 +84,10 @@ module CocinaDisplay
|
|
77
84
|
end
|
78
85
|
|
79
86
|
# Combination of all subjects with nested values concatenated for display.
|
80
|
-
# @see Subject#
|
87
|
+
# @see Subject#to_s
|
81
88
|
# @return [Array<String>]
|
82
89
|
def subject_all_display
|
83
|
-
subjects.map(&:
|
90
|
+
subjects.map(&:to_s).uniq
|
84
91
|
end
|
85
92
|
|
86
93
|
private
|
@@ -100,6 +107,12 @@ module CocinaDisplay
|
|
100
107
|
def subject_values
|
101
108
|
@subject_values ||= subjects.flat_map(&:subject_values)
|
102
109
|
end
|
110
|
+
|
111
|
+
# All subject values that are named places.
|
112
|
+
# @return [Array<PlaceSubjectValue>]
|
113
|
+
def place_subject_values
|
114
|
+
@place_subject_values ||= subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::PlaceSubjectValue }
|
115
|
+
end
|
103
116
|
end
|
104
117
|
end
|
105
118
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
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
|
+
module CocinaDisplay
|
12
|
+
module Contributors
|
13
|
+
# A contributor to a work, such as an author or publisher.
|
14
|
+
class Contributor
|
15
|
+
attr_reader :cocina
|
16
|
+
|
17
|
+
# Initialize a Contributor object with Cocina structured data.
|
18
|
+
# @param cocina [Hash] The Cocina structured data for the contributor.
|
19
|
+
def initialize(cocina)
|
20
|
+
@cocina = cocina
|
21
|
+
end
|
22
|
+
|
23
|
+
# String representation of the contributor, including name and role.
|
24
|
+
# Used for debugging and logging.
|
25
|
+
# @return [String]
|
26
|
+
def to_s
|
27
|
+
Utils.compact_and_join([display_name, display_role], delimiter: ": ")
|
28
|
+
end
|
29
|
+
|
30
|
+
# Is this contributor a human?
|
31
|
+
# @return [Boolean]
|
32
|
+
def person?
|
33
|
+
cocina["type"] == "person"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Is this contributor an organization?
|
37
|
+
# @return [Boolean]
|
38
|
+
def organization?
|
39
|
+
cocina["type"] == "organization"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Is this contributor a conference?
|
43
|
+
# @return [Boolean]
|
44
|
+
def conference?
|
45
|
+
cocina["type"] == "conference"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Is this contributor marked as primary?
|
49
|
+
# @return [Boolean]
|
50
|
+
def primary?
|
51
|
+
cocina["status"] == "primary"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Does this contributor have a role that indicates they are an author?
|
55
|
+
# @return [Boolean]
|
56
|
+
def author?
|
57
|
+
roles.any?(&:author?)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Does this contributor have a role that indicates they are a publisher?
|
61
|
+
# @return [Boolean]
|
62
|
+
def publisher?
|
63
|
+
roles.any?(&:publisher?)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Does this contributor have a role that indicates they are a funder?
|
67
|
+
# @return [Boolean]
|
68
|
+
def funder?
|
69
|
+
roles.any?(&:funder?)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Does this contributor have any roles defined?
|
73
|
+
# @return [Boolean]
|
74
|
+
def role?
|
75
|
+
roles.any?
|
76
|
+
end
|
77
|
+
|
78
|
+
# The display name for the contributor as a string.
|
79
|
+
# Uses the first name if multiple names are present.
|
80
|
+
# @param with_date [Boolean] Include life dates, if present
|
81
|
+
# @return [String]
|
82
|
+
def display_name(with_date: false)
|
83
|
+
names.map { |name| name.to_s(with_date: with_date) }.first
|
84
|
+
end
|
85
|
+
|
86
|
+
# The full forename for the contributor from the first available name.
|
87
|
+
# @see Contributor::Name::forename_str
|
88
|
+
# @return [String, nil]
|
89
|
+
def forename
|
90
|
+
names.map(&:forename_str).first.presence
|
91
|
+
end
|
92
|
+
|
93
|
+
# The full surname for the contributor from the first available name.
|
94
|
+
# @see Contributor::Name::surname_str
|
95
|
+
# @return [String, nil]
|
96
|
+
def surname
|
97
|
+
names.map(&:surname_str).first.presence
|
98
|
+
end
|
99
|
+
|
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
|
+
# All names in the Cocina as Name objects.
|
108
|
+
# @return [Array<Name>]
|
109
|
+
def names
|
110
|
+
@names ||= Array(cocina["name"]).map { |name| Name.new(name) }
|
111
|
+
end
|
112
|
+
|
113
|
+
# All roles in the Cocina structured data.
|
114
|
+
# @return [Array<Hash>]
|
115
|
+
def roles
|
116
|
+
@roles ||= Array(cocina["role"]).map { |role| Role.new(role) }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../utils"
|
4
|
+
|
5
|
+
module CocinaDisplay
|
6
|
+
module Contributors
|
7
|
+
# A name associated with a contributor.
|
8
|
+
class Name
|
9
|
+
attr_reader :cocina
|
10
|
+
|
11
|
+
# Initialize a Name object with Cocina structured data.
|
12
|
+
# @param cocina [Hash] The Cocina structured data for the name.
|
13
|
+
def initialize(cocina)
|
14
|
+
@cocina = cocina
|
15
|
+
end
|
16
|
+
|
17
|
+
# The display string for the name, optionally including life dates.
|
18
|
+
# Uses these values in order, if present:
|
19
|
+
# 1. Unstructured value
|
20
|
+
# 2. Any structured/parallel values marked as "display"
|
21
|
+
# 3. Joined structured values, optionally with life dates
|
22
|
+
# @param with_date [Boolean] Include life dates, if present
|
23
|
+
# @return [String]
|
24
|
+
# @example no dates
|
25
|
+
# name.to_s # => "King, Martin Luther, Jr."
|
26
|
+
# @example with dates
|
27
|
+
# name.to_s(with_date: true) # => "King, Martin Luther, Jr., 1929-1968"
|
28
|
+
def to_s(with_date: false)
|
29
|
+
if cocina["value"].present?
|
30
|
+
cocina["value"]
|
31
|
+
elsif display_name_str.present?
|
32
|
+
display_name_str
|
33
|
+
elsif dates_str.present? && with_date
|
34
|
+
Utils.compact_and_join([full_name_str, dates_str], delimiter: ", ")
|
35
|
+
else
|
36
|
+
full_name_str
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# The full name as a string, combining all name components and terms of address.
|
41
|
+
# @return [String]
|
42
|
+
def full_name_str
|
43
|
+
Utils.compact_and_join(name_components.push(terms_of_address_str), delimiter: ", ")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Flattened form of any names explicitly marked as "display name".
|
47
|
+
# @return [String]
|
48
|
+
def display_name_str
|
49
|
+
Utils.compact_and_join(Array(name_values["display"]), delimiter: ", ")
|
50
|
+
end
|
51
|
+
|
52
|
+
# List of all name components.
|
53
|
+
# If any of forename, surname, or term of address are present, those are used.
|
54
|
+
# Otherwise, fall back to any names explicitly marked as "name" or untyped.
|
55
|
+
# @return [Array<String>]
|
56
|
+
def name_components
|
57
|
+
[surname_str, forename_str].compact_blank.presence || Array(name_values["name"])
|
58
|
+
end
|
59
|
+
|
60
|
+
# Flatten all terms of address into a comma-delimited string.
|
61
|
+
# @return [String]
|
62
|
+
def terms_of_address_str
|
63
|
+
Utils.compact_and_join(Array(name_values["term of address"]), delimiter: ", ")
|
64
|
+
end
|
65
|
+
|
66
|
+
# Flatten all forename values and ordinals into a whitespace-delimited string.
|
67
|
+
# @return [String]
|
68
|
+
def forename_str
|
69
|
+
Utils.compact_and_join(Array(name_values["forename"]) + Array(name_values["ordinal"]), delimiter: " ")
|
70
|
+
end
|
71
|
+
|
72
|
+
# Flatten all surname values into a whitespace-delimited string.
|
73
|
+
# @return [String]
|
74
|
+
def surname_str
|
75
|
+
Utils.compact_and_join(Array(name_values["surname"]), delimiter: " ")
|
76
|
+
end
|
77
|
+
|
78
|
+
# Flatten all life and activity dates into a comma-delimited string.
|
79
|
+
# @return [String]
|
80
|
+
def dates_str
|
81
|
+
Utils.compact_and_join(Array(name_values["life dates"]) + Array(name_values["activity dates"]), delimiter: ", ")
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# A hash mapping destructured name types to their values.
|
87
|
+
# Name values with no type are grouped under "name".
|
88
|
+
# @return [Hash<String, Array<String>>]
|
89
|
+
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#contributor-name-part-types-for-structured-value
|
90
|
+
# @note Currently we do nothing with "alternative", "inverted full name", "pseudonym", and "transliteration" types.
|
91
|
+
def name_values
|
92
|
+
Utils.flatten_nested_values(cocina).each_with_object({}) do |node, hash|
|
93
|
+
type = node["type"] || "name"
|
94
|
+
hash[type] ||= []
|
95
|
+
hash[type] << node["value"]
|
96
|
+
end.compact_blank
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../vocabularies/marc_relator_codes"
|
4
|
+
|
5
|
+
module CocinaDisplay
|
6
|
+
module Contributors
|
7
|
+
# A role associated with a contributor.
|
8
|
+
class Role
|
9
|
+
attr_reader :cocina
|
10
|
+
|
11
|
+
# Initialize a Role object with Cocina structured data.
|
12
|
+
# @param cocina [Hash] The Cocina structured data for the role.
|
13
|
+
def initialize(cocina)
|
14
|
+
@cocina = cocina
|
15
|
+
end
|
16
|
+
|
17
|
+
# The name of the role.
|
18
|
+
# Translates the MARC relator code if no value was present.
|
19
|
+
# @return [String, nil]
|
20
|
+
def to_s
|
21
|
+
cocina["value"] || (Vocabularies::MARC_RELATOR[code] if marc_relator?)
|
22
|
+
end
|
23
|
+
|
24
|
+
# A code associated with the role, e.g. a MARC relator code.
|
25
|
+
# @return [String, nil]
|
26
|
+
def code
|
27
|
+
cocina["code"]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Does this role indicate the contributor is an author?
|
31
|
+
# @return [Boolean]
|
32
|
+
def author?
|
33
|
+
to_s =~ /^(author|creator|primary investigator)/i
|
34
|
+
end
|
35
|
+
|
36
|
+
# Does this role indicate the contributor is a publisher?
|
37
|
+
# @return [Boolean]
|
38
|
+
def publisher?
|
39
|
+
to_s =~ /^publisher/i
|
40
|
+
end
|
41
|
+
|
42
|
+
# Does this role indicate the contributor is a funder?
|
43
|
+
# @return [Boolean]
|
44
|
+
def funder?
|
45
|
+
to_s =~ /^funder/i
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Does this role have a MARC relator code?
|
51
|
+
# @return [Boolean]
|
52
|
+
def marc_relator?
|
53
|
+
cocina.dig("source", "code") == "marcrelator"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative "location"
|
2
2
|
require_relative "../dates/date"
|
3
|
-
require_relative "../contributor"
|
3
|
+
require_relative "../contributors/contributor"
|
4
4
|
|
5
5
|
module CocinaDisplay
|
6
6
|
module Events
|
@@ -62,7 +62,7 @@ module CocinaDisplay
|
|
62
62
|
# @return [Array<CocinaDisplay::Contributor>]
|
63
63
|
def contributors
|
64
64
|
@contributors ||= Array(cocina["contributor"]).map do |contributor|
|
65
|
-
CocinaDisplay::Contributor.new(contributor)
|
65
|
+
CocinaDisplay::Contributors::Contributor.new(contributor)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
@@ -16,7 +16,7 @@ module CocinaDisplay
|
|
16
16
|
class Imprint < Event
|
17
17
|
# The entire imprint statement formatted as a string for display.
|
18
18
|
# @return [String]
|
19
|
-
def
|
19
|
+
def to_s
|
20
20
|
place_pub = Utils.compact_and_join([place_str, publisher_str], delimiter: " : ")
|
21
21
|
edition_place_pub = Utils.compact_and_join([edition_str, place_pub], delimiter: " - ")
|
22
22
|
Utils.compact_and_join([edition_place_pub, date_str], delimiter: ", ")
|
@@ -93,7 +93,7 @@ module CocinaDisplay
|
|
93
93
|
def locations_for_display
|
94
94
|
unencoded_locs, encoded_locs = locations.partition { |loc| loc.unencoded_value? }
|
95
95
|
locs_for_display = unencoded_locs.presence || encoded_locs
|
96
|
-
locs_for_display.map(&:
|
96
|
+
locs_for_display.map(&:to_s).compact_blank.uniq
|
97
97
|
end
|
98
98
|
end
|
99
99
|
end
|