cocina-models 0.101.0 → 0.103.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/Gemfile.lock +7 -7
- data/lib/cocina/models/builders/title_builder.rb +28 -13
- data/lib/cocina/models/mapping/to_mods/title.rb +28 -20
- data/lib/cocina/models/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25d2a47837021c38ac5a6f722e53e17fc78c9f07b09efa10d5d2df18a6aa714f
|
4
|
+
data.tar.gz: f52008be424354db1c55160c79ae4511c368b3bcd501a61d86d3ff23f572e57a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc39f2cd59a93a142503c575469ca20362a17c8a308169140f7f851dcc1099e2b758a8cef019ab52338d8f9ad25a2408c5a5a42d1c024655abc240e15a85f32b
|
7
|
+
data.tar.gz: fc2eb7c64bc4055a957c4ffc6de5a9d3106154eb12171fd214127bf6c8047cbc1e02e3a1ed3b7d33580052b148389174a96e5d7400b60c285dd8c1964c08e909
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cocina-models (0.
|
4
|
+
cocina-models (0.103.0)
|
5
5
|
activesupport
|
6
6
|
deprecation
|
7
7
|
dry-struct (~> 1.0)
|
@@ -43,7 +43,7 @@ GEM
|
|
43
43
|
openapi_parser (~> 1.0)
|
44
44
|
rack (>= 1.5)
|
45
45
|
concurrent-ruby (1.3.5)
|
46
|
-
connection_pool (2.5.
|
46
|
+
connection_pool (2.5.2)
|
47
47
|
deprecation (1.1.0)
|
48
48
|
activesupport
|
49
49
|
diff-lcs (1.6.1)
|
@@ -78,7 +78,7 @@ GEM
|
|
78
78
|
i18n (1.14.7)
|
79
79
|
concurrent-ruby (~> 1.0)
|
80
80
|
ice_nine (0.11.2)
|
81
|
-
json (2.
|
81
|
+
json (2.11.3)
|
82
82
|
json_schema (0.21.0)
|
83
83
|
jsonpath (1.1.5)
|
84
84
|
multi_json
|
@@ -88,12 +88,12 @@ GEM
|
|
88
88
|
mini_portile2 (2.8.8)
|
89
89
|
minitest (5.25.5)
|
90
90
|
multi_json (1.15.0)
|
91
|
-
nokogiri (1.18.
|
91
|
+
nokogiri (1.18.8)
|
92
92
|
mini_portile2 (~> 2.8.2)
|
93
93
|
racc (~> 1.4)
|
94
94
|
openapi_parser (1.0.0)
|
95
95
|
optimist (3.2.1)
|
96
|
-
parallel (1.
|
96
|
+
parallel (1.27.0)
|
97
97
|
parser (3.3.8.0)
|
98
98
|
ast (~> 2.4.1)
|
99
99
|
racc
|
@@ -120,7 +120,7 @@ GEM
|
|
120
120
|
rspec-support (3.13.2)
|
121
121
|
rspec_junit_formatter (0.6.0)
|
122
122
|
rspec-core (>= 2, < 4, != 2.12.0)
|
123
|
-
rubocop (1.75.
|
123
|
+
rubocop (1.75.3)
|
124
124
|
json (~> 2.3)
|
125
125
|
language_server-protocol (~> 3.17.0.2)
|
126
126
|
lint_roller (~> 1.1.0)
|
@@ -137,7 +137,7 @@ GEM
|
|
137
137
|
rubocop-rake (0.7.1)
|
138
138
|
lint_roller (~> 1.1)
|
139
139
|
rubocop (>= 1.72.1)
|
140
|
-
rubocop-rspec (3.
|
140
|
+
rubocop-rspec (3.6.0)
|
141
141
|
lint_roller (~> 1.1)
|
142
142
|
rubocop (~> 1.72, >= 1.72.1)
|
143
143
|
ruby-progressbar (1.13.0)
|
@@ -8,26 +8,28 @@ module Cocina
|
|
8
8
|
# TitleBuilder selects the prefered title from the cocina object for solr indexing
|
9
9
|
class TitleBuilder # rubocop:disable Metrics/ClassLength
|
10
10
|
extend Deprecation
|
11
|
-
# @param [
|
11
|
+
# @param [Array<Cocina::Models::Title,Cocina::Models::DescriptiveValue>] titles the titles to consider
|
12
|
+
# @param [Array<Cocina::Models::FolioCatalogLink>] catalog_links the folio catalog links to check for digital serials part labels
|
12
13
|
# @param [Symbol] strategy ":first" is the strategy for selection when primary or display
|
13
14
|
# title are missing
|
14
15
|
# @param [Boolean] add_punctuation determines if the title should be formmated with punctuation
|
15
16
|
# @return [String, Array] the title value for Solr - for :first strategy, a string; for :all strategy, an array
|
16
17
|
# (e.g. title displayed in blacklight search results vs boosting values for search result rankings)
|
17
|
-
def self.build(titles, strategy: :first, add_punctuation: true)
|
18
|
+
def self.build(titles, catalog_links: [], strategy: :first, add_punctuation: true)
|
19
|
+
part_label = catalog_links.find { |link| link.catalog == 'folio' }&.partLabel
|
18
20
|
if titles.respond_to?(:description)
|
19
21
|
Deprecation.warn(self,
|
20
22
|
"Calling TitleBuilder.build with a #{titles.class} is deprecated. " \
|
21
23
|
'It must be called with an array of titles')
|
22
24
|
titles = titles.description.title
|
23
25
|
end
|
24
|
-
new(strategy: strategy, add_punctuation: add_punctuation).build(titles)
|
26
|
+
new(strategy: strategy, add_punctuation: add_punctuation, part_label: part_label).build(titles)
|
25
27
|
end
|
26
28
|
|
27
29
|
# the "main title" is the title withOUT subtitle, part name, etc. We want to index it separately so
|
28
30
|
# we can boost matches on it in search results (boost matching this string higher than matching full title string)
|
29
31
|
# e.g. "The Hobbit" (main_title) vs "The Hobbit, or, There and Back Again (full_title)
|
30
|
-
# @param [
|
32
|
+
# @param [Array<Cocina::Models::Title,Cocina::Models::DescriptiveValue>] titles the titles to consider
|
31
33
|
# @return [Array<String>] the main title value(s) for Solr - array due to possible parallelValue
|
32
34
|
def self.main_title(titles)
|
33
35
|
new(strategy: :first, add_punctuation: false).main_title(titles)
|
@@ -35,15 +37,17 @@ module Cocina
|
|
35
37
|
|
36
38
|
# the "full title" is the title WITH subtitle, part name, etc. We want to able able to index it separately so
|
37
39
|
# we can boost matches on it in search results (boost matching this string higher than other titles present)
|
38
|
-
# @param [
|
40
|
+
# @param [Array<Cocina::Models::Title,Cocina::Models::DescriptiveValue>] titles the titles to consider
|
41
|
+
# @param [Array<Cocina::Models::FolioCatalogLink>] catalog_links the folio catalog links to check for digital serials part labels
|
39
42
|
# @return [Array<String>] the full title value(s) for Solr - array due to possible parallelValue
|
40
|
-
def self.full_title(titles)
|
41
|
-
|
43
|
+
def self.full_title(titles, catalog_links: [])
|
44
|
+
part_label = catalog_links.find { |link| link.catalog == 'folio' }&.partLabel
|
45
|
+
[new(strategy: :first, add_punctuation: false, only_one_parallel_value: false, part_label: part_label).build(titles)].flatten.compact
|
42
46
|
end
|
43
47
|
|
44
48
|
# "additional titles" are all title data except for full_title. We want to able able to index it separately so
|
45
49
|
# we can boost matches on it in search results (boost matching these strings lower than other titles present)
|
46
|
-
# @param [
|
50
|
+
# @param [Array<Cocina::Models::Title,Cocina::Models::DescriptiveValue>] titles the titles to consider
|
47
51
|
# @return [Array<String>] the values for Solr
|
48
52
|
def self.additional_titles(titles)
|
49
53
|
[new(strategy: :all, add_punctuation: false).build(titles)].flatten - full_title(titles)
|
@@ -57,13 +61,15 @@ module Cocina
|
|
57
61
|
# of primary, untyped, first occurrence. When false, return an array containing all the parallel values.
|
58
62
|
# Why? Think of e.g. title displayed in blacklight search results vs boosting values for ranking of search
|
59
63
|
# results
|
60
|
-
|
64
|
+
# @param part_label [String] the partLabel to add for digital serials display
|
65
|
+
def initialize(strategy:, add_punctuation:, only_one_parallel_value: true, part_label: nil)
|
61
66
|
@strategy = strategy
|
62
67
|
@add_punctuation = add_punctuation
|
63
68
|
@only_one_parallel_value = only_one_parallel_value
|
69
|
+
@part_label = part_label
|
64
70
|
end
|
65
71
|
|
66
|
-
# @param [
|
72
|
+
# @param [Array<Cocina::Models::Title>] cocina_titles the titles to consider
|
67
73
|
# @return [String, Array] the title value for Solr - for :first strategy, a string; for :all strategy, an array
|
68
74
|
# (e.g. title displayed in blacklight search results vs boosting values for search result rankings)
|
69
75
|
#
|
@@ -71,9 +77,10 @@ module Cocina
|
|
71
77
|
def build(cocina_titles)
|
72
78
|
cocina_title = primary_title(cocina_titles) || untyped_title(cocina_titles)
|
73
79
|
cocina_title = other_title(cocina_titles) if cocina_title.blank?
|
74
|
-
|
75
80
|
if strategy == :first
|
76
|
-
extract_title(cocina_title)
|
81
|
+
result = extract_title(cocina_title)
|
82
|
+
result = add_part_label(result) if part_label.present?
|
83
|
+
result
|
77
84
|
else
|
78
85
|
result = cocina_titles.map { |ctitle| extract_title(ctitle) }.flatten
|
79
86
|
if only_one_parallel_value? && result.length == 1
|
@@ -97,7 +104,13 @@ module Cocina
|
|
97
104
|
|
98
105
|
private
|
99
106
|
|
100
|
-
attr_reader :strategy
|
107
|
+
attr_reader :strategy, :part_label
|
108
|
+
|
109
|
+
def add_part_label(title)
|
110
|
+
# when a digital serial
|
111
|
+
title = title.sub(/[ .,]*$/, '').to_s
|
112
|
+
add_punctuation? ? "#{title}, #{part_label}" : "#{title} #{part_label}"
|
113
|
+
end
|
101
114
|
|
102
115
|
def extract_title(cocina_title)
|
103
116
|
title_values = if cocina_title.value
|
@@ -234,6 +247,8 @@ module Cocina
|
|
234
247
|
padding = non_sorting_padding(cocina_title, value)
|
235
248
|
result = add_non_sorting_value(result, value, padding)
|
236
249
|
when 'part name', 'part number'
|
250
|
+
# even if there is a partLabel, use any existing structuredValue
|
251
|
+
# part name/number that remains for non-digital serials purposes
|
237
252
|
if part_name_number.blank?
|
238
253
|
part_name_number = part_name_number(cocina_title.structuredValue)
|
239
254
|
result = if !add_punctuation?
|
@@ -11,30 +11,29 @@ module Cocina
|
|
11
11
|
TAG_NAME = Cocina::Models::Mapping::FromMods::Title::TYPES.invert.merge('activity dates' => 'date').freeze
|
12
12
|
NAME_TYPES = ['name', 'forename', 'surname', 'life dates', 'term of address'].freeze
|
13
13
|
|
14
|
+
def self.write(...)
|
15
|
+
new(...).write
|
16
|
+
end
|
17
|
+
|
14
18
|
# @params [Nokogiri::XML::Builder] xml
|
15
19
|
# @params [Array<Cocina::Models::Title>] titles
|
20
|
+
# @params [IdGenerator] id_generator
|
16
21
|
# @params [Array<Cocina::Models::Contributor>] contributors
|
17
22
|
# @params [Hash] additional_attrs for title
|
18
|
-
# @params [
|
19
|
-
|
20
|
-
|
21
|
-
id_generator: id_generator).write
|
22
|
-
end
|
23
|
-
|
24
|
-
def initialize(xml:, titles:, additional_attrs:, contributors: [], id_generator: {})
|
23
|
+
# @params [Array<Cocina::Models::CatalogLink>] catalog_links a list of catalog
|
24
|
+
# links which may contain part label and sort key information
|
25
|
+
def initialize(xml:, titles:, id_generator:, contributors: [], additional_attrs: {}, catalog_links: []) # rubocop:disable Metrics/ParameterLists
|
25
26
|
@xml = xml
|
26
27
|
@titles = titles
|
28
|
+
@id_generator = id_generator
|
27
29
|
@contributors = contributors
|
28
|
-
@name_title_vals_index = {}
|
29
30
|
@additional_attrs = additional_attrs
|
30
|
-
@
|
31
|
+
@catalog_links = catalog_links
|
32
|
+
@name_title_vals_index = {}
|
31
33
|
end
|
32
34
|
|
33
|
-
# rubocop:disable Metrics/AbcSize
|
34
|
-
|
35
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
36
|
-
def write
|
37
|
-
titles.each do |title|
|
35
|
+
def write # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
36
|
+
titles.each do |title| # rubocop:disable Metrics/BlockLength
|
38
37
|
name_title_vals_index = name_title_vals_index_for(title)
|
39
38
|
|
40
39
|
if title.valueAt
|
@@ -78,13 +77,10 @@ module Cocina
|
|
78
77
|
end
|
79
78
|
end
|
80
79
|
end
|
81
|
-
# rubocop:enable Metrics/AbcSize
|
82
|
-
# rubocop:enable Metrics/BlockLength
|
83
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
84
80
|
|
85
81
|
private
|
86
82
|
|
87
|
-
attr_reader :xml, :titles, :contributors, :name_title_vals_index, :id_generator, :additional_attrs
|
83
|
+
attr_reader :xml, :titles, :contributors, :name_title_vals_index, :id_generator, :additional_attrs, :catalog_links
|
88
84
|
|
89
85
|
def write_xlink(title:)
|
90
86
|
attrs = { 'xlink:href' => title.valueAt }
|
@@ -173,9 +169,21 @@ module Cocina
|
|
173
169
|
title_info_attrs = title_info_attrs_for(title).merge(title_info_attrs)
|
174
170
|
xml.titleInfo(with_uri_info(title, title_info_attrs)) do
|
175
171
|
title_parts = flatten_structured_value(title)
|
176
|
-
|
172
|
+
write_title_parts!(title_parts: title_parts_without_names(title_parts), title: title)
|
173
|
+
end
|
174
|
+
end
|
177
175
|
|
178
|
-
|
176
|
+
def write_title_parts!(title_parts:, title:)
|
177
|
+
# If title parts contain the main title and a part label has been
|
178
|
+
# recorded in a catalog link, short-circuit the usual title part
|
179
|
+
# parsing and pull the part label from the catalog link instead of
|
180
|
+
# from descriptive metadata
|
181
|
+
if (main_title = title_parts.find { |part| part.type == 'main title' }&.value) &&
|
182
|
+
(part_label = catalog_links.find { |link| link.catalog == 'folio' && link.partLabel.present? }&.partLabel)
|
183
|
+
xml.title(main_title)
|
184
|
+
xml.partNumber(part_label)
|
185
|
+
else
|
186
|
+
title_parts.each do |title_part|
|
179
187
|
title_type = tag_name_for(title_part)
|
180
188
|
title_value = title_value_for(title_part, title_type, title)
|
181
189
|
xml.public_send(title_type, title_value) if title_part.note.blank?
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cocina-models
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.103.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Coyne
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-29 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activesupport
|