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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.standard.yml +1 -1
  4. data/README.md +21 -2
  5. data/config/i18n-tasks.yml +0 -0
  6. data/config/licenses.yml +59 -0
  7. data/config/locales/en.yml +109 -0
  8. data/lib/cocina_display/cocina_record.rb +27 -63
  9. data/lib/cocina_display/concerns/accesses.rb +78 -0
  10. data/lib/cocina_display/concerns/contributors.rb +32 -11
  11. data/lib/cocina_display/concerns/events.rb +19 -6
  12. data/lib/cocina_display/concerns/forms.rb +98 -11
  13. data/lib/cocina_display/concerns/geospatial.rb +9 -5
  14. data/lib/cocina_display/concerns/identifiers.rb +15 -4
  15. data/lib/cocina_display/concerns/languages.rb +6 -2
  16. data/lib/cocina_display/concerns/notes.rb +36 -0
  17. data/lib/cocina_display/concerns/related_resources.rb +20 -0
  18. data/lib/cocina_display/concerns/subjects.rb +25 -8
  19. data/lib/cocina_display/concerns/titles.rb +67 -25
  20. data/lib/cocina_display/concerns/{access.rb → url_helpers.rb} +3 -3
  21. data/lib/cocina_display/concerns.rb +6 -0
  22. data/lib/cocina_display/contributors/contributor.rb +47 -26
  23. data/lib/cocina_display/contributors/name.rb +18 -14
  24. data/lib/cocina_display/contributors/role.rb +20 -13
  25. data/lib/cocina_display/dates/date.rb +55 -14
  26. data/lib/cocina_display/dates/date_range.rb +0 -2
  27. data/lib/cocina_display/description/access.rb +41 -0
  28. data/lib/cocina_display/description/access_contact.rb +11 -0
  29. data/lib/cocina_display/description/url.rb +17 -0
  30. data/lib/cocina_display/display_data.rb +104 -0
  31. data/lib/cocina_display/events/event.rb +8 -4
  32. data/lib/cocina_display/events/imprint.rb +0 -10
  33. data/lib/cocina_display/events/location.rb +0 -2
  34. data/lib/cocina_display/events/note.rb +33 -0
  35. data/lib/cocina_display/forms/form.rb +71 -0
  36. data/lib/cocina_display/forms/genre.rb +12 -0
  37. data/lib/cocina_display/forms/resource_type.rb +38 -0
  38. data/lib/cocina_display/geospatial.rb +1 -1
  39. data/lib/cocina_display/identifier.rb +101 -0
  40. data/lib/cocina_display/json_backed_record.rb +27 -0
  41. data/lib/cocina_display/language.rb +9 -11
  42. data/lib/cocina_display/license.rb +32 -0
  43. data/lib/cocina_display/note.rb +103 -0
  44. data/lib/cocina_display/related_resource.rb +74 -0
  45. data/lib/cocina_display/subjects/subject.rb +32 -9
  46. data/lib/cocina_display/subjects/subject_value.rb +34 -16
  47. data/lib/cocina_display/title.rb +194 -0
  48. data/lib/cocina_display/utils.rb +4 -4
  49. data/lib/cocina_display/version.rb +1 -1
  50. data/lib/cocina_display.rb +30 -2
  51. metadata +45 -11
  52. data/lib/cocina_display/title_builder.rb +0 -397
  53. /data/lib/cocina_display/vocabularies/{marc_country_codes.rb → marc_country.rb} +0 -0
  54. /data/lib/cocina_display/vocabularies/{marc_relator_codes.rb → marc_relator.rb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d3e38973fd7ea8399ce60829b3d11bafaa551c0f87e2bc1ccb49f55463b2c1b9
4
- data.tar.gz: dd1398f2e2176979fdedfede55b9b09f46e42fffad1c7a16d2a4bcc9e9ef0229
3
+ metadata.gz: c0196b844ff2e2914b0216d0881f72d099772c73574cacbb2c2b8427d37910db
4
+ data.tar.gz: e1d9657e7868bcef2e20468998d118954d7ce3deefb9049a79b32f5cae54d3d2
5
5
  SHA512:
6
- metadata.gz: 39fd439e148f6eb1dc58fdec2752519083b627bd102eb8b1f0c40a74e539fdc2799aa6caed446f1142c16ffc59220bccc94f8a1fe7cc846786dc8832ba585310
7
- data.tar.gz: 7a6560028d3f0e75e90ac922fc15b83eeabf393f28eeca4b50a78462278dba9db5e9785a8c8ab4f93a3cabc70ab1f6cf6ce7047a5712f05dd5de8c06d3b2b7e4
6
+ metadata.gz: 5963e10cfa7d99df707b3c088d0142170fd3a7b45d16a19ea98b5bec7872e24e6299ee18477107e43fbaa92f3eaf7620b25479b04b4cf26e7288c7ded0cf14be
7
+ data.tar.gz: 0a0cc895e8c1bd18aff64fa437945d9d9ae172be32730f7cbbb863f244ea17e4ce70087ac5cfab5e090f2e138849d3d74cdb1c5e3ce262fc1c85d9dcb920e018
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
- --format documentation
2
1
  --color
3
2
  --require spec_helper
data/.standard.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  # For available configuration options, see:
2
2
  # https://github.com/standardrb/standard
3
- ruby_version: 3.1
3
+ ruby_version: 3.3
data/README.md CHANGED
@@ -44,10 +44,10 @@ The `CocinaRecord` class provides some methods to access common fields, as well
44
44
  => "Bugatti Type 51A. Road & Track Salon January 1957"
45
45
  > record.content_type
46
46
  => "image"
47
- > record.iiif_manifest_url
47
+ > record.iiif_manifest_url
48
48
  => "https://purl.stanford.edu/bb112zx3193/iiif3/manifest"
49
49
  # access the hash representation
50
- > record.cocina_doc.dig("description", "contributor", 0, "name", 0, "value")
50
+ > record.cocina_doc.dig("description", "contributor", 0, "name", 0, "value")
51
51
  => "Hearst Magazines, Inc."
52
52
  ```
53
53
 
@@ -92,6 +92,25 @@ cat spec/fixtures/bb112zx3193.json | janeway "$.description.contributor[?@.role[
92
92
 
93
93
  Sometimes you need to determine if records exist "in the wild" that exhibit particular characteristics in the Cocina metadata, like the presence or absence of a field, or a specific value in a field. There is a template script in the `scripts/` directory that can be used to crawl all DRUIDs released to a particular target, like Searchworks, and examine each record.
94
94
 
95
+ Another approach is to examine the records in the `/stacks` share using jq. For example:
96
+
97
+ ```shell
98
+ find /stacks -name cocina.json | head -10000 |
99
+ xargs jq '.description.subject[] | select(.source.uri=="http://id.loc.gov/authorities/subjects/") | select(.value != null) | select(.value | contains("--")) | .value'
100
+ ```
101
+
102
+ ### Logging of errors
103
+
104
+ You may create a custom error handler by implementing the `Honeybadger` interface (or just using Honeybadger) and assigning it to the `CocinaRecord.notifier`.
105
+
106
+ For example:
107
+ ```ruby
108
+ Rails.application.config.to_prepare do
109
+ CocinaDisplay.notifier = Honeybadger
110
+ end
111
+ ```
112
+
113
+
95
114
  ## Development
96
115
 
97
116
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. This is a useful place to try out JSONPath expressions with `CocinaRecord#path`.
File without changes
@@ -0,0 +1,59 @@
1
+ https://www.apache.org/licenses/LICENSE-2.0:
2
+ description: "This work is licensed under an Apache License 2.0."
3
+ https://opensource.org/licenses/BSD-2-Clause:
4
+ description: 'This work is licensed under a BSD 2-Clause "Simplified" License.'
5
+ https://opensource.org/licenses/BSD-3-Clause:
6
+ description: 'This work is licensed under a BSD 3-Clause "New" or "Revised" License.'
7
+ https://creativecommons.org/licenses/by/3.0/legalcode:
8
+ description: "This work is licensed under a Creative Commons Attribution 3.0 Unported license (CC BY)."
9
+ https://creativecommons.org/licenses/by/4.0/legalcode:
10
+ description: "This work is licensed under a Creative Commons Attribution 4.0 International license (CC BY)."
11
+ https://creativecommons.org/licenses/by-nc/3.0/legalcode:
12
+ description: "This work is licensed under a Creative Commons Attribution Non Commercial 3.0 Unported license (CC BY-NC)."
13
+ https://creativecommons.org/licenses/by-nc/4.0/legalcode:
14
+ description: "This work is licensed under a Creative Commons Attribution Non Commercial 4.0 International license (CC BY-NC)."
15
+ https://creativecommons.org/licenses/by-nc-nd/3.0/legalcode:
16
+ description: "This work is licensed under a Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported license (CC BY-NC-ND)."
17
+ https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode:
18
+ description: "This work is licensed under a Creative Commons Attribution Non Commercial No Derivatives 4.0 International license (CC BY-NC-ND)."
19
+ https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode:
20
+ description: "This work is licensed under a Creative Commons Attribution Non Commercial Share Alike 3.0 Unported license (CC BY-NC-SA)."
21
+ https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode:
22
+ description: "This work is licensed under a Creative Commons Attribution Non Commercial Share Alike 4.0 International license (CC BY-NC-SA)."
23
+ https://creativecommons.org/licenses/by-nd/4.0/legalcode:
24
+ description: "This work is licensed under a Creative Commons Attribution No Derivatives 4.0 International license (CC BY-ND)."
25
+ https://creativecommons.org/licenses/by-sa/3.0/legalcode:
26
+ description: "This work is licensed under a Creative Commons Attribution Share Alike 3.0 Unported license (CC BY-SA)."
27
+ https://creativecommons.org/licenses/by-sa/4.0/legalcode:
28
+ description: "This work is licensed under a Creative Commons Attribution Share Alike 4.0 International license (CC BY-SA)."
29
+ https://creativecommons.org/licenses/by-nd/3.0/legalcode:
30
+ description: "This work is licensed under a Creative Commons Attribution No Derivatives 3.0 Unported license (CC BY-ND)."
31
+ https://creativecommons.org/publicdomain/zero/1.0/legalcode:
32
+ description: "This work is licensed under a Creative Commons Zero v1.0 Universal license (CC0)."
33
+ https://opensource.org/licenses/cddl1:
34
+ description: "This work is licensed under a Common Development and Distribution License 1.0."
35
+ https://www.eclipse.org/legal/epl-2.0:
36
+ description: "This work is licensed under an Eclipse Public License 2.0."
37
+ https://www.gnu.org/licenses/agpl.txt:
38
+ description: "This work is licensed under a GNU Affero General Public License v3.0 only."
39
+ https://www.gnu.org/licenses/gpl-3.0-standalone.html:
40
+ description: "This work is licensed under a GNU General Public License v3.0 only."
41
+ https://www.gnu.org/licenses/lgpl-3.0-standalone.html:
42
+ description: "This work is licensed under a GNU Lesser General Public License v3.0 only."
43
+ https://www.isc.org/downloads/software-support-policy/isc-license/:
44
+ description: "This work is licensed under an ISC License."
45
+ https://opensource.org/licenses/MIT:
46
+ description: "This work is licensed under an MIT License."
47
+ https://www.mozilla.org/MPL/2.0/:
48
+ description: "This work is licensed under a Mozilla Public License 2.0."
49
+ https://opendatacommons.org/licenses/by/1-0/:
50
+ description: "This work is licensed under an Open Data Commons Attribution License v1.0."
51
+ http://opendatacommons.org/licenses/odbl/1.0/:
52
+ # This is a non-canonical url found in some existing data. It redirects to # https://opendatacommons.org/licenses/odbl/1-0/
53
+ description: "This work is licensed under an Open Data Commons Open Database License v1.0."
54
+ https://opendatacommons.org/licenses/odbl/1-0/:
55
+ description: "This work is licensed under an Open Data Commons Open Database License v1.0."
56
+ https://opendatacommons.org/licenses/pddl/1-0/:
57
+ description: "This work is licensed under an Open Data Commons Public Domain Dedication & License 1.0."
58
+ https://creativecommons.org/publicdomain/mark/1.0/:
59
+ description: "This work has been identified as being free of known restrictions under copyright law, including all related and neighboring rights (Public Domain Mark 1.0)."
@@ -0,0 +1,109 @@
1
+ ---
2
+ en:
3
+ cocina_display:
4
+ field_label:
5
+ access:
6
+ access: Location
7
+ email: Contact information
8
+ repository: Repository
9
+ url: Location
10
+ copyright: Copyright
11
+ event:
12
+ date:
13
+ default: "%{type} date"
14
+ untyped: Date
15
+ note:
16
+ default: Imprint note
17
+ edition: Edition
18
+ issuance: Issuance
19
+ form:
20
+ digital_origin: Digital origin
21
+ extent: Extent
22
+ form: Form
23
+ genre: Genre
24
+ map_projection: Map data
25
+ map_scale: Map data
26
+ note: Note
27
+ resource_type: Resource Type
28
+ identifier:
29
+ doi: DOI
30
+ handle: Handle
31
+ identifier: Identifier
32
+ isbn: ISBN
33
+ ismn: ISMN
34
+ isrc: ISRC
35
+ issn: ISSN
36
+ issn_l: ISSN
37
+ issue_number: Issue number
38
+ language: Language
39
+ lccn: LCCN
40
+ matrix_number: Matrix number
41
+ music_plate: Music plate
42
+ music_publisher: Music publisher
43
+ oclc: OCLC
44
+ orcid: ORCID
45
+ sici: SICI
46
+ stock_number: Stock number
47
+ upc: UPC
48
+ videorecording_identifier: Videorecording identifier
49
+ language: Language
50
+ license: License
51
+ note:
52
+ abstract: Abstract
53
+ bibliography: Bibliography
54
+ biographical_historical: Biographical/Historical
55
+ citation_reference: Citation/Reference
56
+ creation_production_credits: Creation/Production credits
57
+ date_sequential_designation: Date/Sequential designation
58
+ note: Note
59
+ preferred_citation: Preferred citation
60
+ publications: Publications
61
+ references: References
62
+ scope_and_content: Scope and content
63
+ statement_of_responsibility: Statement of responsibility
64
+ summary: Summary
65
+ table_of_contents: Table of contents
66
+ purl: Location
67
+ subject:
68
+ bounding_box_coordinates: Map data
69
+ coverage: Map data
70
+ genre: Genre
71
+ map_coordinates: Map data
72
+ point_coordinates: Map data
73
+ subject: Subject
74
+ topic: Subject
75
+ use_and_reproduction: Use and reproduction statement
76
+ related_resource:
77
+ described_by: Described by
78
+ describes: Describes
79
+ derived_from: Derived from
80
+ has_original_version: Has original version
81
+ has_other_format: Has other format
82
+ has_part: Contains
83
+ has_version: Has version
84
+ identical_to: Identical to
85
+ in_series: In series
86
+ other_relation_type: Related to
87
+ part_of: Part of
88
+ preceded_by: Preceded by
89
+ referenced_by: Referenced by
90
+ references: References
91
+ related_to: Related item
92
+ reviewed_by: Reviewed by
93
+ source_of: Source of
94
+ succeeded_by: Succeeded by
95
+ supplemented_by: Supplemented by
96
+ supplement_to: Supplement to
97
+ version_of_record: Version of record
98
+ title:
99
+ title: Title
100
+ abbreviated: Abbreviated title
101
+ alternative: Alternative title
102
+ parallel: Parallel title
103
+ supplied: Supplied title
104
+ translated: Translated title
105
+ transliterated: Transliterated title
106
+ uniform: Uniform title
107
+ contributor:
108
+ role:
109
+ default: Associated with
@@ -1,37 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "janeway"
4
- require "json"
5
- require "net/http"
6
- require "active_support"
7
- require "active_support/core_ext/object/blank"
8
- require "active_support/core_ext/hash/conversions"
9
-
10
- require_relative "concerns/events"
11
- require_relative "concerns/contributors"
12
- require_relative "concerns/identifiers"
13
- require_relative "concerns/titles"
14
- require_relative "concerns/access"
15
- require_relative "concerns/subjects"
16
- require_relative "concerns/forms"
17
- require_relative "concerns/languages"
18
- require_relative "concerns/geospatial"
19
- require_relative "concerns/structural"
20
- require_relative "utils"
21
-
22
3
  module CocinaDisplay
23
4
  # Public Cocina metadata for an SDR object, as fetched from PURL.
24
- class CocinaRecord
5
+ class CocinaRecord < JsonBackedRecord
6
+ include CocinaDisplay::Concerns::Accesses
25
7
  include CocinaDisplay::Concerns::Events
26
8
  include CocinaDisplay::Concerns::Contributors
27
9
  include CocinaDisplay::Concerns::Identifiers
10
+ include CocinaDisplay::Concerns::Notes
28
11
  include CocinaDisplay::Concerns::Titles
29
- include CocinaDisplay::Concerns::Access
12
+ include CocinaDisplay::Concerns::UrlHelpers
30
13
  include CocinaDisplay::Concerns::Subjects
31
14
  include CocinaDisplay::Concerns::Forms
32
15
  include CocinaDisplay::Concerns::Languages
33
16
  include CocinaDisplay::Concerns::Geospatial
34
17
  include CocinaDisplay::Concerns::Structural
18
+ include CocinaDisplay::Concerns::RelatedResources
35
19
 
36
20
  # Fetch a public Cocina document from PURL and create a CocinaRecord.
37
21
  # @note This is intended to be used in development or testing only.
@@ -39,7 +23,7 @@ module CocinaDisplay
39
23
  # @param deep_compact [Boolean] If true, compact the JSON to remove blank values.
40
24
  # @return [CocinaDisplay::CocinaRecord]
41
25
  # :nocov:
42
- def self.fetch(druid, deep_compact: false)
26
+ def self.fetch(druid, deep_compact: true)
43
27
  from_json(Net::HTTP.get(URI("https://purl.stanford.edu/#{druid}.json")), deep_compact: deep_compact)
44
28
  end
45
29
  # :nocov:
@@ -53,28 +37,6 @@ module CocinaDisplay
53
37
  deep_compact ? new(Utils.deep_compact_blank(cocina_doc)) : new(cocina_doc)
54
38
  end
55
39
 
56
- # The parsed Cocina document.
57
- # @return [Hash]
58
- attr_reader :cocina_doc
59
-
60
- # Initialize a CocinaRecord with a Cocina document hash.
61
- # @param cocina_doc [Hash]
62
- def initialize(cocina_doc)
63
- @cocina_doc = cocina_doc
64
- end
65
-
66
- # Evaluate a JSONPath expression against the Cocina document.
67
- # @return [Enumerator] An enumerator that yields results matching the expression.
68
- # @param path_expression [String] The JSONPath expression to evaluate.
69
- # @see https://www.rubydoc.info/gems/janeway-jsonpath/0.6.0/file/README.md
70
- # @example Name values for contributors
71
- # record.path("$.description.contributor.*.name.*.value").search #=> ["Smith, John", "ACME Corp."]
72
- # @example Filtering nodes using a condition
73
- # record.path("$.description.contributor[?(@.type == 'person')].name.*.value").search #=> ["Smith, John"]
74
- def path(path_expression)
75
- Janeway.enum_for(path_expression, cocina_doc)
76
- end
77
-
78
40
  # Timestamp when the Cocina was created.
79
41
  # @note This is for the metadata itself, not the object.
80
42
  # @return [Time]
@@ -96,7 +58,7 @@ module CocinaDisplay
96
58
  # @example
97
59
  # record.content_type #=> "image"
98
60
  def content_type
99
- cocina_doc["type"]&.split("/")&.last
61
+ cocina_doc["type"].delete_prefix("https://cocina.sul.stanford.edu/models/")
100
62
  end
101
63
 
102
64
  # Primary processing label for the object.
@@ -112,27 +74,29 @@ module CocinaDisplay
112
74
  content_type == "collection"
113
75
  end
114
76
 
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) }
77
+ # Copyright statement from Cocina access metadata.
78
+ # @return [String, nil]
79
+ def copyright
80
+ cocina_doc.dig("access", "copyright")
119
81
  end
