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.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.standard.yml +1 -1
- data/README.md +21 -2
- data/config/i18n-tasks.yml +0 -0
- data/config/licenses.yml +59 -0
- data/config/locales/en.yml +109 -0
- data/config/marc_countries.yml +385 -0
- data/config/marc_relators.yml +310 -0
- data/config/searchworks_languages.yml +520 -0
- data/lib/cocina_display/cocina_record.rb +29 -64
- data/lib/cocina_display/concerns/accesses.rb +78 -0
- data/lib/cocina_display/concerns/contributors.rb +32 -11
- data/lib/cocina_display/concerns/events.rb +19 -6
- data/lib/cocina_display/concerns/forms.rb +98 -11
- data/lib/cocina_display/concerns/geospatial.rb +9 -5
- data/lib/cocina_display/concerns/identifiers.rb +25 -5
- data/lib/cocina_display/concerns/languages.rb +6 -2
- data/lib/cocina_display/concerns/notes.rb +36 -0
- data/lib/cocina_display/concerns/related_resources.rb +20 -0
- data/lib/cocina_display/concerns/subjects.rb +25 -8
- data/lib/cocina_display/concerns/titles.rb +67 -25
- data/lib/cocina_display/concerns/{access.rb → url_helpers.rb} +3 -3
- data/lib/cocina_display/concerns.rb +6 -0
- data/lib/cocina_display/contributors/contributor.rb +47 -26
- data/lib/cocina_display/contributors/name.rb +18 -14
- data/lib/cocina_display/contributors/role.rb +31 -13
- data/lib/cocina_display/dates/date.rb +55 -14
- data/lib/cocina_display/dates/date_range.rb +0 -2
- data/lib/cocina_display/description/access.rb +41 -0
- data/lib/cocina_display/description/access_contact.rb +11 -0
- data/lib/cocina_display/description/url.rb +17 -0
- data/lib/cocina_display/display_data.rb +104 -0
- data/lib/cocina_display/events/event.rb +8 -4
- data/lib/cocina_display/events/imprint.rb +0 -10
- data/lib/cocina_display/events/location.rb +9 -3
- data/lib/cocina_display/events/note.rb +33 -0
- data/lib/cocina_display/forms/form.rb +71 -0
- data/lib/cocina_display/forms/genre.rb +12 -0
- data/lib/cocina_display/forms/resource_type.rb +38 -0
- data/lib/cocina_display/geospatial.rb +1 -1
- data/lib/cocina_display/identifier.rb +101 -0
- data/lib/cocina_display/json_backed_record.rb +27 -0
- data/lib/cocina_display/language.rb +18 -12
- data/lib/cocina_display/license.rb +32 -0
- data/lib/cocina_display/note.rb +103 -0
- data/lib/cocina_display/related_resource.rb +74 -0
- data/lib/cocina_display/subjects/subject.rb +32 -9
- data/lib/cocina_display/subjects/subject_value.rb +34 -16
- data/lib/cocina_display/title.rb +221 -0
- data/lib/cocina_display/utils.rb +4 -4
- data/lib/cocina_display/version.rb +1 -1
- data/lib/cocina_display.rb +32 -2
- metadata +46 -12
- data/lib/cocina_display/title_builder.rb +0 -397
- data/lib/cocina_display/vocabularies/marc_country_codes.rb +0 -393
- data/lib/cocina_display/vocabularies/marc_relator_codes.rb +0 -318
- data/lib/cocina_display/vocabularies/searchworks_languages.rb +0 -526
|
@@ -1,48 +1,77 @@
|
|
|
1
|
-
require_relative "../title_builder"
|
|
2
|
-
|
|
3
1
|
module CocinaDisplay
|
|
4
2
|
module Concerns
|
|
5
3
|
# Methods for finding and formatting titles.
|
|
6
4
|
module Titles
|
|
7
5
|
# The main title for the object, without subtitle, part name, etc.
|
|
8
|
-
# If there are multiple titles, uses the first.
|
|
9
|
-
# @see CocinaDisplay::
|
|
10
|
-
# @
|
|
11
|
-
# @return [String]
|
|
6
|
+
# If there are multiple primary titles, uses the first.
|
|
7
|
+
# @see CocinaDisplay::Title#main_title
|
|
8
|
+
# @return [String, nil]
|
|
12
9
|
def main_title
|
|
13
|
-
|
|
10
|
+
primary_title&.short_title
|
|
14
11
|
end
|
|
15
12
|
|
|
16
13
|
# The full title for the object, including subtitle, part name, etc.
|
|
17
|
-
# If there are multiple titles, uses the first.
|
|
18
|
-
# @see CocinaDisplay::
|
|
19
|
-
# @
|
|
20
|
-
# @return [String]
|
|
14
|
+
# If there are multiple primary titles, uses the first.
|
|
15
|
+
# @see CocinaDisplay::Title#full_title
|
|
16
|
+
# @return [String, nil]
|
|
21
17
|
def full_title
|
|
22
|
-
|
|
18
|
+
primary_title&.full_title
|
|
23
19
|
end
|
|
24
20
|
|
|
25
21
|
# The full title, joined together with additional punctuation.
|
|
26
|
-
# If there are multiple titles, uses the first.
|
|
27
|
-
# @see CocinaDisplay::
|
|
28
|
-
# @return [String]
|
|
22
|
+
# If there are multiple primary titles, uses the first.
|
|
23
|
+
# @see CocinaDisplay::Title#display_title
|
|
24
|
+
# @return [String, nil]
|
|
29
25
|
def display_title
|
|
30
|
-
|
|
26
|
+
primary_title&.display_title
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# A string value for sorting by title that sorts missing values last.
|
|
30
|
+
# If there are multiple primary titles, uses the first.
|
|
31
|
+
# @see CocinaDisplay::Title#sort_title
|
|
32
|
+
# @return [String]
|
|
33
|
+
def sort_title
|
|
34
|
+
primary_title&.sort_title || "\u{10FFFF}"
|
|
31
35
|
end
|
|
32
36
|
|
|
33
|
-
# Any additional titles for the object excluding the
|
|
37
|
+
# Any additional titles for the object excluding the primary title.
|
|
34
38
|
# @return [Array<String>]
|
|
35
|
-
# @see CocinaDisplay::
|
|
39
|
+
# @see CocinaDisplay::Title#display_title
|
|
36
40
|
def additional_titles
|
|
37
|
-
|
|
41
|
+
secondary_titles.map(&:display_title).compact_blank
|
|
38
42
|
end
|
|
39
43
|
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
# @
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
# All {Title} objects, grouped by their label for display.
|
|
45
|
+
# @note All primary titles are included under "Title", not just the first.
|
|
46
|
+
# @return [Array<DisplayData>]
|
|
47
|
+
def title_display_data
|
|
48
|
+
DisplayData.from_objects(all_titles)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The first title marked primary, or the first without a type.
|
|
52
|
+
# @return [Array<Title>]
|
|
53
|
+
def primary_title
|
|
54
|
+
all_titles.find { |title| title.primary? }.presence || all_titles.find { |title| !title.type? }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# All titles except the primary title.
|
|
58
|
+
# @return [Array<Title>]
|
|
59
|
+
def secondary_titles
|
|
60
|
+
all_titles - [primary_title]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# All {Title} objects built from the Cocina titles.
|
|
64
|
+
# Flattens parallel values into separate titles.
|
|
65
|
+
# @return [Array<Title>]
|
|
66
|
+
def all_titles
|
|
67
|
+
@all_titles ||= cocina_titles.flat_map do |cocina_title|
|
|
68
|
+
(Array(cocina_title["parallelValue"]).presence || [cocina_title]).map do |value|
|
|
69
|
+
Title.new(value, part_label: part_label, part_numbers: part_numbers).tap do |title|
|
|
70
|
+
title.type ||= cocina_title["type"]
|
|
71
|
+
title.status ||= cocina_title["status"]
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
46
75
|
end
|
|
47
76
|
|
|
48
77
|
private
|
|
@@ -59,6 +88,19 @@ module CocinaDisplay
|
|
|
59
88
|
def catalog_links
|
|
60
89
|
@catalog_links ||= Array(cocina_doc.dig("identification", "catalogLinks"))
|
|
61
90
|
end
|
|
91
|
+
|
|
92
|
+
# Part label for digital serials display from FOLIO, if any.
|
|
93
|
+
# @return [String, nil]
|
|
94
|
+
def part_label
|
|
95
|
+
catalog_links.find { |link| link["catalog"] == "folio" }&.fetch("partLabel", nil)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Part numbers from notes, if any.
|
|
99
|
+
# @note This is used for RelatedResource title display.
|
|
100
|
+
# @return [Array<String>]
|
|
101
|
+
def part_numbers
|
|
102
|
+
notes.filter(&:part?).flat_map { |note| note.values_by_type["number"] }.compact_blank
|
|
103
|
+
end
|
|
62
104
|
end
|
|
63
105
|
end
|
|
64
106
|
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
module CocinaDisplay
|
|
2
2
|
module Concerns
|
|
3
3
|
# Methods that generate URLs to access an object.
|
|
4
|
-
module
|
|
4
|
+
module UrlHelpers
|
|
5
5
|
# The PURL URL for this object.
|
|
6
6
|
# @return [String]
|
|
7
7
|
# @example
|
|
8
8
|
# record.purl_url #=> "https://purl.stanford.edu/bx658jh7339"
|
|
9
9
|
def purl_url
|
|
10
|
-
cocina_doc.dig("description", "purl")
|
|
10
|
+
cocina_doc.dig("description", "purl")
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# The oEmbed URL for the object, optionally with additional parameters.
|
|
@@ -18,7 +18,7 @@ module CocinaDisplay
|
|
|
18
18
|
# @example Generate an oEmbed URL for the viewer and hide the title
|
|
19
19
|
# record.oembed_url(hide_title: true) #=> "https://purl.stanford.edu/bx658jh7339/embed.json?hide_title=true"
|
|
20
20
|
def oembed_url(params: {})
|
|
21
|
-
return if collection? || purl_url.blank?
|
|
21
|
+
return if (!is_a?(CocinaDisplay::RelatedResource) && collection?) || purl_url.blank?
|
|
22
22
|
|
|
23
23
|
params[:url] ||= purl_url
|
|
24
24
|
"#{purl_base_url}/embed.json?#{params.to_query}"
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "active_support"
|
|
4
|
-
require "active_support/core_ext/object/blank"
|
|
5
|
-
require "active_support/core_ext/array/conversions"
|
|
6
|
-
|
|
7
|
-
require_relative "../utils"
|
|
8
|
-
require_relative "name"
|
|
9
|
-
require_relative "role"
|
|
10
|
-
|
|
11
3
|
module CocinaDisplay
|
|
12
4
|
module Contributors
|
|
13
5
|
# A contributor to a work, such as an author or publisher.
|
|
@@ -20,11 +12,22 @@ module CocinaDisplay
|
|
|
20
12
|
@cocina = cocina
|
|
21
13
|
end
|
|
22
14
|
|
|
23
|
-
# String representation of the contributor,
|
|
24
|
-
#
|
|
25
|
-
# @return [String]
|
|
15
|
+
# String representation of the contributor, using display name with date.
|
|
16
|
+
# @return [String, nil]
|
|
26
17
|
def to_s
|
|
27
|
-
|
|
18
|
+
display_name(with_date: true)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Support equality based on the underlying Cocina data.
|
|
22
|
+
# @param other [Object]
|
|
23
|
+
def ==(other)
|
|
24
|
+
other.is_a?(Contributor) && other.cocina == cocina
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Identifiers for the contributor.
|
|
28
|
+
# @return [Array<Identifier>]
|
|
29
|
+
def identifiers
|
|
30
|
+
Array(cocina["identifier"]).map { |id| Identifier.new(id) }
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
# Is this contributor a human?
|
|
@@ -75,39 +78,57 @@ module CocinaDisplay
|
|
|
75
78
|
roles.any?
|
|
76
79
|
end
|
|
77
80
|
|
|
78
|
-
# The display name for the contributor as a string.
|
|
79
|
-
# Uses the first name if multiple names are present.
|
|
81
|
+
# The primary display name for the contributor as a string.
|
|
80
82
|
# @param with_date [Boolean] Include life dates, if present
|
|
81
|
-
# @return [String]
|
|
83
|
+
# @return [String, nil]
|
|
82
84
|
def display_name(with_date: false)
|
|
83
|
-
|
|
85
|
+
primary_name&.to_s(with_date: with_date)
|
|
84
86
|
end
|
|
85
87
|
|
|
86
|
-
#
|
|
88
|
+
# String renderings of all names for the contributor.
|
|
89
|
+
# @param with_date [Boolean] Include life dates, if present
|
|
90
|
+
# @return [Array<String>]
|
|
91
|
+
def display_names(with_date: false)
|
|
92
|
+
names.map { |name| name.to_s(with_date: with_date) }.compact_blank
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# A single primary name for the contributor.
|
|
96
|
+
# Prefers a name of type "display" or one marked primary.
|
|
97
|
+
# @return [Contributor::Name, nil]
|
|
98
|
+
def primary_name
|
|
99
|
+
names.find { |name| name.type == "display" }.presence ||
|
|
100
|
+
names.find(&:primary?).presence ||
|
|
101
|
+
names.first
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# The forename for the contributor, if structured name info is available.
|
|
87
105
|
# @see Contributor::Name::forename_str
|
|
88
106
|
# @return [String, nil]
|
|
89
107
|
def forename
|
|
90
108
|
names.map(&:forename_str).first.presence
|
|
91
109
|
end
|
|
92
110
|
|
|
93
|
-
# The
|
|
111
|
+
# The surname for the contributor, if structured name info is available.
|
|
94
112
|
# @see Contributor::Name::surname_str
|
|
95
113
|
# @return [String, nil]
|
|
96
114
|
def surname
|
|
97
115
|
names.map(&:surname_str).first.presence
|
|
98
116
|
end
|
|
99
117
|
|
|
100
|
-
# A string representation of the contributor's roles, formatted for display.
|
|
101
|
-
# If there are multiple roles, they are joined with commas.
|
|
102
|
-
# @return [String]
|
|
103
|
-
def display_role
|
|
104
|
-
roles.map(&:to_s).to_sentence
|
|
105
|
-
end
|
|
106
|
-
|
|
107
118
|
# All names in the Cocina as Name objects.
|
|
119
|
+
# Flattens parallel values into separate Name objects.
|
|
108
120
|
# @return [Array<Name>]
|
|
109
121
|
def names
|
|
110
|
-
@names ||= Array(cocina["name"]).
|
|
122
|
+
@names ||= Array(cocina["name"]).flat_map do |name|
|
|
123
|
+
(Array(name["parallelValue"]).presence || [name]).filter_map do |name_value|
|
|
124
|
+
unless name_value.blank?
|
|
125
|
+
Name.new(name_value).tap do |name_obj|
|
|
126
|
+
name_obj.type ||= name["type"]
|
|
127
|
+
name_obj.status ||= name["status"]
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
111
132
|
end
|
|
112
133
|
|
|
113
134
|
# All roles in the Cocina structured data.
|
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "../utils"
|
|
4
|
-
|
|
5
3
|
module CocinaDisplay
|
|
6
4
|
module Contributors
|
|
7
5
|
# A name associated with a contributor.
|
|
8
6
|
class Name
|
|
7
|
+
# The underlying Cocina structured data for the name.
|
|
8
|
+
# @return [Hash]
|
|
9
9
|
attr_reader :cocina
|
|
10
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
|
+
|
|
11
19
|
# Initialize a Name object with Cocina structured data.
|
|
12
20
|
# @param cocina [Hash] The Cocina structured data for the name.
|
|
13
21
|
def initialize(cocina)
|
|
14
22
|
@cocina = cocina
|
|
23
|
+
@type = cocina["type"]
|
|
24
|
+
@status = cocina["status"]
|
|
15
25
|
end
|
|
16
26
|
|
|
17
27
|
# 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
28
|
# @param with_date [Boolean] Include life dates, if present
|
|
23
29
|
# @return [String]
|
|
24
30
|
# @example no dates
|
|
@@ -28,8 +34,6 @@ module CocinaDisplay
|
|
|
28
34
|
def to_s(with_date: false)
|
|
29
35
|
if cocina["value"].present?
|
|
30
36
|
cocina["value"]
|
|
31
|
-
elsif display_name_str.present?
|
|
32
|
-
display_name_str
|
|
33
37
|
elsif dates_str.present? && with_date
|
|
34
38
|
Utils.compact_and_join([full_name_str, dates_str], delimiter: ", ")
|
|
35
39
|
else
|
|
@@ -43,12 +47,6 @@ module CocinaDisplay
|
|
|
43
47
|
Utils.compact_and_join(name_components.push(terms_of_address_str), delimiter: ", ")
|
|
44
48
|
end
|
|
45
49
|
|
|
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
50
|
# List of all name components.
|
|
53
51
|
# If any of forename, surname, or term of address are present, those are used.
|
|
54
52
|
# Otherwise, fall back to any names explicitly marked as "name" or untyped.
|
|
@@ -81,6 +79,12 @@ module CocinaDisplay
|
|
|
81
79
|
Utils.compact_and_join(Array(name_values["life dates"]) + Array(name_values["activity dates"]), delimiter: ", ")
|
|
82
80
|
end
|
|
83
81
|
|
|
82
|
+
# Is this a primary name for the contributor?
|
|
83
|
+
# @return [Boolean]
|
|
84
|
+
def primary?
|
|
85
|
+
status == "primary"
|
|
86
|
+
end
|
|
87
|
+
|
|
84
88
|
private
|
|
85
89
|
|
|
86
90
|
# A hash mapping destructured name types to their values.
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "../vocabularies/marc_relator_codes"
|
|
4
|
-
|
|
5
3
|
module CocinaDisplay
|
|
6
4
|
module Contributors
|
|
7
5
|
# A role associated with a contributor.
|
|
8
6
|
class Role
|
|
7
|
+
MARC_RELATORS_FILE_PATH = CocinaDisplay.root / "config" / "marc_relators.yml"
|
|
8
|
+
|
|
9
9
|
attr_reader :cocina
|
|
10
10
|
|
|
11
|
+
# A hash mapping MARC relator codes to their names.
|
|
12
|
+
# @return [Hash{String => String}]
|
|
13
|
+
def self.marc_relators
|
|
14
|
+
@marc_relators ||= YAML.safe_load_file(MARC_RELATORS_FILE_PATH)
|
|
15
|
+
end
|
|
16
|
+
|
|
11
17
|
# Initialize a Role object with Cocina structured data.
|
|
12
18
|
# @param cocina [Hash] The Cocina structured data for the role.
|
|
13
19
|
def initialize(cocina)
|
|
@@ -16,37 +22,49 @@ module CocinaDisplay
|
|
|
16
22
|
|
|
17
23
|
# The name of the role.
|
|
18
24
|
# Translates the MARC relator code if no value was present.
|
|
19
|
-
# @return [String, nil]
|
|
25
|
+
# @return [String, nil] A nil role is typically displayed in the UI as an "Associated with" relationship
|
|
20
26
|
def to_s
|
|
21
|
-
cocina
|
|
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"]
|
|
27
|
+
cocina.fetch("value") { marc_value }
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# Does this role indicate the contributor is an author?
|
|
31
31
|
# @return [Boolean]
|
|
32
32
|
def author?
|
|
33
|
-
|
|
33
|
+
/^(author|creator|primary investigator)/i.match? to_s
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Does this role indicate the contributor is a publisher?
|
|
37
37
|
# @return [Boolean]
|
|
38
38
|
def publisher?
|
|
39
|
-
|
|
39
|
+
/^publisher/i.match? to_s
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Does this role indicate the contributor is a funder?
|
|
43
43
|
# @return [Boolean]
|
|
44
44
|
def funder?
|
|
45
|
-
|
|
45
|
+
/^funder/i.match? to_s
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
private
|
|
49
49
|
|
|
50
|
+
# The name of the MARC relator role
|
|
51
|
+
# @raises [KeyError] if the role is not valid
|
|
52
|
+
# @return [String, nil]
|
|
53
|
+
def marc_value
|
|
54
|
+
return unless marc_relator?
|
|
55
|
+
|
|
56
|
+
Role.marc_relators.fetch(code)
|
|
57
|
+
rescue
|
|
58
|
+
CocinaDisplay.notifier&.notify("Invalid marc relator: #{code}")
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A code associated with the role, e.g. a MARC relator code.
|
|
63
|
+
# @return [String, nil]
|
|
64
|
+
def code
|
|
65
|
+
cocina["code"]
|
|
66
|
+
end
|
|
67
|
+
|
|
50
68
|
# Does this role have a MARC relator code?
|
|
51
69
|
# @return [Boolean]
|
|
52
70
|
def marc_relator?
|
|
@@ -11,6 +11,10 @@ module CocinaDisplay
|
|
|
11
11
|
# List of values that we shouldn't even attempt to parse.
|
|
12
12
|
UNPARSABLE_VALUES = ["0000-00-00", "9999", "uuuu", "[uuuu]"].freeze
|
|
13
13
|
|
|
14
|
+
def self.notifier
|
|
15
|
+
CocinaDisplay.notifier
|
|
16
|
+
end
|
|
17
|
+
|
|
14
18
|
# Construct a Date from parsed Cocina data.
|
|
15
19
|
# @param cocina [Hash] Cocina date data
|
|
16
20
|
# @return [CocinaDisplay::Date]
|
|
@@ -75,6 +79,11 @@ module CocinaDisplay
|
|
|
75
79
|
# @param value [String] the date value to modify
|
|
76
80
|
# @return [String]
|
|
77
81
|
def self.normalize_to_edtf(value)
|
|
82
|
+
unless value
|
|
83
|
+
notifier&.notify("Invalid date value: #{value}")
|
|
84
|
+
return
|
|
85
|
+
end
|
|
86
|
+
|
|
78
87
|
sanitized = value.gsub(/^[\[]+/, "").gsub(/[\.\]]+$/, "")
|
|
79
88
|
sanitized = value.rjust(4, "0") if /^\d{3}$/.match?(value)
|
|
80
89
|
|
|
@@ -110,6 +119,18 @@ module CocinaDisplay
|
|
|
110
119
|
cocina["value"]
|
|
111
120
|
end
|
|
112
121
|
|
|
122
|
+
# The string representation of the date for display.
|
|
123
|
+
# @return [String]
|
|
124
|
+
def to_s
|
|
125
|
+
qualified_value
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Label used to group the date for display.
|
|
129
|
+
# @return [String]
|
|
130
|
+
def label
|
|
131
|
+
cocina["displayLabel"].presence || type_label
|
|
132
|
+
end
|
|
133
|
+
|
|
113
134
|
# The qualifier for this date, if any, such as "approximate", "inferred", etc.
|
|
114
135
|
# @return [String, nil]
|
|
115
136
|
def qualifier
|
|
@@ -183,14 +204,18 @@ module CocinaDisplay
|
|
|
183
204
|
# @return [Symbol] :year, :month, :day, :decade, :century, or :unknown
|
|
184
205
|
def precision
|
|
185
206
|
return :unknown unless date_range || date
|
|
186
|
-
|
|
187
207
|
if date_range.is_a? EDTF::Century
|
|
188
|
-
:century
|
|
208
|
+
return :century
|
|
189
209
|
elsif date_range.is_a? EDTF::Decade
|
|
190
|
-
:decade
|
|
191
|
-
|
|
210
|
+
return :decade
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
case date
|
|
214
|
+
when EDTF::Season
|
|
192
215
|
:month
|
|
193
|
-
|
|
216
|
+
when EDTF::Unknown
|
|
217
|
+
:unknown
|
|
218
|
+
when EDTF::Interval
|
|
194
219
|
date.precision
|
|
195
220
|
else
|
|
196
221
|
case date.precision
|
|
@@ -265,11 +290,11 @@ module CocinaDisplay
|
|
|
265
290
|
|
|
266
291
|
# Decoded version of the date with "BCE" or "CE". Strips leading zeroes.
|
|
267
292
|
# @param allowed_precisions [Array<Symbol>] List of allowed precisions for the output.
|
|
268
|
-
# Defaults to [:day, :month, :year, :decade, :century].
|
|
293
|
+
# Defaults to [:day, :month, :year, :decade, :century, :unknown].
|
|
269
294
|
# @param ignore_unparseable [Boolean] Return nil instead of the original value if it couldn't be parsed
|
|
270
295
|
# @param display_original_value [Boolean] Return the original value if it was not encoded
|
|
271
296
|
# @return [String]
|
|
272
|
-
def decoded_value(allowed_precisions: [:day, :month, :year, :decade, :century], ignore_unparseable: false, display_original_value: true)
|
|
297
|
+
def decoded_value(allowed_precisions: [:day, :month, :year, :decade, :century, :unknown], ignore_unparseable: false, display_original_value: true)
|
|
273
298
|
return if ignore_unparseable && !parsed_date?
|
|
274
299
|
return value.strip unless parsed_date?
|
|
275
300
|
|
|
@@ -340,12 +365,13 @@ module CocinaDisplay
|
|
|
340
365
|
# @param date [Date] The date to format.
|
|
341
366
|
# @param precision [Symbol] The precision to format the date at, e.g. :month
|
|
342
367
|
# @param allowed_precisions [Array<Symbol>] List of allowed precisions for the output.
|
|
343
|
-
# Options are [:day, :month, :year, :decade, :century].
|
|
368
|
+
# Options are [:day, :month, :year, :decade, :century, :unknown].
|
|
344
369
|
# @note allowed_precisions should be ordered by granularity, with most specific first.
|
|
345
370
|
def format_date(date, precision, allowed_precisions)
|
|
346
371
|
precision = allowed_precisions.first unless allowed_precisions.include?(precision)
|
|
347
|
-
|
|
348
372
|
case precision
|
|
373
|
+
when :unknown
|
|
374
|
+
"Unknown"
|
|
349
375
|
when :day
|
|
350
376
|
date.strftime("%B %e, %Y")
|
|
351
377
|
when :month
|
|
@@ -378,8 +404,6 @@ module CocinaDisplay
|
|
|
378
404
|
return nil if date.nil?
|
|
379
405
|
|
|
380
406
|
case date_range
|
|
381
|
-
when EDTF::Unknown
|
|
382
|
-
nil
|
|
383
407
|
when EDTF::Epoch, EDTF::Interval, EDTF::Season
|
|
384
408
|
date_range.min
|
|
385
409
|
when EDTF::Set
|
|
@@ -400,8 +424,6 @@ module CocinaDisplay
|
|
|
400
424
|
return nil if date.nil?
|
|
401
425
|
|
|
402
426
|
case date_range
|
|
403
|
-
when EDTF::Unknown
|
|
404
|
-
nil
|
|
405
427
|
when EDTF::Epoch, EDTF::Interval, EDTF::Season
|
|
406
428
|
date_range.max
|
|
407
429
|
when EDTF::Set
|
|
@@ -439,18 +461,37 @@ module CocinaDisplay
|
|
|
439
461
|
[nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
|
|
440
462
|
end
|
|
441
463
|
end
|
|
464
|
+
|
|
465
|
+
# Label for the date based on its type.
|
|
466
|
+
# @example "publication" becomes "Publication date"
|
|
467
|
+
# @return [String]
|
|
468
|
+
def type_label
|
|
469
|
+
I18n.t(
|
|
470
|
+
type || "untyped",
|
|
471
|
+
scope: "cocina_display.field_label.event.date",
|
|
472
|
+
type: type&.capitalize,
|
|
473
|
+
default: [:default]
|
|
474
|
+
)
|
|
475
|
+
end
|
|
442
476
|
end
|
|
443
477
|
|
|
444
478
|
# Strict ISO8601-encoded date parser.
|
|
445
479
|
class Iso8601Format < Date
|
|
446
480
|
def self.parse_date(value)
|
|
447
481
|
::Date.parse(normalize_to_edtf(value))
|
|
482
|
+
rescue ::Date::Error
|
|
483
|
+
notifier&.notify("Invalid date value \"#{value}\" for iso8601 encoding")
|
|
484
|
+
nil
|
|
448
485
|
end
|
|
449
486
|
end
|
|
450
487
|
|
|
451
488
|
# Less strict W3CDTF-encoded date parser.
|
|
452
489
|
class W3cdtfFormat < Date
|
|
453
490
|
def self.normalize_to_edtf(value)
|
|
491
|
+
unless value
|
|
492
|
+
notifier&.notify("Invalid date value: #{value}")
|
|
493
|
+
return
|
|
494
|
+
end
|
|
454
495
|
super.gsub("-00", "")
|
|
455
496
|
end
|
|
456
497
|
end
|
|
@@ -503,7 +544,7 @@ module CocinaDisplay
|
|
|
503
544
|
# Base class for date formats that match using a regex.
|
|
504
545
|
class ExtractorDateFormat < Date
|
|
505
546
|
def self.supports?(value)
|
|
506
|
-
|
|
547
|
+
self::REGEX.match?(value)
|
|
507
548
|
end
|
|
508
549
|
end
|
|
509
550
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module CocinaDisplay
|
|
2
|
+
module Description
|
|
3
|
+
# Access information for a Cocina object.
|
|
4
|
+
class Access
|
|
5
|
+
attr_reader :cocina
|
|
6
|
+
|
|
7
|
+
# Create an Access object from Cocina structured data.
|
|
8
|
+
# @param cocina [Hash]
|
|
9
|
+
def initialize(cocina)
|
|
10
|
+
@cocina = cocina
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# String representation of the access metadata.
|
|
14
|
+
# @return [String, nil]
|
|
15
|
+
def to_s
|
|
16
|
+
cocina["value"].presence
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# The type of the access metadata, e.g. "repository".
|
|
20
|
+
# @return [String, nil]
|
|
21
|
+
def type
|
|
22
|
+
cocina["type"].presence
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# The display label for the access metadata.
|
|
26
|
+
# @return [String]
|
|
27
|
+
def label
|
|
28
|
+
cocina["displayLabel"].presence ||
|
|
29
|
+
I18n.t(type&.parameterize&.underscore, default: :access, scope: "cocina_display.field_label.access")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Whether the access info is a contact email.
|
|
33
|
+
# Always false, see CocinaDisplay::Description::AccessContact
|
|
34
|
+
# for cases when this is true
|
|
35
|
+
# @return [Boolean]
|
|
36
|
+
def contact_email?
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module CocinaDisplay
|
|
2
|
+
module Description
|
|
3
|
+
class Url < Access
|
|
4
|
+
# The display label for the URL access metadata.
|
|
5
|
+
# @return [String]
|
|
6
|
+
def label
|
|
7
|
+
I18n.t(:url, scope: "cocina_display.field_label.access")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# The link text for the URL access metadata.
|
|
11
|
+
# @return [String, nil]
|
|
12
|
+
def link_text
|
|
13
|
+
cocina["displayLabel"].presence
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|