cocina_display 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca2e2a44615201a7709f9a7b4d7e0792aaea7f14344dc93b3bf800acdef83e91
4
- data.tar.gz: 8dfd7f06a65930d2d639083c1235a7a9e41d8bcf59ac12f60bd4b2b87118d784
3
+ metadata.gz: 6ef43e2d573c99c10d6db87974ff32091ff7e7d312bf2e812ef1043f513f291c
4
+ data.tar.gz: f04b35117201aaebff5e8d041d2776525dcd18a46bc408baf9d8649376d90618
5
5
  SHA512:
6
- metadata.gz: 11e6b9f94eaa777d1069deb972e046cda240e815524a2d938a8df97f75556393a10706c73dcaa9172a2c2a3cb90a459ed0ce8ca3c9d04f5f025242629b10f809
7
- data.tar.gz: be9642a9205aca7b1aac2c73cb6250dbc1def18cba5fa88b3afdb4839186f6832ca80404f04ca47680fe181738276317f27b9c751c527b6ded4b6d637d1d1254
6
+ metadata.gz: c36dd129d129ce44c7c516b670295b59f68732d0270664dda63ce119fe37ec60e68c6fd615e37ada5ecef06777db7be30cd708d0f0c461c18c97085e98eb553f
7
+ data.tar.gz: cddd56237670d908a6be7951e24fe62a0fbd3076fd0eff96693c7debc03fa9ab8e01157a330923938143ec2067a4d97d4ead339f44fd2b00d0bcf99ea92b6af0
data/README.md CHANGED
@@ -28,24 +28,18 @@ To start, you need some Cocina in JSON form.
28
28
 
29
29
  You can download some directly from PURL by visiting an object's PURL URL and appending `.json` to the end, like `https://purl.stanford.edu/bb112zx3193.json`. Some examples are available in the `spec/fixtures` directory.
30
30
 
31
- You can also use the built-in `HTTP` library or `faraday` gem to fetch the record for you, e.g.:
31
+ There is also a helper method to fetch the Cocina JSON for a given DRUID and immediately parse it into a `CocinaRecord` object:
32
32
 
33
33
  ```ruby
34
- require 'http'
35
- cocina_json = HTTP.get('https://purl.stanford.edu/bb112zx3193.json').to_s
34
+ > record = CocinaDisplay::CocinaRecord.fetch('bb112zx3193')
35
+ => #<CocinaDisplay::CocinaRecord:0x00007f8c8c0b5c80
36
36
  ```
37
37
 
38
38
  ### Working with objects
39
39
 
40
- Once you have the JSON, you can initialize a `CocinaRecord` object and start working with it. The `CocinaRecord` class provides some methods to access common fields, as well as an underlying hash representation parsed from the JSON.
40
+ The `CocinaRecord` class provides some methods to access common fields, as well as an underlying hash representation parsed from the JSON.
41
41
 