120
- end
121
82
 
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
83
+ # Use and reproduction statement from Cocina access metadata.
84
+ # @return [String, nil]
85
+ def use_and_reproduction
86
+ cocina_doc.dig("access", "useAndReproductionStatement")
87
+ end
130
88
 
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")}
89
+ # Description of the license
90
+ # @return [String, nil]
91
+ def license_description
92
+ @license_description ||=
93
+ license ? License.new(url: license).description : nil
94
+ end
95
+
96
+ # License URI
97
+ # @return [String, nil]
98
+ def license
99
+ cocina_doc.dig("access", "license")
136
100
  end
137
101
  end
138
102
  end
@@ -0,0 +1,78 @@
1
+ module CocinaDisplay
2
+ module Concerns
3
+ # Methods for extracting access/location information from a Cocina object.
4
+ module Accesses
5
+ # Display data for all access metadata except contact emails
6
+ # @return [Array<DisplayData>]
7
+ def access_display_data
8
+ CocinaDisplay::DisplayData.from_objects(accesses +
9
+ access_contacts.reject(&:contact_email?) +
10
+ purls +
11
+ urls)
12
+ end
13
+
14
+ # Display data for all access contact email metadata
15
+ # @return [Array<DisplayData>]
16
+ def contact_email_display_data
17
+ CocinaDisplay::DisplayData.from_objects(access_contacts.select(&:contact_email?))
18
+ end
19
+
20
+ # Display data for the use and reproduction statement.
21
+ # Exhibits and EarthWorks handle useAndReproductionStatement like descriptive metadata.
22
+ # @return [Array<CocinaDisplay::DisplayData>]
23
+ def use_and_reproduction_display_data
24
+ CocinaDisplay::DisplayData.from_string(use_and_reproduction,
25
+ label: I18n.t("cocina_display.field_label.use_and_reproduction"))
26
+ end
27
+
28
+ # Display data for the copyright statement.
29
+ # Exhibits and EarthWorks handle copyright like descriptive metadata.
30
+ # @return [Array<CocinaDisplay::DisplayData>]
31
+ def copyright_display_data
32
+ CocinaDisplay::DisplayData.from_string(copyright,
33
+ label: I18n.t("cocina_display.field_label.copyright"))
34
+ end
35
+
36
+ def license_display_data
37
+ CocinaDisplay::DisplayData.from_string(license_description,
38
+ label: I18n.t("cocina_display.field_label.license"))
39
+ end
40
+
41
+ # All access metadata except contact emails and URLs
42
+ # @return [Array<Description::Access>]
43
+ def accesses
44
+ @accesses ||= Enumerator::Chain.new(
45
+ path("$.description.access.physicalLocation.*"),
46
+ path("$.description.access.digitalLocation.*"),
47
+ path("$.description.access.digitalRepository.*")
48
+ ).map { |a| CocinaDisplay::Description::Access.new(a) }
49
+ end
50
+
51
+ # All access contact metadata
52
+ # @return [Array<Description::AccessContact>]
53
+ def access_contacts
54
+ path("$.description.access.accessContact.*").map do |contact|
55
+ CocinaDisplay::Description::AccessContact.new(contact)
56
+ end
57
+ end
58
+
59
+ # All access URL metadata
60
+ # @return [Array<Description::Url>]
61
+ def urls
62
+ path("$.description.access.url.*").map do |url|
63
+ CocinaDisplay::Description::Url.new(url)
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ # The Purl URL to combine with other access metadata
70
+ # @return [Array<DescriptiveValue>]
71
+ def purls
72
+ return [] unless purl_url.present?
73
+
74
+ CocinaDisplay::DisplayData.descriptive_values_from_string(purl_url, label: I18n.t("cocina_display.field_label.purl"))
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,5 +1,3 @@
1
- require_relative "../contributors/contributor"
2
-
3
1
  module CocinaDisplay
