cocina_display 1.0.0 → 1.1.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/lib/cocina_display/cocina_record.rb +26 -12
- data/lib/cocina_display/concerns/access.rb +29 -27
- data/lib/cocina_display/concerns/identifiers.rb +8 -5
- data/lib/cocina_display/concerns/structural.rb +41 -0
- data/lib/cocina_display/contributors/contributor.rb +20 -0
- data/lib/cocina_display/contributors/name.rb +11 -11
- data/lib/cocina_display/contributors/role.rb +7 -1
- data/lib/cocina_display/title_builder.rb +2 -0
- data/lib/cocina_display/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2b164eda474dc396b74c8b911fe50bbba7c2dbbce17b29c24923db4f25ff8a5
|
4
|
+
data.tar.gz: c782186b53a4dcb7f53b11a3d552dcf5f892f4435fc46fb4bcddd65ad444d9be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: efed0c780617aaeadb7187391c74691bcacddcb24cb2850e14918ab755acfaef273c8e3277a9c4674b2b41201a72ffe91a79a0065daa07ad469e2178ea17fd92
|
7
|
+
data.tar.gz: 28933bab6be3f6cd2fed4155d5b4a73bf9f9d9301e5edc679018f6428d90f88ca14ab8a6614d1c07e53f1723644b0871786fb075b664bb7e2fb73dba96d21588
|
@@ -16,6 +16,7 @@ require_relative "concerns/subjects"
|
|
16
16
|
require_relative "concerns/forms"
|
17
17
|
require_relative "concerns/languages"
|
18
18
|
require_relative "concerns/geospatial"
|
19
|
+
require_relative "concerns/structural"
|
19
20
|
require_relative "utils"
|
20
21
|
|
21
22
|
module CocinaDisplay
|
@@ -30,6 +31,7 @@ module CocinaDisplay
|
|
30
31
|
include CocinaDisplay::Concerns::Forms
|
31
32
|
include CocinaDisplay::Concerns::Languages
|
32
33
|
include CocinaDisplay::Concerns::Geospatial
|
34
|
+
include CocinaDisplay::Concerns::Structural
|
33
35
|
|
34
36
|
# Fetch a public Cocina document from PURL and create a CocinaRecord.
|
35
37
|
# @note This is intended to be used in development or testing only.
|
@@ -88,12 +90,13 @@ module CocinaDisplay
|
|
88
90
|
end
|
89
91
|
|
90
92
|
# SDR content type of the object.
|
91
|
-
# @
|
93
|
+
# @note {RelatedResource}s may not have a content type.
|
94
|
+
# @return [String, nil]
|
92
95
|
# @see https://github.com/sul-dlss/cocina-models/blob/main/openapi.yml#L532-L546
|
93
96
|
# @example
|
94
97
|
# record.content_type #=> "image"
|
95
98
|
def content_type
|
96
|
-
cocina_doc["type"]
|
99
|
+
cocina_doc["type"]&.split("/")&.last
|
97
100
|
end
|
98
101
|
|
99
102
|
# Primary processing label for the object.
|
@@ -109,16 +112,27 @@ module CocinaDisplay
|
|
109
112
|
content_type == "collection"
|
110
113
|
end
|
111
114
|
|
112
|
-
#
|
113
|
-
#
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
115
|
+
# Resources related to the object.
|
116
|
+
# @return [Array<CocinaDisplay::RelatedResource>]
|
117
|
+
def related_resources
|
118
|
+
@related_resources ||= path("$.description.relatedResource[*]").map { |res| RelatedResource.new(res) }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# A resource related to the record; behaves like a CocinaRecord.
|
123
|
+
# @note Related resources have no structural metadata.
|
124
|
+
class RelatedResource < CocinaRecord
|
125
|
+
# Description of the relation to the source record.
|
126
|
+
# @return [String]
|
127
|
+
# @example "is part of"
|
128
|
+
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#relatedresource-types
|
129
|
+
attr_reader :type
|
130
|
+
|
131
|
+
# Restructure the hash so that everything is under "description" key, since
|
132
|
+
# it's all descriptive metadata. This makes most CocinaRecord methods work.
|
133
|
+
def initialize(cocina_doc)
|
134
|
+
@type = cocina_doc["type"]
|
135
|
+
@cocina_doc = {"description" => cocina_doc.except("type")}
|
122
136
|
end
|
123
137
|
end
|
124
138
|
end
|
@@ -7,30 +7,7 @@ module CocinaDisplay
|
|
7
7
|
# @example
|
8
8
|
# record.purl_url #=> "https://purl.stanford.edu/bx658jh7339"
|
9
9
|
def purl_url
|
10
|
-
cocina_doc.dig("description", "purl") || "https://purl.stanford.edu/#{bare_druid}"
|
11
|
-
end
|
12
|
-
|
13
|
-
# The URL to the PURL environment this object is from.
|
14
|
-
# @note Objects accessed via UAT will still have a production PURL base URL.
|
15
|
-
# @return [String]
|
16
|
-
# @example
|
17
|
-
# record.purl_base_url #=> "https://purl.stanford.edu"
|
18
|
-
def purl_base_url
|
19
|
-
URI(purl_url).origin
|
20
|
-
end
|
21
|
-
|
22
|
-
# The URL to the stacks environment this object is shelved in.
|
23
|
-
# Corresponds to the PURL environment.
|
24
|
-
# @see purl_base_url
|
25
|
-
# @return [String]
|
26
|
-
# @example
|
27
|
-
# record.stacks_base_url #=> "https://stacks.stanford.edu"
|
28
|
-
def stacks_base_url
|
29
|
-
if purl_base_url == "https://sul-purl-stage.stanford.edu"
|
30
|
-
"https://sul-stacks-stage.stanford.edu"
|
31
|
-
else
|
32
|
-
"https://stacks.stanford.edu"
|
33
|
-
end
|
10
|
+
cocina_doc.dig("description", "purl") || ("https://purl.stanford.edu/#{bare_druid}" if bare_druid)
|
34
11
|
end
|
35
12
|
|
36
13
|
# The oEmbed URL for the object, optionally with additional parameters.
|
@@ -41,7 +18,7 @@ module CocinaDisplay
|
|
41
18
|
# @example Generate an oEmbed URL for the viewer and hide the title
|
42
19
|
# record.oembed_url(hide_title: true) #=> "https://purl.stanford.edu/bx658jh7339/embed.json?hide_title=true"
|
43
20
|
def oembed_url(params: {})
|
44
|
-
return if collection?
|
21
|
+
return if collection? || purl_url.blank?
|
45
22
|
|
46
23
|
params[:url] ||= purl_url
|
47
24
|
"#{purl_base_url}/embed.json?#{params.to_query}"
|
@@ -53,7 +30,7 @@ module CocinaDisplay
|
|
53
30
|
# @example
|
54
31
|
# record.download_url #=> "https://stacks.stanford.edu/object/bx658jh7339"
|
55
32
|
def download_url
|
56
|
-
"#{stacks_base_url}/object/#{bare_druid}"
|
33
|
+
"#{stacks_base_url}/object/#{bare_druid}" if bare_druid.present?
|
57
34
|
end
|
58
35
|
|
59
36
|
# The IIIF manifest URL for the object.
|
@@ -64,7 +41,32 @@ module CocinaDisplay
|
|
64
41
|
# record.iiif_manifest_url #=> "https://purl.stanford.edu/bx658jh7339/iiif3/manifest"
|
65
42
|
def iiif_manifest_url(version: 3)
|
66
43
|
iiif_path = (version == 3) ? "iiif3" : "iiif"
|
67
|
-
"#{purl_url}/#{iiif_path}/manifest"
|
44
|
+
"#{purl_url}/#{iiif_path}/manifest" if purl_url.present?
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# The URL to the PURL environment this object is from.
|
50
|
+
# @note Objects accessed via UAT will still have a production PURL base URL.
|
51
|
+
# @return [String]
|
52
|
+
# @example
|
53
|
+
# record.purl_base_url #=> "https://purl.stanford.edu"
|
54
|
+
def purl_base_url
|
55
|
+
URI(purl_url).origin if purl_url.present?
|
56
|
+
end
|
57
|
+
|
58
|
+
# The URL to the stacks environment this object is shelved in.
|
59
|
+
# Corresponds to the PURL environment.
|
60
|
+
# @see purl_base_url
|
61
|
+
# @return [String]
|
62
|
+
# @example
|
63
|
+
# record.stacks_base_url #=> "https://stacks.stanford.edu"
|
64
|
+
def stacks_base_url
|
65
|
+
if purl_base_url == "https://sul-purl-stage.stanford.edu"
|
66
|
+
"https://sul-stacks-stage.stanford.edu"
|
67
|
+
elsif purl_base_url.present?
|
68
|
+
"https://stacks.stanford.edu"
|
69
|
+
end
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
@@ -3,19 +3,22 @@ module CocinaDisplay
|
|
3
3
|
# Methods for extracting and formatting identifiers from Cocina records.
|
4
4
|
module Identifiers
|
5
5
|
# The DRUID for the object, with the +druid:+ prefix.
|
6
|
-
# @
|
6
|
+
# @note A {RelatedResource} may not have a DRUID.
|
7
|
+
# @return [String, nil]
|
7
8
|
# @example
|
8
9
|
# record.druid #=> "druid:bb099mt5053"
|
9
10
|
def druid
|
10
|
-
cocina_doc["externalIdentifier"]
|
11
|
+
cocina_doc["externalIdentifier"] ||
|
12
|
+
cocina_doc.dig("description", "purl")&.split("/")&.last
|
11
13
|
end
|
12
14
|
|
13
15
|
# The DRUID for the object, without the +druid:+ prefix.
|
14
|
-
# @
|
16
|
+
# @note A {RelatedResource} may not have a DRUID.
|
17
|
+
# @return [String, nil]
|
15
18
|
# @example
|
16
19
|
# record.bare_druid #=> "bb099mt5053"
|
17
20
|
def bare_druid
|
18
|
-
druid
|
21
|
+
druid&.delete_prefix("druid:")
|
19
22
|
end
|
20
23
|
|
21
24
|
# The DOI for the object, if there is one – just the identifier part.
|
@@ -59,7 +62,7 @@ module CocinaDisplay
|
|
59
62
|
# @note This doesn't imply the object is available in Searchworks at this ID.
|
60
63
|
# @see folio_hrid
|
61
64
|
# @see bare_druid
|
62
|
-
# @return [String]
|
65
|
+
# @return [String, nil]
|
63
66
|
def searchworks_id
|
64
67
|
folio_hrid || bare_druid
|
65
68
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "active_support/number_helper/number_to_human_size_converter"
|
2
|
+
|
3
|
+
module CocinaDisplay
|
4
|
+
module Concerns
|
5
|
+
# Methods for inspecting structural metadata (e.g. file hierarchy)
|
6
|
+
module Structural
|
7
|
+
# Structured data for all individual files in the object.
|
8
|
+
# Traverses nested FileSet structure to return a flattened array.
|
9
|
+
# @return [Array<Hash>]
|
10
|
+
# @example
|
11
|
+
# record.files.each do |file|
|
12
|
+
# puts file["filename"] #=> "image1.jpg"
|
13
|
+
# puts file["size"] #=> 123456
|
14
|
+
# end
|
15
|
+
def files
|
16
|
+
@files ||= path("$.structural.contains.*.structural.contains.*").search
|
17
|
+
end
|
18
|
+
|
19
|
+
# All unique MIME types of files in this object.
|
20
|
+
# @return [Array<String>]
|
21
|
+
# @example ["image/jpeg", "application/pdf"]
|
22
|
+
def file_mime_types
|
23
|
+
files.pluck("hasMimeType").uniq
|
24
|
+
end
|
25
|
+
|
26
|
+
# Human-readable string representation of {total_file_size_int}.
|
27
|
+
# @return [String]
|
28
|
+
# @example "2.5 MB"
|
29
|
+
def total_file_size_str
|
30
|
+
ActiveSupport::NumberHelper.number_to_human_size(total_file_size_int)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Summed size of all files in bytes.
|
34
|
+
# @return [Integer]
|
35
|
+
# @example 2621440
|
36
|
+
def total_file_size_int
|
37
|
+
files.pluck("size").sum
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -63,6 +63,12 @@ module CocinaDisplay
|
|
63
63
|
roles.any?(&:publisher?)
|
64
64
|
end
|
65
65
|
|
66
|
+
# Does this contributor have a role that indicates they are a funder?
|
67
|
+
# @return [Boolean]
|
68
|
+
def funder?
|
69
|
+
roles.any?(&:funder?)
|
70
|
+
end
|
71
|
+
|
66
72
|
# Does this contributor have any roles defined?
|
67
73
|
# @return [Boolean]
|
68
74
|
def role?
|
@@ -77,6 +83,20 @@ module CocinaDisplay
|
|
77
83
|
names.map { |name| name.to_s(with_date: with_date) }.first
|
78
84
|
end
|
79
85
|
|
86
|
+
# The full forename for the contributor from the first available name.
|
87
|
+
# @see Contributor::Name::forename_str
|
88
|
+
# @return [String, nil]
|
89
|
+
def forename
|
90
|
+
names.map(&:forename_str).first.presence
|
91
|
+
end
|
92
|
+
|
93
|
+
# The full surname for the contributor from the first available name.
|
94
|
+
# @see Contributor::Name::surname_str
|
95
|
+
# @return [String, nil]
|
96
|
+
def surname
|
97
|
+
names.map(&:surname_str).first.presence
|
98
|
+
end
|
99
|
+
|
80
100
|
# A string representation of the contributor's roles, formatted for display.
|
81
101
|
# If there are multiple roles, they are joined with commas.
|
82
102
|
# @return [String]
|
@@ -37,8 +37,6 @@ module CocinaDisplay
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
private
|
41
|
-
|
42
40
|
# The full name as a string, combining all name components and terms of address.
|
43
41
|
# @return [String]
|
44
42
|
def full_name_str
|
@@ -56,33 +54,35 @@ module CocinaDisplay
|
|
56
54
|
# Otherwise, fall back to any names explicitly marked as "name" or untyped.
|
57
55
|
# @return [Array<String>]
|
58
56
|
def name_components
|
59
|
-
[surname_str,
|
57
|
+
[surname_str, forename_str].compact_blank.presence || Array(name_values["name"])
|
60
58
|
end
|
61
59
|
|
62
|
-
# Flatten all
|
60
|
+
# Flatten all terms of address into a comma-delimited string.
|
63
61
|
# @return [String]
|
64
|
-
def
|
65
|
-
Utils.compact_and_join(Array(name_values["
|
62
|
+
def terms_of_address_str
|
63
|
+
Utils.compact_and_join(Array(name_values["term of address"]), delimiter: ", ")
|
66
64
|
end
|
67
65
|
|
68
|
-
# Flatten all
|
66
|
+
# Flatten all forename values and ordinals into a whitespace-delimited string.
|
69
67
|
# @return [String]
|
70
|
-
def
|
71
|
-
Utils.compact_and_join(Array(name_values["
|
68
|
+
def forename_str
|
69
|
+
Utils.compact_and_join(Array(name_values["forename"]) + Array(name_values["ordinal"]), delimiter: " ")
|
72
70
|
end
|
73
71
|
|
74
|
-
# Flatten all
|
72
|
+
# Flatten all surname values into a whitespace-delimited string.
|
75
73
|
# @return [String]
|
76
74
|
def surname_str
|
77
75
|
Utils.compact_and_join(Array(name_values["surname"]), delimiter: " ")
|
78
76
|
end
|
79
77
|
|
80
|
-
# Flatten all life and activity dates into a
|
78
|
+
# Flatten all life and activity dates into a comma-delimited string.
|
81
79
|
# @return [String]
|
82
80
|
def dates_str
|
83
81
|
Utils.compact_and_join(Array(name_values["life dates"]) + Array(name_values["activity dates"]), delimiter: ", ")
|
84
82
|
end
|
85
83
|
|
84
|
+
private
|
85
|
+
|
86
86
|
# A hash mapping destructured name types to their values.
|
87
87
|
# Name values with no type are grouped under "name".
|
88
88
|
# @return [Hash<String, Array<String>>]
|
@@ -30,7 +30,7 @@ module CocinaDisplay
|
|
30
30
|
# Does this role indicate the contributor is an author?
|
31
31
|
# @return [Boolean]
|
32
32
|
def author?
|
33
|
-
to_s =~ /^(author|creator)/i
|
33
|
+
to_s =~ /^(author|creator|primary investigator)/i
|
34
34
|
end
|
35
35
|
|
36
36
|
# Does this role indicate the contributor is a publisher?
|
@@ -39,6 +39,12 @@ module CocinaDisplay
|
|
39
39
|
to_s =~ /^publisher/i
|
40
40
|
end
|
41
41
|
|
42
|
+
# Does this role indicate the contributor is a funder?
|
43
|
+
# @return [Boolean]
|
44
|
+
def funder?
|
45
|
+
to_s =~ /^funder/i
|
46
|
+
end
|
47
|
+
|
42
48
|
private
|
43
49
|
|
44
50
|
# Does this role have a MARC relator code?
|
@@ -99,6 +99,8 @@ module CocinaDisplay
|
|
99
99
|
# this may be useful for boosting and exact matching for search results
|
100
100
|
# @return [Array<String>] the main title value(s) for Solr - can be array due to parallel titles
|
101
101
|
def main_title(titles)
|
102
|
+
return [] if titles.empty?
|
103
|
+
|
102
104
|
cocina_title = primary_title(titles) || untyped_title(titles)
|
103
105
|
cocina_title = other_title(titles) if cocina_title.blank?
|
104
106
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cocina_display
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Budak
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: janeway-jsonpath
|
@@ -219,6 +219,7 @@ files:
|
|
219
219
|
- lib/cocina_display/concerns/geospatial.rb
|
220
220
|
- lib/cocina_display/concerns/identifiers.rb
|
221
221
|
- lib/cocina_display/concerns/languages.rb
|
222
|
+
- lib/cocina_display/concerns/structural.rb
|
222
223
|
- lib/cocina_display/concerns/subjects.rb
|
223
224
|
- lib/cocina_display/concerns/titles.rb
|
224
225
|
- lib/cocina_display/contributors/contributor.rb
|