cocina_display 2.1.0 → 2.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/config/locales/en.yml +1 -0
- data/lib/cocina_display/concerns/geospatial.rb +8 -8
- data/lib/cocina_display/concerns/languages.rb +3 -3
- data/lib/cocina_display/concerns/notes.rb +1 -1
- data/lib/cocina_display/concerns/subjects.rb +28 -28
- data/lib/cocina_display/concerns/titles.rb +15 -14
- data/lib/cocina_display/concerns/url_helpers.rb +5 -2
- data/lib/cocina_display/contributors/contributor.rb +2 -12
- data/lib/cocina_display/contributors/name.rb +8 -93
- data/lib/cocina_display/contributors/name_value.rb +78 -0
- data/lib/cocina_display/languages/language.rb +91 -0
- data/lib/cocina_display/languages/script.rb +24 -0
- data/lib/cocina_display/notes/note.rb +37 -0
- data/lib/cocina_display/notes/note_value.rb +123 -0
- data/lib/cocina_display/parallel/parallel.rb +127 -0
- data/lib/cocina_display/parallel/parallel_value.rb +120 -0
- data/lib/cocina_display/related_resource.rb +11 -2
- data/lib/cocina_display/subjects/subject.rb +12 -57
- data/lib/cocina_display/subjects/subject_part.rb +177 -0
- data/lib/cocina_display/subjects/subject_value.rb +29 -160
- data/lib/cocina_display/titles/title.rb +49 -0
- data/lib/cocina_display/titles/title_value.rb +181 -0
- data/lib/cocina_display/version.rb +1 -1
- data/script/deep_compact.rb +2 -0
- data/script/find_records.rb +1 -1
- metadata +13 -6
- data/lib/cocina_display/language.rb +0 -53
- data/lib/cocina_display/note.rb +0 -142
- data/lib/cocina_display/title.rb +0 -213
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3465ccfb8142f2351977032932d4e38e8ea97188bbeea980073eb145780c7465
|
|
4
|
+
data.tar.gz: 9a7c59a505bc7eaa88b3ae0cb81becd67f508eeff72d39b9db4725db4a0403e4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cc5c29815d9d5250cfc63eb5546db9eae7374d50f4308089dce8eb7cc5383193ebb125fb4fe0a07226602b3f631fc75aa4fbec78b7796794ad2c27c7c9717217
|
|
7
|
+
data.tar.gz: 7046fcda48d7004206e5b1ee3f724f591f7cc8306c9e560226fb48832ff041f1bdff0c4d28e45d38090e356a8e1375d3da1d39c2ff3a13316e4870d83c7fd0d3
|
data/config/locales/en.yml
CHANGED
|
@@ -8,7 +8,7 @@ module CocinaDisplay
|
|
|
8
8
|
# @return [Array<String>]
|
|
9
9
|
# @example ["34°03′08″N 118°14′37″W"]
|
|
10
10
|
def coordinates
|
|
11
|
-
|
|
11
|
+
coordinate_subject_parts.map(&:to_s).compact.uniq
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# All valid coordinate data formatted for indexing into a Solr RPT field.
|
|
@@ -43,7 +43,7 @@ module CocinaDisplay
|
|
|
43
43
|
# @return [Array<String>]
|
|
44
44
|
# @example ["6252001", "5368361"]
|
|
45
45
|
def geonames_ids
|
|
46
|
-
|
|
46
|
+
place_subject_parts.map { |s| s.geonames_id }.compact.uniq
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
private
|
|
@@ -54,16 +54,16 @@ module CocinaDisplay
|
|
|
54
54
|
all_subjects.filter { |subject| subject.type&.include? "coordinates" }
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
# Parsed coordinate values from the coordinate subject
|
|
57
|
+
# Parsed coordinate values from the coordinate subject parts.
|
|
58
58
|
# @return [Array<Geospatial::Coordinates>]
|
|
59
59
|
def coordinate_objects
|
|
60
|
-
|
|
60
|
+
coordinate_subject_parts.filter_map(&:coordinates)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
# All subject
|
|
64
|
-
# @return [Array<Subjects::
|
|
65
|
-
def
|
|
66
|
-
|
|
63
|
+
# All subject parts that could contain parsed coordinates.
|
|
64
|
+
# @return [Array<Subjects::CoordinatesSubjectPart>]
|
|
65
|
+
def coordinate_subject_parts
|
|
66
|
+
subject_parts.filter { |s| s.is_a? CocinaDisplay::Subjects::CoordinatesSubjectPart }
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
end
|
|
@@ -2,10 +2,10 @@ module CocinaDisplay
|
|
|
2
2
|
module Concerns
|
|
3
3
|
# Methods for extracting language information from a Cocina object.
|
|
4
4
|
module Languages
|
|
5
|
-
# Languages
|
|
6
|
-
# @return [Array<CocinaDisplay::Language>]
|
|
5
|
+
# Languages associated with the object.
|
|
6
|
+
# @return [Array<CocinaDisplay::Languages::Language>]
|
|
7
7
|
def languages
|
|
8
|
-
@languages ||= path("$.description.language.*").map { |lang| CocinaDisplay::Language.new(lang) }
|
|
8
|
+
@languages ||= path("$.description.language.*").map { |lang| CocinaDisplay::Languages::Language.new(lang) }
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
# Names of languages associated with the object, if recognized by Searchworks.
|
|
@@ -5,7 +5,7 @@ module CocinaDisplay
|
|
|
5
5
|
# Note objects associated with the cocina record.
|
|
6
6
|
# @return [Array<CocinaDisplay::Note>]
|
|
7
7
|
def notes
|
|
8
|
-
@notes ||= path("$.description.note.*").map { |note| CocinaDisplay::Note.new(note) }
|
|
8
|
+
@notes ||= path("$.description.note.*").map { |note| CocinaDisplay::Notes::Note.new(note) }
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
# Text of all abstract notes.
|
|
@@ -2,51 +2,51 @@ module CocinaDisplay
|
|
|
2
2
|
module Concerns
|
|
3
3
|
# Methods for extracting and formatting subject information.
|
|
4
4
|
module Subjects
|
|
5
|
-
# All unique subject
|
|
5
|
+
# All unique subject parts that are topics.
|
|
6
6
|
# @return [Array<String>]
|
|
7
7
|
def subject_topics
|
|
8
|
-
|
|
8
|
+
subject_parts.filter { |s| s.type == "topic" }.map(&:to_s).uniq
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# All unique subject
|
|
11
|
+
# All unique subject parts that are genres.
|
|
12
12
|
# @return [Array<String>]
|
|
13
13
|
def subject_genres
|
|
14
|
-
|
|
14
|
+
subject_parts.filter { |s| s.type == "genre" }.map(&:to_s).uniq
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
# All unique subject
|
|
17
|
+
# All unique subject parts that are titles.
|
|
18
18
|
# @return [Array<String>]
|
|
19
19
|
def subject_titles
|
|
20
|
-
|
|
20
|
+
subject_parts.filter { |s| s.type == "title" }.map(&:to_s).uniq
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# All unique subject
|
|
23
|
+
# All unique subject parts that are date/time info.
|
|
24
24
|
# @return [Array<String>]
|
|
25
25
|
def subject_temporal
|
|
26
|
-
|
|
26
|
+
subject_parts.filter { |s| s.type == "time" }.map(&:to_s).uniq
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
# All unique subject
|
|
29
|
+
# All unique subject parts that are occupations.
|
|
30
30
|
# @return [Array<String>]
|
|
31
31
|
def subject_occupations
|
|
32
|
-
|
|
32
|
+
subject_parts.filter { |s| s.type == "occupation" }.map(&:to_s).uniq
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
# All unique subject
|
|
35
|
+
# All unique subject parts that are named geographic places.
|
|
36
36
|
# @return [Array<String>]
|
|
37
37
|
def subject_places
|
|
38
|
-
|
|
38
|
+
place_subject_parts.map(&:to_s).uniq
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
# All unique subject
|
|
41
|
+
# All unique subject parts that are names of entities.
|
|
42
42
|
# @note Multiple types are handled: person, family, organization, conference, etc.
|
|
43
|
-
# @see CocinaDisplay::
|
|
43
|
+
# @see CocinaDisplay::NameSubjectPart
|
|
44
44
|
# @return [Array<String>]
|
|
45
45
|
def subject_names
|
|
46
|
-
|
|
46
|
+
subject_parts.filter { |s| s.is_a? CocinaDisplay::Subjects::NameSubjectPart }.map(&:to_s).uniq
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
# Combination of all subject
|
|
49
|
+
# Combination of all subject parts for searching.
|
|
50
50
|
# @see #subject_topics_other
|
|
51
51
|
# @see #subject_temporal_genre
|
|
52
52
|
# @see #subject_places
|
|
@@ -55,7 +55,7 @@ module CocinaDisplay
|
|
|
55
55
|
subject_topics_other + subject_temporal_genre + subject_places
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
# Combination of topic, occupation, name, and title subject
|
|
58
|
+
# Combination of topic, occupation, name, and title subject parts for searching.
|
|
59
59
|
# @see #subject_topics
|
|
60
60
|
# @see #subject_other
|
|
61
61
|
# @return [Array<String>]
|
|
@@ -63,7 +63,7 @@ module CocinaDisplay
|
|
|
63
63
|
subject_topics + subject_other
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
# Combination of occupation, name, and title subject
|
|
66
|
+
# Combination of occupation, name, and title subject parts for searching.
|
|
67
67
|
# @see #subject_occupations
|
|
68
68
|
# @see #subject_names
|
|
69
69
|
# @see #subject_titles
|
|
@@ -72,7 +72,7 @@ module CocinaDisplay
|
|
|
72
72
|
subject_occupations + subject_names + subject_titles
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
# Combination of temporal and genre subject
|
|
75
|
+
# Combination of temporal and genre subject parts for searching.
|
|
76
76
|
# @see #subject_temporal
|
|
77
77
|
# @see #subject_genres
|
|
78
78
|
# @return [Array<String>]
|
|
@@ -88,7 +88,7 @@ module CocinaDisplay
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
# Subject data to be rendered for display.
|
|
91
|
-
# Uses the concatenated form for structured subject
|
|
91
|
+
# Uses the concatenated form for structured subject parts.
|
|
92
92
|
# @see Subject#to_s
|
|
93
93
|
# @return [Array<DisplayData>]
|
|
94
94
|
def subject_display_data
|
|
@@ -119,16 +119,16 @@ module CocinaDisplay
|
|
|
119
119
|
all_subjects.filter { |subject| subject.type == "classification" }
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
-
# All subject
|
|
123
|
-
# @return [Array<
|
|
124
|
-
def
|
|
125
|
-
@
|
|
122
|
+
# All subject parts, flattened from all subjects.
|
|
123
|
+
# @return [Array<SubjectPart>]
|
|
124
|
+
def subject_parts
|
|
125
|
+
@subject_parts ||= all_subjects.flat_map(&:parallel_values).flat_map(&:subject_parts)
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
-
# All subject
|
|
129
|
-
# @return [Array<
|
|
130
|
-
def
|
|
131
|
-
@
|
|
128
|
+
# All subject parts that are named places.
|
|
129
|
+
# @return [Array<PlaceSubjectPart>]
|
|
130
|
+
def place_subject_parts
|
|
131
|
+
@place_subject_parts ||= subject_parts.filter { |s| s.is_a? CocinaDisplay::Subjects::PlaceSubjectPart }
|
|
132
132
|
end
|
|
133
133
|
end
|
|
134
134
|
end
|
|
@@ -34,25 +34,28 @@ module CocinaDisplay
|
|
|
34
34
|
primary_title&.sort_title || "\u{10FFFF}"
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
# Any additional titles for the object excluding the primary title.
|
|
37
|
+
# Any additional titles for the object excluding the main value of the primary title.
|
|
38
|
+
# Includes parallel titles of the primary title and all titles from secondary titles.
|
|
38
39
|
# @return [Array<String>]
|
|
39
40
|
# @see CocinaDisplay::Title#display_title
|
|
40
41
|
def additional_titles
|
|
41
|
-
secondary_titles.
|
|
42
|
+
(Array(primary_title&.main_value&.siblings) + secondary_titles.flat_map(&:parallel_values))
|
|
43
|
+
.map(&:display_title).compact_blank
|
|
42
44
|
end
|
|
43
45
|
|
|
44
|
-
# All {
|
|
46
|
+
# All {TitleValue} objects, grouped by their label for display.
|
|
45
47
|
# @note All primary titles are included under "Title", not just the first.
|
|
46
48
|
# @return [Array<DisplayData>]
|
|
47
49
|
# @param exclude_primary [Boolean] Exclude primary titles. Defaults to false.
|
|
48
50
|
def title_display_data(exclude_primary: false)
|
|
49
|
-
|
|
51
|
+
target_titles = exclude_primary ? secondary_titles : all_titles
|
|
52
|
+
DisplayData.from_objects(target_titles.flat_map(&:parallel_values))
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
# The first title marked primary, or the first without a type.
|
|
53
|
-
# @return [
|
|
56
|
+
# @return [Title, nil]
|
|
54
57
|
def primary_title
|
|
55
|
-
all_titles.find { |title| title.primary? }.presence || all_titles.find { |title| !title.
|
|
58
|
+
all_titles.find { |title| title.primary? }.presence || all_titles.find { |title| !title.typed? }
|
|
56
59
|
end
|
|
57
60
|
|
|
58
61
|
# All titles except the primary title.
|
|
@@ -62,16 +65,14 @@ module CocinaDisplay
|
|
|
62
65
|
end
|
|
63
66
|
|
|
64
67
|
# All {Title} objects built from the Cocina titles.
|
|
65
|
-
# Flattens parallel values into separate titles.
|
|
66
68
|
# @return [Array<Title>]
|
|
67
69
|
def all_titles
|
|
68
|
-
@all_titles ||= cocina_titles.
|
|
69
|
-
(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
end
|
|
70
|
+
@all_titles ||= cocina_titles.map do |cocina_title|
|
|
71
|
+
CocinaDisplay::Titles::Title.new(
|
|
72
|
+
cocina_title,
|
|
73
|
+
part_label: part_label,
|
|
74
|
+
part_numbers: part_numbers
|
|
75
|
+
)
|
|
75
76
|
end
|
|
76
77
|
end
|
|
77
78
|
|
|
@@ -19,8 +19,11 @@ module CocinaDisplay
|
|
|
19
19
|
def oembed_url(params: {})
|
|
20
20
|
return if (!is_a?(CocinaDisplay::RelatedResource) && collection?) || purl_url.blank?
|
|
21
21
|
|
|
22
|
-
params
|
|
23
|
-
|
|
22
|
+
### Note that searchworks_traject_indexer sends in a single instance of params for all oembed_url calls.
|
|
23
|
+
### We dup the params to avoid modifying the original hash.
|
|
24
|
+
embed_params = params.dup
|
|
25
|
+
embed_params[:url] ||= purl_url
|
|
26
|
+
"#{purl_base_url}/embed.json?#{embed_params.to_query}"
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
# The download URL to get the entire object as a .zip file.
|
|
@@ -90,7 +90,7 @@ module CocinaDisplay
|
|
|
90
90
|
# @param with_date [Boolean] Include life dates, if present
|
|
91
91
|
# @return [Array<String>]
|
|
92
92
|
def display_names(with_date: false)
|
|
93
|
-
names.
|
|
93
|
+
names.flat_map { |name| name.parallel_values.map { |pv| pv.to_s(with_date: with_date) } }.compact_blank
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
# A single primary name for the contributor.
|
|
@@ -117,19 +117,9 @@ module CocinaDisplay
|
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
# All names in the Cocina as Name objects.
|
|
120
|
-
# Flattens parallel values into separate Name objects.
|
|
121
120
|
# @return [Array<Name>]
|
|
122
121
|
def names
|
|
123
|
-
@names ||= Array(cocina["name"]).
|
|
124
|
-
(Array(name["parallelValue"]).presence || [name]).filter_map do |name_value|
|
|
125
|
-
unless name_value.blank?
|
|
126
|
-
Name.new(name_value).tap do |name_obj|
|
|
127
|
-
name_obj.type ||= name["type"]
|
|
128
|
-
name_obj.status ||= name["status"]
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
122
|
+
@names ||= Array(cocina["name"]).map { |name| Name.new(name) }
|
|
133
123
|
end
|
|
134
124
|
|
|
135
125
|
# All roles in the Cocina structured data.
|
|
@@ -2,102 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
module CocinaDisplay
|
|
4
4
|
module Contributors
|
|
5
|
-
# A name associated with a contributor.
|
|
6
|
-
class Name
|
|
7
|
-
# The
|
|
8
|
-
|
|
9
|
-
attr_reader :cocina
|
|
10
|
-
|
|
11
|
-
# The type of name, if any (e.g., "personal", "corporate", etc.).
|
|
12
|
-
# @return [String, nil]
|
|
13
|
-
attr_accessor :type
|
|
14
|
-
|
|
15
|
-
# The status of the name, if any (e.g., "primary").
|
|
16
|
-
# @return [String, nil]
|
|
17
|
-
attr_accessor :status
|
|
18
|
-
|
|
19
|
-
# Initialize a Name object with Cocina structured data.
|
|
20
|
-
# @param cocina [Hash] The Cocina structured data for the name.
|
|
21
|
-
def initialize(cocina)
|
|
22
|
-
@cocina = cocina
|
|
23
|
-
@type = cocina["type"]
|
|
24
|
-
@status = cocina["status"]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# The display string for the name, optionally including life dates.
|
|
28
|
-
# @param with_date [Boolean] Include life dates, if present
|
|
29
|
-
# @return [String]
|
|
30
|
-
# @example no dates
|
|
31
|
-
# name.to_s # => "King, Martin Luther, Jr."
|
|
32
|
-
# @example with dates
|
|
33
|
-
# name.to_s(with_date: true) # => "King, Martin Luther, Jr., 1929-1968"
|
|
34
|
-
def to_s(with_date: false)
|
|
35
|
-
if cocina["value"].present?
|
|
36
|
-
cocina["value"]
|
|
37
|
-
elsif dates_str.present? && with_date
|
|
38
|
-
Utils.compact_and_join([full_name_str, dates_str], delimiter: ", ")
|
|
39
|
-
else
|
|
40
|
-
full_name_str
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# The full name as a string, combining all name components and terms of address.
|
|
45
|
-
# @return [String]
|
|
46
|
-
def full_name_str
|
|
47
|
-
Utils.compact_and_join(name_components.push(terms_of_address_str), delimiter: ", ")
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# List of all name components.
|
|
51
|
-
# If any of forename, surname, or term of address are present, those are used.
|
|
52
|
-
# Otherwise, fall back to any names explicitly marked as "name" or untyped.
|
|
53
|
-
# @return [Array<String>]
|
|
54
|
-
def name_components
|
|
55
|
-
[surname_str, forename_str].compact_blank.presence || Array(name_values["name"])
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Flatten all terms of address into a comma-delimited string.
|
|
59
|
-
# @return [String]
|
|
60
|
-
def terms_of_address_str
|
|
61
|
-
Utils.compact_and_join(Array(name_values["term of address"]), delimiter: ", ")
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Flatten all forename values and ordinals into a whitespace-delimited string.
|
|
65
|
-
# @return [String]
|
|
66
|
-
def forename_str
|
|
67
|
-
Utils.compact_and_join(Array(name_values["forename"]) + Array(name_values["ordinal"]), delimiter: " ")
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Flatten all surname values into a whitespace-delimited string.
|
|
71
|
-
# @return [String]
|
|
72
|
-
def surname_str
|
|
73
|
-
Utils.compact_and_join(Array(name_values["surname"]), delimiter: " ")
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Flatten all life and activity dates into a comma-delimited string.
|
|
77
|
-
# @return [String]
|
|
78
|
-
def dates_str
|
|
79
|
-
Utils.compact_and_join(Array(name_values["life dates"]) + Array(name_values["activity dates"]), delimiter: ", ")
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Is this a primary name for the contributor?
|
|
83
|
-
# @return [Boolean]
|
|
84
|
-
def primary?
|
|
85
|
-
status == "primary"
|
|
86
|
-
end
|
|
5
|
+
# A name associated with a contributor, potentially in multiple languages.
|
|
6
|
+
class Name < Parallel::Parallel
|
|
7
|
+
# The name is represented by the main parallel value.
|
|
8
|
+
delegate :to_s, :forename_str, :surname_str, to: :main_value
|
|
87
9
|
|
|
88
10
|
private
|
|
89
11
|
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# @note Currently we do nothing with "alternative", "inverted full name", "pseudonym", and "transliteration" types.
|
|
95
|
-
def name_values
|
|
96
|
-
Utils.flatten_nested_values(cocina).each_with_object({}) do |node, hash|
|
|
97
|
-
type = node["type"] || "name"
|
|
98
|
-
hash[type] ||= []
|
|
99
|
-
hash[type] << node["value"]
|
|
100
|
-
end.compact_blank
|
|
12
|
+
# The class to use for parallel values.
|
|
13
|
+
# @return [Class]
|
|
14
|
+
def parallel_value_class
|
|
15
|
+
NameValue
|
|
101
16
|
end
|
|
102
17
|
end
|
|
103
18
|
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CocinaDisplay
|
|
4
|
+
module Contributors
|
|
5
|
+
# A name associated with a contributor in a single language/script.
|
|
6
|
+
class NameValue < Parallel::ParallelValue
|
|
7
|
+
# The display string for the name, optionally including life dates.
|
|
8
|
+
# @param with_date [Boolean] Include life dates, if present
|
|
9
|
+
# @return [String]
|
|
10
|
+
# @example no dates
|
|
11
|
+
# name.to_s # => "King, Martin Luther, Jr."
|
|
12
|
+
# @example with dates
|
|
13
|
+
# name.to_s(with_date: true) # => "King, Martin Luther, Jr., 1929-1968"
|
|
14
|
+
def to_s(with_date: false)
|
|
15
|
+
if cocina["value"].present?
|
|
16
|
+
cocina["value"]
|
|
17
|
+
elsif dates_str.present? && with_date
|
|
18
|
+
Utils.compact_and_join([full_name_str, dates_str], delimiter: ", ")
|
|
19
|
+
else
|
|
20
|
+
full_name_str.presence
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The full name as a string, combining all name components and terms of address.
|
|
25
|
+
# @return [String]
|
|
26
|
+
def full_name_str
|
|
27
|
+
Utils.compact_and_join(name_components.push(terms_of_address_str), delimiter: ", ")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# List of all name components.
|
|
31
|
+
# If any of forename, surname, or term of address are present, those are used.
|
|
32
|
+
# Otherwise, fall back to any names explicitly marked as "name" or untyped.
|
|
33
|
+
# @return [Array<String>]
|
|
34
|
+
def name_components
|
|
35
|
+
[surname_str, forename_str].compact_blank.presence || Array(name_values["name"])
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Flatten all terms of address into a comma-delimited string.
|
|
39
|
+
# @return [String]
|
|
40
|
+
def terms_of_address_str
|
|
41
|
+
Utils.compact_and_join(Array(name_values["term of address"]), delimiter: ", ")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Flatten all forename values and ordinals into a whitespace-delimited string.
|
|
45
|
+
# @return [String]
|
|
46
|
+
def forename_str
|
|
47
|
+
Utils.compact_and_join(Array(name_values["forename"]) + Array(name_values["ordinal"]), delimiter: " ")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Flatten all surname values into a whitespace-delimited string.
|
|
51
|
+
# @return [String]
|
|
52
|
+
def surname_str
|
|
53
|
+
Utils.compact_and_join(Array(name_values["surname"]), delimiter: " ")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Flatten all life and activity dates into a comma-delimited string.
|
|
57
|
+
# @return [String]
|
|
58
|
+
def dates_str
|
|
59
|
+
Utils.compact_and_join(Array(name_values["life dates"]) + Array(name_values["activity dates"]), delimiter: ", ")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# A hash mapping destructured name types to their values.
|
|
65
|
+
# Name values with no type are grouped under "name".
|
|
66
|
+
# @return [Hash<String, Array<String>>]
|
|
67
|
+
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#contributor-name-part-types-for-structured-value
|
|
68
|
+
# @note Currently we do nothing with "alternative", "inverted full name", "pseudonym", and "transliteration" types.
|
|
69
|
+
def name_values
|
|
70
|
+
Utils.flatten_nested_values(cocina).each_with_object({}) do |node, hash|
|
|
71
|
+
type = node["type"] || "name"
|
|
72
|
+
hash[type] ||= []
|
|
73
|
+
hash[type] << node["value"]
|
|
74
|
+
end.compact_blank
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CocinaDisplay
|
|
4
|
+
module Languages
|
|
5
|
+
# A language associated with part or all of a Cocina object.
|
|
6
|
+
class Language
|
|
7
|
+
SEARCHWORKS_LANGUAGES_FILE_PATH = CocinaDisplay.root / "config" / "searchworks_languages.yml"
|
|
8
|
+
|
|
9
|
+
attr_reader :cocina
|
|
10
|
+
|
|
11
|
+
# A hash of language codes to language names recognized by Searchworks.
|
|
12
|
+
# @return [Hash{String => String}]
|
|
13
|
+
def self.searchworks_languages
|
|
14
|
+
@searchworks_languages ||= YAML.safe_load_file(SEARCHWORKS_LANGUAGES_FILE_PATH)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Create a Language object from Cocina structured data.
|
|
18
|
+
# @param cocina [Hash]
|
|
19
|
+
def initialize(cocina)
|
|
20
|
+
@cocina = cocina
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The language name for display.
|
|
24
|
+
# @return [String, nil]
|
|
25
|
+
def to_s
|
|
26
|
+
cocina["value"] || decoded_value
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The language code, e.g. an ISO 639 code like "eng" or "spa".
|
|
30
|
+
# @return [String, nil]
|
|
31
|
+
def code
|
|
32
|
+
cocina["code"]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The IETF tag describing this language and script, if available.
|
|
36
|
+
# @return [String, nil]
|
|
37
|
+
# @example "ara-Latn" for Arabic written in Latin script
|
|
38
|
+
# @see https://en.wikipedia.org/wiki/IETF_language_tag
|
|
39
|
+
def ietf_tag
|
|
40
|
+
return code unless transliterated?
|
|
41
|
+
|
|
42
|
+
"#{code}-#{script.code}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# True if the value is transliterated, i.e. non-English written in Latin script.
|
|
46
|
+
# @return [Boolean]
|
|
47
|
+
def transliterated?
|
|
48
|
+
!english? && script&.latin?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# True if the language is English.
|
|
52
|
+
# @return [Boolean]
|
|
53
|
+
def english?
|
|
54
|
+
to_s == "English"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Decoded name of the language based on the code, if present.
|
|
58
|
+
# @return [String, nil]
|
|
59
|
+
def decoded_value
|
|
60
|
+
Language.searchworks_languages[code] if searchworks_language?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Display label for this field.
|
|
64
|
+
# @return [String]
|
|
65
|
+
def label
|
|
66
|
+
cocina["displayLabel"].presence || I18n.t("cocina_display.field_label.language")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# True if the language is recognized by Searchworks.
|
|
70
|
+
# @see CocinaDisplay::Language.searchworks_languages
|
|
71
|
+
# @return [Boolean]
|
|
72
|
+
def searchworks_language?
|
|
73
|
+
Language.searchworks_languages.value?(cocina["value"]) || Language.searchworks_languages.key?(code)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The script of the language, if specified.
|
|
77
|
+
# @return [CocinaDisplay::Languages::Script, nil]
|
|
78
|
+
def script
|
|
79
|
+
CocinaDisplay::Languages::Script.new(script_cocina) if script_cocina.present?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# The script might be in "script" (top-level) or "valueScript" (nested).
|
|
85
|
+
# @return [Hash, nil]
|
|
86
|
+
def script_cocina
|
|
87
|
+
cocina["valueScript"] || cocina["script"]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module CocinaDisplay
|
|
2
|
+
module Languages
|
|
3
|
+
# A script used to write a Language.
|
|
4
|
+
class Script
|
|
5
|
+
attr_reader :cocina
|
|
6
|
+
|
|
7
|
+
def initialize(cocina)
|
|
8
|
+
@cocina = cocina
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# The script code, e.g. an ISO 15924 code like "Latn" or "Cyrl".
|
|
12
|
+
# @return [String, nil]
|
|
13
|
+
def code
|
|
14
|
+
cocina["code"]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# True if the script is Latin.
|
|
18
|
+
# @return [Boolean]
|
|
19
|
+
def latin?
|
|
20
|
+
code == "Latn"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module CocinaDisplay
|
|
2
|
+
module Notes
|
|
3
|
+
# A note associated with a cocina record
|
|
4
|
+
class Note < Parallel::Parallel
|
|
5
|
+
# Use the main parallel value to identify the type of note.
|
|
6
|
+
delegate :abstract?, :general_note?, :preferred_citation?, :table_of_contents?, :part?, to: :main_value
|
|
7
|
+
|
|
8
|
+
# Display methods reference the main note value. For parallel values,
|
|
9
|
+
# see #translated_value and #transliterated_value.
|
|
10
|
+
delegate :to_s, :values, :values_by_type, to: :main_value
|
|
11
|
+
|
|
12
|
+
# Label used to render the note for display.
|
|
13
|
+
# Uses a displayLabel if available, otherwise tries to look up via type.
|
|
14
|
+
# Falls back to a default label derived from the type or a generic note label if
|
|
15
|
+
# no type is set.
|
|
16
|
+
# @return [String]
|
|
17
|
+
def label
|
|
18
|
+
display_label ||
|
|
19
|
+
I18n.t(type&.parameterize&.underscore, default: default_label, scope: "cocina_display.field_label.note")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# The class to use for parallel values.
|
|
25
|
+
# @return [Class]
|
|
26
|
+
def parallel_value_class
|
|
27
|
+
NoteValue
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The default label for the note if no i18n key exists.
|
|
31
|
+
# @return [String]
|
|
32
|
+
def default_label
|
|
33
|
+
type&.capitalize || I18n.t("cocina_display.field_label.note.note")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|