4
2
  module Concerns
5
3
  # Methods for finding and formatting names for contributors
@@ -20,20 +18,20 @@ module CocinaDisplay
20
18
  # @param with_date [Boolean] Include life dates, if present
21
19
  # @return [Array<String>]
22
20
  def additional_contributor_names(with_date: false)
23
- additional_contributors.map { |c| c.display_name(with_date: with_date) }.compact
21
+ additional_contributors.flat_map { |c| c.display_names(with_date: with_date) }.compact
24
22
  end
25
23
 
26
24
  # All names of publishers, formatted for display.
27
25
  # @return [Array<String>]
28
26
  def publisher_names
29
- publisher_contributors.map(&:display_name).compact
27
+ publisher_contributors.flat_map(&:display_names).compact
30
28
  end
31
29
 
32
30
  # All names of contributors who are people, formatted for display.
33
31
  # @param with_date [Boolean] Include life dates, if present
34
32
  # @return [Array<String>]
35
33
  def person_contributor_names(with_date: false)
36
- contributors.filter(&:person?).map { |c| c.display_name(with_date: with_date) }.compact
34
+ contributors.filter(&:person?).flat_map { |c| c.display_names(with_date: with_date) }.compact
37
35
  end
38
36
 
39
37
  # All names of non-person contributors, formatted for display.