42
42
  ```ruby
43
- > require 'cocina_display/cocina_record'
44
- => true
45
- > record = CocinaDisplay::CocinaRecord.new(cocina_json)
46
- =>
47
- #<CocinaDisplay::CocinaRecord:0x000000012d11b600
48
- ...
49
43
  > record.title
50
44
  => "Bugatti Type 51A. Road & Track Salon January 1957"
51
45
  > record.content_type
@@ -2,16 +2,29 @@
2
2
 
3
3
  require "janeway"
4
4
  require "json"
5
- require "uri"
5
+ require "net/http"
6
6
  require "active_support"
7
7
  require "active_support/core_ext/object/blank"
8
8
  require "active_support/core_ext/hash/conversions"
9
9
 
10
10
  require_relative "title_builder"
11
+ require_relative "concerns/events"
11
12
 
12
13
  module CocinaDisplay
13
14
  # Public Cocina metadata for an SDR object, as fetched from PURL.
14
15
  class CocinaRecord
16
+ include CocinaDisplay::Concerns::Events
17
+
18
+ # Fetch a public Cocina document from PURL and create a CocinaRecord.
19
+ # @note This is intended to be used in development or testing only.
20
+ # @param druid [String] The bare DRUID of the object to fetch.
21
+ # @return [CocinaDisplay::CocinaRecord]
22
+ # :nocov:
23
+ def self.fetch(druid)
24
+ new(Net::HTTP.get(URI("https://purl.stanford.edu/#{druid}.json")))
25
+ end
26
+ # :nocov:
27
+
15
28
  # The parsed Cocina document.
16
29
  # @return [Hash]
17
30
  attr_reader :cocina_doc
@@ -0,0 +1,137 @@
1
+ require_relative "../dates/date"
2
+ require_relative "../dates/date_range"
3
+ require_relative "../imprint"
4
+
5
+ module CocinaDisplay
6
+ module Concerns
7
+ module Events
8
+ # The earliest preferred publication date as a Date object.
9
+ # If the date was a range or interval, uses the start (or end if no start).
10
+ # Considers publication, creation, and capture dates in that order.
11
+ # Prefers dates marked as primary and those with a declared encoding.
12
+ # @param ignore_qualified [Boolean] Reject qualified dates (e.g. approximate)
13
+ # @return [Date, nil]
14
+ # @see https://github.com/inukshuk/edtf-ruby
15
+ def pub_date_edtf(ignore_qualified: false)
16
+ date = pub_date(ignore_qualified: ignore_qualified)
17
+ return unless date
18
+
19
+ if date.is_a? CocinaDisplay::Dates::DateRange
20
+ date = date.start || date.stop
21
+ end
22
+
23
+ edtf_date = date.date
24
+ return unless edtf_date
25
+
26
+ if edtf_date.is_a? EDTF::Interval
27
+ edtf_date.from
28
+ else
29
+ edtf_date
30
+ end
31
+ end
32
+
33
+ # The earliest preferred publication year as an integer.
34
+ # If the date was a range or interval, uses the start (or end if no start).
35
+ # Considers publication, creation, and capture dates in that order.
36
+ # Prefers dates marked as primary and those with a declared encoding.
37
+ # @param ignore_qualified [Boolean] Reject qualified dates (e.g. approximate)
38
+ # @return [Integer, nil]
39
+ # @note 6 BCE will return -5; 4 CE will return 4.
40
+ def pub_year_int(ignore_qualified: false)
41
+ pub_date_edtf(ignore_qualified: ignore_qualified)&.year
42
+ end
43
+
44
+ # String for displaying the earliest preferred publication year or range.
45
+ # Considers publication, creation, and capture dates in that order.
46
+ # Prefers dates marked as primary and those with a declared encoding.
47
+ # @param ignore_qualified [Boolean] Reject qualified dates (e.g. approximate)
48
+ # @return [String, nil]
49
+ # @example Year range
50
+ # CocinaRecord.fetch('bb099mt5053').pub_year_display_str #=> "1932 - 2012"
51
+ def pub_year_display_str(ignore_qualified: false)
52
+ date = pub_date(ignore_qualified: ignore_qualified)
53
+ return unless date
54
+
55
+ date.decoded_value(allowed_precisions: [:year, :decade, :century])
56
+ end
57
+
58
+ # String for displaying the imprint statement(s).
59
+ # @return [String, nil]
60
+ # @see CocinaDisplay::Imprint#display_str
61
+ # @example
62
+ # CocinaRecord.fetch('bt553vr2845').imprint_display_str #=> "New York : Meridian Book, 1993, c1967"
63
+ def imprint_display_str
64
+ imprints.map(&:display_str).compact_blank.join("; ")
65
+ end
66
+
67
+ private
68
+
69
+ # Event dates as an array of CocinaDisplay::Dates::Date objects.
70
+ # If type is provided, keep dates with a matching event type OR date type.
71
+ # @param type [Symbol, nil] Filter by event type (e.g. :publication).
72
+ # @return [Array<CocinaDisplay::Dates::Date>] The list of event dates
73
+ def event_dates(type: nil)
74
+ filter_expr = type.present? ? "?match(@.type, \"#{type}\")" : "*"
75
+
76
+ Enumerator::Chain.new(
77
+ path("$.description.event[*].date[#{filter_expr}]"),
78
+ path("$.description.event[#{filter_expr}].date[*]")
79
+ ).uniq.map do |date|
80
+ CocinaDisplay::Dates::Date.from_cocina(date)
81
+ end
82
+ end
83
+
84
+ # Array of CocinaDisplay::Imprint objects for all relevant Cocina events.
85
+ # Considers publication, creation, capture, and copyright events.
86
+ # Considers event types as well as date types if the event is untyped.
87
+ # Prefers events where the date was not encoded, if any.
88
+ # @return [Array<CocinaDisplay::Imprint>] The list of Imprint objects
89
+ def imprints
90
+ filter_expr = "\"(publication|creation|capture|copyright)\""
91
+
92
+ imprints = Enumerator::Chain.new(
93
+ path("$.description.event[?match(@.type, #{filter_expr})]"),
94
+ path("$.description.event[?@.date[?match(@.type, #{filter_expr})]]")
95
+ ).uniq.map do |event|
96
+ CocinaDisplay::Imprint.new(event)
97
+ end
98
+
99
+ imprints.reject(&:date_encoding?).presence || imprints
100
+ end
101
+
102
+ # The earliest preferred publication date as a CocinaDisplay::Dates::Date object.
103
+ # Considers publication, creation, and capture dates in that order.
104
+ # Prefers dates marked as primary and those with a declared encoding.
105
+ # @param ignore_qualified [Boolean] Reject qualified dates (e.g. approximate)
106
+ # @return [CocinaDisplay::Dates::Date] The earliest preferred date
107
+ # @return [nil] if no dates are left after filtering
108
+ def pub_date(ignore_qualified: false)
109
+ [:publication, :creation, :capture].map do |type|
110
+ earliest_preferred_date(event_dates(type: type), ignore_qualified: ignore_qualified)
111
+ end.compact.first
112
+ end
113
+
114
+ # Choose the earliest, best date from a provided list of event dates.
115
+ # Rules to consider:
116
+ # 1. Reject any dates that were not parsed.
117
+ # 2. If `ignore_qualified` is true, reject any qualified dates.
118
+ # 3. If there are any primary dates, prefer those dates.
119
+ # 4. If there are any encoded dates, prefer those dates.
120
+ # 5. From whatever is left, choose the earliest date.
121
+ # @param dates [Array<CocinaDisplay::Dates::Date>] The list of dates
122
+ # @param ignore_qualified [Boolean] Reject qualified dates (e.g. approximate)
123
+ # @return [CocinaDisplay::Dates::Date] The earliest preferred date
124
+ # @return [nil] if no dates are left after filtering
125
+ def earliest_preferred_date(dates, ignore_qualified: false)
126
+ return nil if dates.empty?
127
+
128
+ dates.filter!(&:parsed_date?)
129
+ dates.reject!(&:approximate?) if ignore_qualified
130
+ dates = dates.filter(&:primary?).presence || dates
131
+ dates = dates.filter(&:encoding?).presence || dates
132
+
133
+ dates.min
134
+ end
135
+ end
136
+ end
137
+ end