cocina_display 0.6.0 → 1.0.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 +4 -0
- data/lib/cocina_display/concerns/contributors.rb +8 -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 +20 -0
- data/lib/cocina_display/concerns/subjects.rb +43 -16
- data/lib/cocina_display/contributors/contributor.rb +100 -0
- data/lib/cocina_display/contributors/name.rb +100 -0
- data/lib/cocina_display/contributors/role.rb +51 -0
- data/lib/cocina_display/dates/date_range.rb +21 -9
- data/lib/cocina_display/events/event.rb +2 -2
- data/lib/cocina_display/events/imprint.rb +2 -3
- data/lib/cocina_display/events/location.rb +3 -3
- data/lib/cocina_display/geospatial.rb +348 -0
- data/lib/cocina_display/language.rb +47 -0
- data/lib/cocina_display/subjects/subject.rb +49 -0
- data/lib/cocina_display/subjects/subject_value.rb +159 -0
- data/lib/cocina_display/utils.rb +12 -5
- data/lib/cocina_display/version.rb +1 -1
- data/lib/cocina_display/vocabularies/marc_country_codes.rb +393 -0
- data/lib/cocina_display/vocabularies/marc_relator_codes.rb +318 -0
- data/lib/cocina_display/vocabularies/searchworks_languages.rb +526 -0
- data/script/deep_compact.rb +13 -0
- metadata +43 -6
- data/lib/cocina_display/contributor.rb +0 -234
- data/lib/cocina_display/marc_country_codes.rb +0 -394
- data/lib/cocina_display/marc_relator_codes.rb +0 -314
- data/lib/cocina_display/subject.rb +0 -127
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68f245cc22043da571e4a9abf0f0fedd613fc19035c4a50369b95400c76573b5
|
4
|
+
data.tar.gz: 86de10bbfcabc44d23d9b3df480eb6b3f0d28b358c9f4fece1ece089cf5cca20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ea9ac44480ac83533704c06705f0f6f078ac4065b27756cea527f4110ab99b3bafcfcf525097368005e89534bed78a8bb17a81cb96dc1c48c479a5052b877f6
|
7
|
+
data.tar.gz: 3600cc57ec5e162cb39e2547fa09c9dc7aadc87742146649a0c9d3427ad6c8a2eede1357103af6d4cab531e51d93136a69f075de0995c86daaf471fac2ff08c7
|
@@ -14,6 +14,8 @@ require_relative "concerns/titles"
|
|
14
14
|
require_relative "concerns/access"
|
15
15
|
require_relative "concerns/subjects"
|
16
16
|
require_relative "concerns/forms"
|
17
|
+
require_relative "concerns/languages"
|
18
|
+
require_relative "concerns/geospatial"
|
17
19
|
require_relative "utils"
|
18
20
|
|
19
21
|
module CocinaDisplay
|
@@ -26,6 +28,8 @@ module CocinaDisplay
|
|
26
28
|
include CocinaDisplay::Concerns::Access
|
27
29
|
include CocinaDisplay::Concerns::Subjects
|
28
30
|
include CocinaDisplay::Concerns::Forms
|
31
|
+
include CocinaDisplay::Concerns::Languages
|
32
|
+
include CocinaDisplay::Concerns::Geospatial
|
29
33
|
|
30
34
|
# Fetch a public Cocina document from PURL and create a CocinaRecord.
|
31
35
|
# @note This is intended to be used in development or testing only.
|
@@ -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
|
@@ -79,9 +79,13 @@ module CocinaDisplay
|
|
79
79
|
end
|
80
80
|
|
81
81
|
# All contributors for the object, including authors, editors, etc.
|
82
|
+
# Checks both description.contributor and description.event.contributor.
|
82
83
|
# @return [Array<Contributor>]
|
83
84
|
def contributors
|
84
|
-
@contributors ||=
|
85
|
+
@contributors ||= Enumerator::Chain.new(
|
86
|
+
path("$.description.contributor.*"),
|
87
|
+
path("$.description.event.*.contributor.*")
|
88
|
+
).map { |c| CocinaDisplay::Contributors::Contributor.new(c) }
|
85
89
|
end
|
86
90
|
|
87
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
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "../language"
|
2
|
+
|
3
|
+
module CocinaDisplay
|
4
|
+
module Concerns
|
5
|
+
# Methods for extracting language information from a Cocina object.
|
6
|
+
module Languages
|
7
|
+
# Languages objects associated with the object.
|
8
|
+
# @return [Array<CocinaDisplay::Language>]
|
9
|
+
def languages
|
10
|
+
@languages ||= path("$.description.language.*").map { |lang| CocinaDisplay::Language.new(lang) }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Names of languages associated with the object, if recognized by Searchworks.
|
14
|
+
# @return [Array<String>]
|
15
|
+
def searchworks_language_names
|
16
|
+
languages.filter_map { |lang| lang.to_s if lang.searchworks_language? }.compact_blank.uniq
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,53 +1,61 @@
|
|
1
|
-
require_relative "../subject"
|
1
|
+
require_relative "../subjects/subject"
|
2
|
+
require_relative "../subjects/subject_value"
|
2
3
|
|
3
4
|
module CocinaDisplay
|
4
5
|
module Concerns
|
5
6
|
# Methods for extracting and formatting subject information.
|
6
7
|
module Subjects
|
7
|
-
# All unique
|
8
|
+
# All unique subject values that are topics.
|
8
9
|
# @return [Array<String>]
|
9
10
|
def subject_topics
|
10
|
-
|
11
|
+
subject_values.filter { |s| s.type == "topic" }.map(&:to_s).uniq
|
11
12
|
end
|
12
13
|
|
13
|
-
# All unique
|
14
|
+
# All unique subject values that are genres.
|
14
15
|
# @return [Array<String>]
|
15
16
|
def subject_genres
|
16
|
-
|
17
|
+
subject_values.filter { |s| s.type == "genre" }.map(&:to_s).uniq
|
17
18
|
end
|
18
19
|
|
19
|
-
# All unique
|
20
|
+
# All unique subject values that are titles.
|
20
21
|
# @return [Array<String>]
|
21
22
|
def subject_titles
|
22
|
-
|
23
|
+
subject_values.filter { |s| s.type == "title" }.map(&:to_s).uniq
|
23
24
|
end
|
24
25
|
|
25
|
-
# All unique
|
26
|
+
# All unique subject values that are date/time info.
|
26
27
|
# @return [Array<String>]
|
27
28
|
def subject_temporal
|
28
|
-
|
29
|
+
subject_values.filter { |s| s.type == "time" }.map(&:to_s).uniq
|
29
30
|
end
|
30
31
|
|
31
|
-
# All unique
|
32
|
+
# All unique subject values that are occupations.
|
32
33
|
# @return [Array<String>]
|
33
34
|
def subject_occupations
|
34
|
-
|
35
|
+
subject_values.filter { |s| s.type == "occupation" }.map(&:to_s).uniq
|
35
36
|
end
|
36
37
|
|
37
|
-
# All unique
|
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
|
42
|
+
end
|
43
|
+
|
44
|
+
# All unique subject values that are names of entities.
|
38
45
|
# @note Multiple types are handled: person, family, organization, conference, etc.
|
39
|
-
# @see CocinaDisplay::
|
46
|
+
# @see CocinaDisplay::NameSubjectValue
|
40
47
|
# @return [Array<String>]
|
41
48
|
def subject_names
|
42
|
-
|
49
|
+
subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::NameSubjectValue }.map(&:to_s).uniq
|
43
50
|
end
|
44
51
|
|
45
52
|
# Combination of all subject values for searching.
|
46
53
|
# @see #subject_topics_other
|
47
54
|
# @see #subject_temporal_genre
|
55
|
+
# @see #subject_places
|
48
56
|
# @return [Array<String>]
|
49
57
|
def subject_all
|
50
|
-
subject_topics_other + subject_temporal_genre
|
58
|
+
subject_topics_other + subject_temporal_genre + subject_places
|
51
59
|
end
|
52
60
|
|
53
61
|
# Combination of topic, occupation, name, and title subject values for searching.
|
@@ -75,6 +83,13 @@ module CocinaDisplay
|
|
75
83
|
subject_temporal + subject_genres
|
76
84
|
end
|
77
85
|
|
86
|
+
# Combination of all subjects with nested values concatenated for display.
|
87
|
+
# @see Subject#to_s
|
88
|
+
# @return [Array<String>]
|
89
|
+
def subject_all_display
|
90
|
+
subjects.map(&:to_s).uniq
|
91
|
+
end
|
92
|
+
|
78
93
|
private
|
79
94
|
|
80
95
|
# All subjects, accessible as Subject objects.
|
@@ -84,7 +99,19 @@ module CocinaDisplay
|
|
84
99
|
@subjects ||= Enumerator::Chain.new(
|
85
100
|
path("$.description.subject[*]"),
|
86
101
|
path("$.description.geographic.*.subject[*]")
|
87
|
-
).map { |s| Subject.
|
102
|
+
).map { |s| CocinaDisplay::Subjects::Subject.new(s) }
|
103
|
+
end
|
104
|
+
|
105
|
+
# All subject values, flattened from all subjects.
|
106
|
+
# @return [Array<SubjectValue>]
|
107
|
+
def subject_values
|
108
|
+
@subject_values ||= subjects.flat_map(&:subject_values)
|
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 }
|
88
115
|
end
|
89
116
|
end
|
90
117
|
end
|
@@ -0,0 +1,100 @@
|
|
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 any roles defined?
|
67
|
+
# @return [Boolean]
|
68
|
+
def role?
|
69
|
+
roles.any?
|
70
|
+
end
|
71
|
+
|
72
|
+
# The display name for the contributor as a string.
|
73
|
+
# Uses the first name if multiple names are present.
|
74
|
+
# @param with_date [Boolean] Include life dates, if present
|
75
|
+
# @return [String]
|
76
|
+
def display_name(with_date: false)
|
77
|
+
names.map { |name| name.to_s(with_date: with_date) }.first
|
78
|
+
end
|
79
|
+
|
80
|
+
# A string representation of the contributor's roles, formatted for display.
|
81
|
+
# If there are multiple roles, they are joined with commas.
|
82
|
+
# @return [String]
|
83
|
+
def display_role
|
84
|
+
roles.map(&:to_s).to_sentence
|
85
|
+
end
|
86
|
+
|
87
|
+
# All names in the Cocina as Name objects.
|
88
|
+
# @return [Array<Name>]
|
89
|
+
def names
|
90
|
+
@names ||= Array(cocina["name"]).map { |name| Name.new(name) }
|
91
|
+
end
|
92
|
+
|
93
|
+
# All roles in the Cocina structured data.
|
94
|
+
# @return [Array<Hash>]
|
95
|
+
def roles
|
96
|
+
@roles ||= Array(cocina["role"]).map { |role| Role.new(role) }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
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
|
+
private
|
41
|
+
|
42
|
+
# The full name as a string, combining all name components and terms of address.
|
43
|
+
# @return [String]
|
44
|
+
def full_name_str
|
45
|
+
Utils.compact_and_join(name_components.push(terms_of_address_str), delimiter: ", ")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Flattened form of any names explicitly marked as "display name".
|
49
|
+
# @return [String]
|
50
|
+
def display_name_str
|
51
|
+
Utils.compact_and_join(Array(name_values["display"]), delimiter: ", ")
|
52
|
+
end
|
53
|
+
|
54
|
+
# List of all name components.
|
55
|
+
# If any of forename, surname, or term of address are present, those are used.
|
56
|
+
# Otherwise, fall back to any names explicitly marked as "name" or untyped.
|
57
|
+
# @return [Array<String>]
|
58
|
+
def name_components
|
59
|
+
[surname_str, forename_ordinal_str].compact_blank.presence || Array(name_values["name"])
|
60
|
+
end
|
61
|
+
|
62
|
+
# Flatten all forenames and ordinals into a single string.
|
63
|
+
# @return [String]
|
64
|
+
def forename_ordinal_str
|
65
|
+
Utils.compact_and_join(Array(name_values["forename"]) + Array(name_values["ordinal"]), delimiter: " ")
|
66
|
+
end
|
67
|
+
|
68
|
+
# Flatten all terms of address into a single string.
|
69
|
+
# @return [String]
|
70
|
+
def terms_of_address_str
|
71
|
+
Utils.compact_and_join(Array(name_values["term of address"]), delimiter: ", ")
|
72
|
+
end
|
73
|
+
|
74
|
+
# Flatten all surnames into a single string.
|
75
|
+
# @return [String]
|
76
|
+
def surname_str
|
77
|
+
Utils.compact_and_join(Array(name_values["surname"]), delimiter: " ")
|
78
|
+
end
|
79
|
+
|
80
|
+
# Flatten all life and activity dates into a single string.
|
81
|
+
# @return [String]
|
82
|
+
def dates_str
|
83
|
+
Utils.compact_and_join(Array(name_values["life dates"]) + Array(name_values["activity dates"]), delimiter: ", ")
|
84
|
+
end
|
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,51 @@
|
|
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)/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
|
+
private
|
43
|
+
|
44
|
+
# Does this role have a MARC relator code?
|
45
|
+
# @return [Boolean]
|
46
|
+
def marc_relator?
|
47
|
+
cocina.dig("source", "code") == "marcrelator"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -63,7 +63,18 @@ module CocinaDisplay
|
|
63
63
|
start&.encoding || stop&.encoding || super
|
64
64
|
end
|
65
65
|
|
66
|
-
#
|
66
|
+
# The qualifier for the entire range.
|
67
|
+
# If both qualifiers match, uses that qualifier. If both are empty, falls
|
68
|
+
# back to the top level qualifier, if any.
|
69
|
+
# @see CocinaDisplay::Date#qualifier
|
70
|
+
# @return [String, nil]
|
71
|
+
def qualifier
|
72
|
+
if start&.qualifier == stop&.qualifier
|
73
|
+
start&.qualifier || stop&.qualifier || super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Is either date in the range, or the range itself, qualified?
|
67
78
|
# @see CocinaDisplay::Date#qualified?
|
68
79
|
# @return [Boolean]
|
69
80
|
def qualified?
|
@@ -105,14 +116,15 @@ module CocinaDisplay
|
|
105
116
|
# @see CocinaDisplay::Date#qualified_value
|
106
117
|
# @return [String]
|
107
118
|
def qualified_value
|
108
|
-
if
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
119
|
+
if qualifier
|
120
|
+
case qualifier
|
121
|
+
when "approximate"
|
122
|
+
"[ca. #{decoded_value}]"
|
123
|
+
when "questionable"
|
124
|
+
"[#{decoded_value}?]"
|
125
|
+
when "inferred"
|
126
|
+
"[#{decoded_value}]"
|
127
|
+
end
|
116
128
|
else
|
117
129
|
"#{start&.qualified_value} - #{stop&.qualified_value}"
|
118
130
|
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
|
|
@@ -7,7 +7,6 @@ require "active_support/core_ext/object/blank"
|
|
7
7
|
|
8
8
|
require_relative "event"
|
9
9
|
require_relative "../utils"
|
10
|
-
require_relative "../marc_country_codes"
|
11
10
|
require_relative "../dates/date"
|
12
11
|
require_relative "../dates/date_range"
|
13
12
|
|
@@ -17,7 +16,7 @@ module CocinaDisplay
|
|
17
16
|
class Imprint < Event
|
18
17
|
# The entire imprint statement formatted as a string for display.
|
19
18
|
# @return [String]
|
20
|
-
def
|
19
|
+
def to_s
|
21
20
|
place_pub = Utils.compact_and_join([place_str, publisher_str], delimiter: " : ")
|
22
21
|
edition_place_pub = Utils.compact_and_join([edition_str, place_pub], delimiter: " - ")
|
23
22
|
Utils.compact_and_join([edition_place_pub, date_str], delimiter: ", ")
|
@@ -94,7 +93,7 @@ module CocinaDisplay
|
|
94
93
|
def locations_for_display
|
95
94
|
unencoded_locs, encoded_locs = locations.partition { |loc| loc.unencoded_value? }
|
96
95
|
locs_for_display = unencoded_locs.presence || encoded_locs
|
97
|
-
locs_for_display.map(&:
|
96
|
+
locs_for_display.map(&:to_s).compact_blank.uniq
|
98
97
|
end
|
99
98
|
end
|
100
99
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative "../marc_country_codes"
|
1
|
+
require_relative "../vocabularies/marc_country_codes"
|
2
2
|
|
3
3
|
module CocinaDisplay
|
4
4
|
module Events
|
@@ -15,7 +15,7 @@ module CocinaDisplay
|
|
15
15
|
# The name of the location.
|
16
16
|
# Decodes a MARC country code if present and no value was present.
|
17
17
|
# @return [String, nil]
|
18
|
-
def
|
18
|
+
def to_s
|
19
19
|
cocina["value"] || decoded_country
|
20
20
|
end
|
21
21
|
|
@@ -36,7 +36,7 @@ module CocinaDisplay
|
|
36
36
|
# Decoded country name if the location is encoded with a MARC country code.
|
37
37
|
# @return [String, nil]
|
38
38
|
def decoded_country
|
39
|
-
MARC_COUNTRY[code] if marc_country? && valid_country_code?
|
39
|
+
Vocabularies::MARC_COUNTRY[code] if marc_country? && valid_country_code?
|
40
40
|
end
|
41
41
|
|
42
42
|
# Is this a decodable country code?
|