@@ -41,35 +39,58 @@ module CocinaDisplay
41
39
  # @return [Array<String>]
42
40
  # @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#contributor-types
43
41
  def impersonal_contributor_names
44
- contributors.reject(&:person?).map(&:display_name).compact
42
+ contributors.reject(&:person?).flat_map(&:display_names).compact
45
43
  end
46
44
 
47
45
  # All names of contributors that are organizations, formatted for display.
48
46
  # @return [Array<String>]
49
47
  def organization_contributor_names
50
- contributors.filter(&:organization?).map(&:display_name).compact
48
+ contributors.filter(&:organization?).flat_map(&:display_names).compact
51
49
  end
52
50
 
53
51
  # All names of contributors that are conferences, formatted for display.
54
52
  # @return [Array<String>]
55
53
  def conference_contributor_names
56
- contributors.filter(&:conference?).map(&:display_name).compact
54
+ contributors.filter(&:conference?).flat_map(&:display_names).compact
57
55
  end
58
56
 
59
57
  # A hash mapping role names to the names of contributors with that role.
60
58
  # @param with_date [Boolean] Include life dates, if present
61
59
  # @return [Hash<String, Array<String>>]
62
60
  def contributor_names_by_role(with_date: false)
63
- contributors.each_with_object({}) do |contributor, hash|
64
- if (name = contributor.display_name(with_date: with_date)).present?
61
+ contributors_by_role(with_date: with_date)
62
+ .transform_values { |contributor_list| contributor_list.flat_map { |contributor| contributor.display_names(with_date: with_date) }.compact_blank }
63
+ .compact_blank
64
+ end
65
+
66
+ # A hash mapping role names to the names of contributors with that role.
67
+ # @param with_date [Boolean] Include life dates, if present
68
+ # @return [Hash<[String,NilClass], Array<Contributor>>]
69
+ def contributors_by_role(with_date: false)
70
+ @contributors_by_role ||= contributors.each_with_object({}) do |contributor, hash|
71
+ if contributor.roles.empty?
72
+ hash[nil] ||= []
73
+ hash[nil] << contributor
74
+ else
65
75
  contributor.roles.each do |role|
