cocina_display 1.1.3 → 1.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/.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/lib/cocina_display/cocina_record.rb +27 -63
- 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 +15 -4
- 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 +20 -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 +0 -2
- 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 +9 -11
- 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 +194 -0
- data/lib/cocina_display/utils.rb +4 -4
- data/lib/cocina_display/version.rb +1 -1
- data/lib/cocina_display.rb +30 -2
- metadata +45 -11
- data/lib/cocina_display/title_builder.rb +0 -397
- /data/lib/cocina_display/vocabularies/{marc_country_codes.rb → marc_country.rb} +0 -0
- /data/lib/cocina_display/vocabularies/{marc_relator_codes.rb → marc_relator.rb} +0 -0
@@ -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,7 +1,5 @@
|
|
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.
|
@@ -16,37 +14,46 @@ module CocinaDisplay
|
|
16
14
|
|
17
15
|
# The name of the role.
|
18
16
|
# Translates the MARC relator code if no value was present.
|
19
|
-
# @return [String, nil]
|
17
|
+
# @return [String, nil] A nil role is typically displayed in the UI as an "Associated with" relationship
|
20
18
|
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"]
|
19
|
+
cocina.fetch("value") { marc_value }
|
28
20
|
end
|
29
21
|
|
30
22
|
# Does this role indicate the contributor is an author?
|
31
23
|
# @return [Boolean]
|
32
24
|
def author?
|
33
|
-
|
25
|
+
/^(author|creator|primary investigator)/i.match? to_s
|
34
26
|
end
|
35
27
|
|
36
28
|
# Does this role indicate the contributor is a publisher?
|
37
29
|
# @return [Boolean]
|
38
30
|
def publisher?
|
39
|
-
|
31
|
+
/^publisher/i.match? to_s
|
40
32
|
end
|
41
33
|
|
42
34
|
# Does this role indicate the contributor is a funder?
|
43
35
|
# @return [Boolean]
|
44
36
|
def funder?
|
45
|
-
|
37
|
+
/^funder/i.match? to_s
|
46
38
|
end
|
47
39
|
|
48
40
|
private
|
49
41
|
|
42
|
+
# The name of the MARC relator role
|
43
|
+
# @raises [KeyError] if the role is not valid
|
44
|
+
# @return [String, nil]
|
45
|
+
def marc_value
|
46
|
+
return unless marc_relator?
|
47
|
+
|
48
|
+
Vocabularies::MARC_RELATOR.fetch(code)
|
49
|
+
end
|
50
|
+
|
51
|
+
# A code associated with the role, e.g. a MARC relator code.
|
52
|
+
# @return [String, nil]
|
53
|
+
def code
|
54
|
+
cocina["code"]
|
55
|
+
end
|
56
|
+
|
50
57
|
# Does this role have a MARC relator code?
|
51
58
|
# @return [Boolean]
|
52
59
|
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
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CocinaDisplay
|
4
|
+
# A data structure to be rendered into HTML by the consumer.
|
5
|
+
class DisplayData
|
6
|
+
class << self
|
7
|
+
# Given objects that support #to_s and #label, group them into {DisplayData}.
|
8
|
+
# Groups by each object's +label+ and keeps unique, non-blank values.
|
9
|
+
# @param objects [Array<Object>]
|
10
|
+
# @return [Array<DisplayData>]
|
11
|
+
def from_objects(objects)
|
12
|
+
objects.group_by(&:label)
|
13
|
+
.map { |label, objs| new(label: label, objects: objs) }
|
14
|
+
.reject { |data| data.values.empty? }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Given an array of Cocina hashes, group them into {DisplayData}.
|
18
|
+
# Uses +label+ as the label if provided, but honors +displayLabel+ if set.
|
19
|
+
# Keeps the unique, non-blank values under each label.
|
20
|
+
# @param cocina [Array<Hash>]
|
21
|
+
# @param label [String]
|
22
|
+
# @return [Array<DisplayData>]
|
23
|
+
def from_cocina(cocina, label: nil)
|
24
|
+
from_objects(descriptive_values_from_cocina(cocina, label: label))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create display data from a string value.
|
28
|
+
# @param value [String] The string value to display
|
29
|
+
# @param label [String] The label for the display data
|
30
|
+
# @return [Array<DisplayData>] The display data
|
31
|
+
def from_string(value, label: nil)
|
32
|
+
from_objects(descriptive_values_from_string(value, label: label))
|
33
|
+
end
|
34
|
+
|
35
|
+
# Create an array containing a descriptive object from a string value.
|
36
|
+
# Can be used to combine a string derived value with other metadata objects.
|
37
|
+
# @param string [String] The string value to display
|
38
|
+
# @param label [String] The label for the display data
|
39
|
+
# @return [Array<DescriptiveValue>] The descriptive values
|
40
|
+
def descriptive_values_from_string(string, label: nil)
|
41
|
+
[DescriptiveValue.new(label: label, value: string)]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Take one or several DisplayData and merge into a single hash.
|
45
|
+
# Keys are labels; values are the merged array of values for that label.
|
46
|
+
# @param display_data [DisplayData, Array<DisplayData>]
|
47
|
+
# @return [Hash{String => Array<String>}] The merged hash
|
48
|
+
def to_hash(display_data)
|
49
|
+
Array(display_data).map(&:to_h).reduce(:merge)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Wrap Cocina nodes into {DescriptiveValue} so they are labelled.
|
55
|
+
# Uses +displayLabel+ from the node if present, otherwise uses the provided label.
|
56
|
+
# @param cocina [Array<Hash>]
|
57
|
+
# @param label [String]
|
58
|
+
# @return [Array<DescriptiveValue>]
|
59
|
+
def descriptive_values_from_cocina(cocina, label: nil)
|
60
|
+
cocina.map { |node| DescriptiveValue.new(label: node["displayLabel"] || label, value: node["value"]) }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Wrapper to make Cocina descriptive values respond to #to_s and #label.
|
64
|
+
# @attr [String] label
|
65
|
+
# @attr [String] value
|
66
|
+
DescriptiveValue = Data.define(:label, :value) do
|
67
|
+
def to_s
|
68
|
+
value
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Create a DisplayData object from a list of objects that share a label
|
74
|
+
# @param label [String]
|
75
|
+
# @param objects [Array<Object>]
|
76
|
+
def initialize(label:, objects:)
|
77
|
+
@label = label
|
78
|
+
@objects = objects
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_reader :label, :objects
|
82
|
+
|
83
|
+
# The unique, non-blank values for display
|
84
|
+
# @return [Array<String>]
|
85
|
+
def values
|
86
|
+
objects.flat_map { |object| split_string_on_newlines(object.to_s) }.compact_blank.uniq
|
87
|
+
end
|
88
|
+
|
89
|
+
# Express the display data as a hash mapping the label to its values.
|
90
|
+
# @return [Hash{String => Array<String>}] The label and values
|
91
|
+
def to_h
|
92
|
+
{label => values}
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Split a string on newlines (including HTML-encoded newlines) and strip whitespace.
|
98
|
+
# @param string [String] The string to split
|
99
|
+
# @return [Array<String>]
|
100
|
+
def split_string_on_newlines(string)
|
101
|
+
string&.gsub(" ", "\n")&.split("\n")&.map(&:strip)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -1,7 +1,3 @@
|
|
1
|
-
require_relative "location"
|
2
|
-
require_relative "../dates/date"
|
3
|
-
require_relative "../contributors/contributor"
|
4
|
-
|
5
1
|
module CocinaDisplay
|
6
2
|
module Events
|
7
3
|
# An event associated with an object, like publication.
|
@@ -73,6 +69,14 @@ module CocinaDisplay
|
|
73
69
|
CocinaDisplay::Events::Location.new(location)
|
74
70
|
end
|
75
71
|
end
|
72
|
+
|
73
|
+
# All notes associated with this event.
|
74
|
+
# @return [Array<CocinaDisplay::Events::Note>]
|
75
|
+
def notes
|
76
|
+
@notes ||= Array(cocina["note"]).map do |note|
|
77
|
+
CocinaDisplay::Events::Note.new(note)
|
78
|
+
end
|
79
|
+
end
|
76
80
|
end
|
77
81
|
end
|
78
82
|
end
|
@@ -1,15 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "edtf"
|
4
|
-
require "active_support"
|
5
|
-
require "active_support/core_ext/enumerable"
|
6
|
-
require "active_support/core_ext/object/blank"
|
7
|
-
|
8
|
-
require_relative "event"
|
9
|
-
require_relative "../utils"
|
10
|
-
require_relative "../dates/date"
|
11
|
-
require_relative "../dates/date_range"
|
12
|
-
|
13
3
|
module CocinaDisplay
|
14
4
|
module Events
|
15
5
|
# Wrapper for Cocina events used to generate an imprint statement for display.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module CocinaDisplay
|
2
|
+
module Events
|
3
|
+
# A single note represented in a Cocina event, like an issuance or edition note.
|
4
|
+
class Note
|
5
|
+
attr_reader :cocina
|
6
|
+
|
7
|
+
# Initialize a Note object with Cocina structured data.
|
8
|
+
# @param cocina [Hash] The Cocina structured data for the note.
|
9
|
+
def initialize(cocina)
|
10
|
+
@cocina = cocina
|
11
|
+
end
|
12
|
+
|
13
|
+
# The value of the note.
|
14
|
+
# @return [String, nil]
|
15
|
+
def to_s
|
16
|
+
cocina["value"].presence
|
17
|
+
end
|
18
|
+
|
19
|
+
# The type of the note, like "issuance" or "edition".
|
20
|
+
# @return [String, nil]
|
21
|
+
def type
|
22
|
+
cocina["type"].presence
|
23
|
+
end
|
24
|
+
|
25
|
+
# The display label for the note.
|
26
|
+
# @return [String]
|
27
|
+
def label
|
28
|
+
cocina["displayLabel"].presence ||
|
29
|
+
I18n.t(type&.parameterize&.underscore, default: :default, scope: "cocina_display.field_label.event.note")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CocinaDisplay
|
4
|
+
# Classes for extracting format/genre information from a Cocina object.
|
5
|
+
module Forms
|
6
|
+
# A form associated with part or all of a Cocina object.
|
7
|
+
class Form
|
8
|
+
attr_reader :cocina
|
9
|
+
|
10
|
+
# Create a Form object from Cocina structured data.
|
11
|
+
# Delegates to subclasses for specific types.
|
12
|
+
# @param cocina [Hash]
|
13
|
+
# @return [Form]
|
14
|
+
def self.from_cocina(cocina)
|
15
|
+
case cocina["type"]
|
16
|
+
when "genre"
|
17
|
+
Genre.new(cocina)
|
18
|
+
when "resource type"
|
19
|
+
ResourceType.new(cocina)
|
20
|
+
else
|
21
|
+
new(cocina)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create a Form object from Cocina structured data.
|
26
|
+
# @param cocina [Hash]
|
27
|
+
def initialize(cocina)
|
28
|
+
@cocina = cocina
|
29
|
+
end
|
30
|
+
|
31
|
+
# The value to use for display.
|
32
|
+
# @return [String]
|
33
|
+
def to_s
|
34
|
+
flat_value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Single concatenated string value for the form.
|
38
|
+
# @return [String]
|
39
|
+
def flat_value
|
40
|
+
Utils.compact_and_join(values, delimiter: " > ")
|
41
|
+
end
|
42
|
+
|
43
|
+
# The raw values from the Cocina data, flattened if nested.
|
44
|
+
# @return [String]
|
45
|
+
def values
|
46
|
+
Utils.flatten_nested_values(cocina).pluck("value").compact_blank
|
47
|
+
end
|
48
|
+
|
49
|
+
# The label to use for display.
|
50
|
+
# Uses a displayLabel if available, otherwise looks up via type.
|
51
|
+
# @return [String]
|
52
|
+
def label
|
53
|
+
cocina["displayLabel"].presence || type_label
|
54
|
+
end
|
55
|
+
|
56
|
+
# The type of form, such as "genre", "extent", etc.
|
57
|
+
# @return [String, nil]
|
58
|
+
def type
|
59
|
+
cocina["type"]
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Type-specific label for this form value.
|
65
|
+
# @return [String]
|
66
|
+
def type_label
|
67
|
+
I18n.t(type&.parameterize&.underscore, default: :form, scope: "cocina_display.field_label.form")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module CocinaDisplay
|
2
|
+
module Forms
|
3
|
+
# A Resource Type form associated with part or all of a Cocina object.
|
4
|
+
class ResourceType < Form
|
5
|
+
# Resource types are lowercased for display.
|
6
|
+
# @return [String]
|
7
|
+
def to_s
|
8
|
+
super&.downcase
|
9
|
+
end
|
10
|
+
|
11
|
+
# Is this a Stanford self-deposit resource type?
|
12
|
+
# @note These are handled separately when displayed.
|
13
|
+
# @return [Boolean]
|
14
|
+
def stanford_self_deposit?
|
15
|
+
source == "Stanford self-deposit resource types"
|
16
|
+
end
|
17
|
+
|
18
|
+
# Is this a MODS resource type?
|
19
|
+
# @return [Boolean]
|
20
|
+
def mods?
|
21
|
+
source == "MODS resource types"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# @return [String]
|
27
|
+
def source
|
28
|
+
cocina.dig("source", "value")
|
29
|
+
end
|
30
|
+
|
31
|
+
# Stanford self-deposit resource types are labeled "Genre".
|
32
|
+
# @return [String]
|
33
|
+
def type_label
|
34
|
+
(I18n.t("cocina_display.field_label.form.genre") if stanford_self_deposit?) || super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|