66
76
  hash[role.to_s] ||= []
67
- hash[role.to_s] << name
77
+ hash[role.to_s] << contributor
68
78
  end
69
79
  end
70
80
  end
71
81
  end
72
82
 
83
+ # DisplayData for Contributors, one per role.
84
+ # Contributors with no role are grouped under a default heading.
85
+ # @return [Array<DisplayData>]
86
+ def contributor_display_data
87
+ contributors_by_role.map do |role, contributors|
88
+ label = I18n.t(role, scope: "cocina_display.contributor.role",
89
+ default: role&.capitalize || I18n.t("default", scope: "cocina_display.contributor.role"))
90
+ DisplayData.new(label: label, objects: contributors)
91
+ end
92
+ end
93
+
73
94
  # A string value for sorting by contributor that sorts missing values last.
74
95
  # Appends the sort title to break ties between contributor names.
75
96
  # Ignores punctuation and leading/trailing spaces.
@@ -1,8 +1,3 @@
1
- require_relative "../dates/date"
2
- require_relative "../dates/date_range"
3
- require_relative "../events/event"
4
- require_relative "../events/imprint"
5
-
6
1
  module CocinaDisplay
7
2
  module Concerns
8
3
  module Events
@@ -86,12 +81,18 @@ module CocinaDisplay
86
81
  publication_events.flat_map { |event| event.locations.map(&:to_s) }
87
82
  end
88
83
 
89
- # All events associated with the object.
84
+ # All root level events associated with the object.
90
85
  # @return [Array<CocinaDisplay::Events::Event>]
91
86
  def events
92
87
  @events ||= path("$.description.event.*").map { |event| CocinaDisplay::Events::Event.new(event) }
93
88
  end
94
89
 
90
+ # The adminMetadata creation event (When was it was deposited?)
91
+ # @return <CocinaDisplay::Events::Event>
92
+ def admin_creation_event
93
+ @admin_events ||= path("$.description.adminMetadata.event[?(@.type==\"creation\")]").map { |event| CocinaDisplay::Events::Event.new(event) }.first
94
+ end
95
+
95
96
  # All events that could be used to select a publication date.
96
97
  # Includes publication, creation, and capture events.
97
98
  # Considers event types as well as date types if the event is untyped.
@@ -121,6 +122,18 @@ module CocinaDisplay
121
122
  @event_dates ||= events.flat_map(&:dates)
122
123
  end
123
124
 
125
+ # DisplayData for all notes associated with events.
126
+ # @return [Array<CocinaDisplay::DisplayData>]
127
+ def event_note_display_data
128
+ CocinaDisplay::DisplayData.from_objects(events.flat_map(&:notes))
129
+ end
130
+
131
+ # DisplayData for all dates associated with events.
132
+ # @return [Array<CocinaDisplay::DisplayData>]
133
+ def event_date_display_data
134
+ CocinaDisplay::DisplayData.from_objects(event_dates)
135
+ end
136
+
124
137
  # The earliest preferred publication date as a CocinaDisplay::Dates::Date object.
125
138
  # Considers publication, creation, and capture dates in that order.
126
139
  # Prefers dates marked as primary and those with a declared encoding.