pennmarc 1.0.6 → 1.0.7

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: cb8a665310aaf17d406fcfbfe562894a71f17d35b2d63173ca587a9c0e343b54
4
- data.tar.gz: 671549b45eb522db5f4350aa0743bcd16585398c6c07f71e2d2a772367a0f33c
3
+ metadata.gz: 11dd403cd5a0f964db340be6258d2cc510c99ccc0fc6d43a65b236853cb5b87c
4
+ data.tar.gz: 85eed79f3e637121cce8be8b0f0509d26e3fa9b6283b7f5bc48b0b321259e3f3
5
5
  SHA512:
6
- metadata.gz: 9a1374b40186e61a56e33924a6f76561877af209e3b8486b185599766e5cc7bc1b45362cf4f3f9e18fc5cf2cb4328b46f8b0f83b0e2c374a1f31164bce3b8483
7
- data.tar.gz: 7a098756899b310a694dbcc1409516de62ff9897b624ac4783b793de145ac61c497bc183409343269e5e33b7b21a07728fa74148fe6af2ad8aaef8149acd71c5
6
+ metadata.gz: 90a59eaf167d985b596663d6b8d1eab0c3263724da550babfb28fa253df326b07b72bbb5aac1cf1d4150efd2eaecce84686b2de9e99801ff178bbfabc982b765
7
+ data.tar.gz: 5e0a4fdc1518af2b3a52bc137f76a30430d025f58f5cd46b9d0347a7a6a08f901e72e45917cc9a52d0352a6dbb0213725fd5a6e22650cfedb00d534fb72b44f2
data/README.md CHANGED
@@ -71,7 +71,7 @@ rspec
71
71
 
72
72
  ## Publishing the Gem
73
73
 
74
- 1. Update the version in `pennmarc.gemspec`
74
+ 1. Update the `VERSION` constant in `lib/pennmarc/version.rb`.
75
75
  2. Run `gem build pennmarc.gemspec` with the latest code
76
76
  3. Run `gem push pennmarc-{version number here}.gem`(e.g. `gem push pennmarc-1.0.0.gem`) to push to RubyGems. You will need access and MFA setup with RubyGems.
77
77
 
@@ -26,9 +26,9 @@ module PennMARC
26
26
  # when no linguistic content is found.
27
27
  #
28
28
  # @note In franklin, we extracted the language code from the 008 control field. After engaging cataloging unit
29
- # representatives, we decided to extract these values from the 041 field: Includes records for multilingual
30
- # items, items that involve translation, and items where the medium of communication is a sign language.
31
- # https://www.loc.gov/marc/bibliographic/bd041.html
29
+ # representatives, we decided to also extract these values from the 041 field: Includes records for
30
+ # multilingual items, items that involve translation, and items where the medium of communication is a sign
31
+ # language. https://www.loc.gov/marc/bibliographic/bd041.html
32
32
  #
33
33
  # @param [MARC::Record] record
34
34
  # @param [Hash] iso_639_2_mapping iso-639-2 spec hash for language code translation
@@ -10,9 +10,52 @@ module PennMARC
10
10
  # @return [Object]
11
11
  def offsite(record); end
12
12
 
13
- def full_text(record:); end
13
+ # Full text links from MARC 856 fields.
14
+ # @param [MARC::Record] record
15
+ # @return [Array] array of hashes
16
+ def full_text(record:)
17
+ indicator2_options = %w[0 1]
18
+ links_from_record(record, indicator2_options)
19
+ end
20
+
21
+ # Web text links from MARC 856 fields.
22
+ # @param [MARC::Record] record
23
+ # @return [Array] array of hashes
24
+ def web(record:)
25
+ indicator2_options = ['2', ' ', '']
26
+ links_from_record(record, indicator2_options)
27
+ end
28
+
29
+ private
30
+
31
+ # Extract subfield 3 and z/y depending on the presence of either. Extract link url and assemble array
32
+ # with text and link.
33
+ # @param [MARC::Field] field
34
+ # @return [Array]
35
+ def link_text_and_url(field)
36
+ subfield3 = subfield_values(field, 3)
37
+ subfield_zy = field.find_all(&subfield_in?(%w[z y])).map(&:value)
38
+ link_text = [subfield3, subfield_zy.first].compact.join(' ')
39
+ link_url = subfield_values(field, 'u')&.first || ''
40
+ [link_text, link_url.sub(' target=_blank', '')]
41
+ end
42
+
43
+ # Assemble array of link text, link URL values from 856 fields. Ensure indicator1 (access method)
44
+ # is always 4 (HTTP) and indicator2 (relationship) can be specified by caller method.
45
+ # @param [MARC::Record] record
46
+ # @param [Array] indicator2_options
47
+ # @return [Array]
48
+ def links_from_record(record, indicator2_options)
49
+ record.fields('856').filter_map do |field|
50
+ next unless field.indicator1 == '4' && indicator2_options.include?(field.indicator2)
14
51
 
15
- def web(record:); end
52
+ link_text, link_url = link_text_and_url(field)
53
+ {
54
+ link_text: link_text.present? ? link_text : link_url,
55
+ link_url: link_url
56
+ }
57
+ end
58
+ end
16
59
  end
17
60
  end
18
61
  end
@@ -4,46 +4,81 @@ module PennMARC
4
4
  # This helper contains logic for parsing out Title and Title-related fields.
5
5
  class Title < Helper
6
6
  class << self
7
- # these will be used when completing the *search_aux methods
7
+ # We use these fields when retrieving auxiliary titles in the *search_aux methods:
8
+ # {https://www.loc.gov/marc/bibliographic/bd130.html 130},
9
+ # {https://www.loc.gov/marc/bibliographic/bd210.html 210},
10
+ # {https://www.loc.gov/marc/bibliographic/bd245.html 245},
11
+ # {https://www.loc.gov/marc/bibliographic/bd246.html 246},
12
+ # {https://www.loc.gov/marc/bibliographic/bd247.html 247},
13
+ # {https://www.loc.gov/marc/bibliographic/bd440.html 440},
14
+ # {https://www.loc.gov/marc/bibliographic/bd490.html 490},
15
+ # {https://www.loc.gov/marc/bibliographic/bd730.html 730},
16
+ # {https://www.loc.gov/marc/bibliographic/bd740.html 740},
17
+ # {https://www.loc.gov/marc/bibliographic/bd830.html 830},
18
+ # {https://www.loc.gov/marc/bibliographic/bd773.html 773},
19
+ # {https://www.loc.gov/marc/bibliographic/bd774.html 774},
20
+ # {https://www.loc.gov/marc/bibliographic/bd780.html 780},
21
+ # {https://www.loc.gov/marc/bibliographic/bd785.html 785},
22
+ # {https://www.loc.gov/marc/bibliographic/bd700.html 700},
23
+ # {https://www.loc.gov/marc/bibliographic/bd710.html 710},
24
+ # {https://www.loc.gov/marc/bibliographic/bd711.html 711},
25
+ # {https://www.loc.gov/marc/bibliographic/bd505.html 505}
8
26
  AUX_TITLE_TAGS = {
9
27
  main: %w[130 210 240 245 246 247 440 490 730 740 830],
10
28
  related: %w[773 774 780 785],
11
- entity: %w[700 710 711]
29
+ entity: %w[700 710 711],
30
+ note: %w[505]
12
31
  }.freeze
13
32
 
14
- # Main Title Search field. Takes from 245 and linked 880.
33
+ # Main Title Search field. Takes from {https://www.loc.gov/marc/bibliographic/bd245.html 245} and linked 880.
15
34
  # @note Ported from get_title_1_search_values.
16
35
  # @param [MARC::Record] record
17
36
  # @return [Array<String>] array of title values for search
18
37
  def search(record)
19
- titles = record.fields('245').filter_map do |field|
20
- join_subfields(field, &subfield_not_in?(%w[c 6 8 h]))
21
- end
22
- titles + record.fields('880').filter_map do |field|
23
- next unless subfield_value?(field, '6', /245/)
38
+ record.fields(%w[245 880]).filter_map do |field|
39
+ next if field.tag == '880' && subfield_value_not_in?(field, '6', %w[245])
24
40
 
25
41
  join_subfields(field, &subfield_not_in?(%w[c 6 8 h]))
26
42
  end
27
43
  end
28
44
 
29
- # Auxiliary Title Search field. Takes from many fields that contain title-like information.
30
- # @note Ported from get_title_2_search_values.
31
- # @todo port this, it is way complicated but essential for relevance
45
+ # Auxiliary Title Search field. Takes from many fields defined in {AUX_TITLE_TAGS} that contain title-like
46
+ # information.
32
47
  # @param [MARC::Record] record
33
- # @return [Array<String>] array of title values for search
34
- def search_aux(record); end
48
+ # @return [Array<String>] array of auxiliary title values for search
49
+ def search_aux(record)
50
+ search_aux_values(record: record, title_type: :main, &subfield_not_in?(%w[c 6 8])) +
51
+ search_aux_values(record: record, title_type: :related, &subfield_not_in?(%w[s t])) +
52
+ search_aux_values(record: record, title_type: :entity, &subfield_in?(%w[t])) +
53
+ search_aux_values(record: record, title_type: :note, &subfield_in?(%w[t]))
54
+ end
35
55
 
36
- # Journal Title Search field.
37
- # @todo port this, it is way complicated but essential for relevance
56
+ # Journal Title Search field. Takes from {https://www.loc.gov/marc/bibliographic/bd245.html 245} and linked 880.
57
+ # We do not return any values if the {https://www.loc.gov/marc/bibliographic/bdleader.html MARC leader}
58
+ # indicates that the record is not a serial.
38
59
  # @param [MARC::Record] record
39
60
  # @return [Array<String>] journal title information for search
40
- def journal_search(record); end
61
+ def journal_search(record)
62
+ return [] if not_a_serial?(record)
63
+
64
+ record.fields(%w[245 880]).filter_map do |field|
65
+ next if field.tag == '880' && subfield_value_not_in?(field, '6', %w[245])
66
+
67
+ join_subfields(field, &subfield_not_in?(%w[c 6 8 h]))
68
+ end
69
+ end
41
70
 
42
- # Auxiliary Journal Title Search field.
43
- # @todo port this, it is way complicated but essential for relevance
71
+ # Auxiliary Journal Title Search field. Takes from many fields defined in {AUX_TITLE_TAGS} that contain title-like
72
+ # information. Does not return any titles if the {https://www.loc.gov/marc/bibliographic/bdleader.html MARC leader}
73
+ # indicates that the record is not a serial.
44
74
  # @param [MARC::Record] record
45
- # @return [Array<String>] journal title information for search
46
- def journal_search_aux(record); end
75
+ # @return [Array<String>] auxiliary journal title information for search
76
+ def journal_search_aux(record)
77
+ search_aux_values(record: record, title_type: :main, journal: true, &subfield_not_in?(%w[c 6 8])) +
78
+ search_aux_values(record: record, title_type: :related, journal: true, &subfield_not_in?(%w[s t])) +
79
+ search_aux_values(record: record, title_type: :entity, journal: true, &subfield_in?(%w[t])) +
80
+ search_aux_values(record: record, title_type: :note, journal: true, &subfield_in?(%w[t]))
81
+ end
47
82
 
48
83
  # Single-valued Title, for use in headings. Takes the first {https://www.oclc.org/bibformats/en/2xx/245.html 245}
49
84
  # value. Special consideration for
@@ -192,6 +227,43 @@ module PennMARC
192
227
  { prefix: '', filing: title.strip }
193
228
  end
194
229
  end
230
+
231
+ # Evaluate {https://www.loc.gov/marc/bibliographic/bdleader.html MARC leader} to determine if record is a serial.
232
+ # @param [MARC::Record] record
233
+ # @return [Boolean]
234
+ def not_a_serial?(record)
235
+ !record.leader[6..7].ends_with?('s')
236
+ end
237
+
238
+ # @param [MARC::DataField] field
239
+ # @param [String] value
240
+ # @return [Boolean]
241
+ def indicators_are_not_value?(field, value)
242
+ field.indicator1 != value && field.indicator2 != value
243
+ end
244
+
245
+ # Retrieve auxiliary title values. Returns no values if a journal is expected but the
246
+ # {https://www.loc.gov/marc/bibliographic/bdleader.html MARC leader} indicates that the record is not a serial.
247
+ # We take special consideration for the {https://www.loc.gov/marc/bibliographic/bd505.html 505 field}, extracting
248
+ # values only when indicator1 and indicator2 are both '0'.
249
+ # @param [MARC::Record] record
250
+ # @param [Symbol] title_type
251
+ # @param [Boolean] journal
252
+ # @bloc [Proc] join_selector
253
+ # @return [Array<String>]
254
+ def search_aux_values(record:, title_type:, journal: false, &join_selector)
255
+ return [] if journal && not_a_serial?(record)
256
+
257
+ tags = AUX_TITLE_TAGS[title_type] + ['880']
258
+
259
+ record.fields(tags).filter_map do |field|
260
+ next if field.tag == '505' && indicators_are_not_value?(field, '0')
261
+
262
+ next if field.tag == '880' && subfield_value_not_in?(field, '6', tags)
263
+
264
+ join_subfields(field, &join_selector)
265
+ end
266
+ end
195
267
  end
196
268
  end
197
269
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PennMARC
4
- VERSION = '1.0.6'
4
+ VERSION = '1.0.7'
5
5
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'PennMARC::Link' do
4
+ include MarcSpecHelpers
5
+
6
+ let(:helper) { PennMARC::Link }
7
+
8
+ describe '.full_text' do
9
+ let(:record) do
10
+ marc_record fields: [marc_field(tag: '856', subfields: { '3': 'Materials specified',
11
+ z: 'Public note',
12
+ y: 'Link text',
13
+ u: 'https://www.test-uri.com/' },
14
+ indicator1: '4', indicator2: '0')]
15
+ end
16
+
17
+ it 'returns full text link text and url' do
18
+ expect(helper.full_text(record: record)).to contain_exactly({ link_text: 'Materials specified Public note',
19
+ link_url: 'https://www.test-uri.com/' })
20
+ end
21
+ end
22
+
23
+ describe '.web' do
24
+ let(:record) do
25
+ marc_record fields: [marc_field(tag: '856', subfields: { '3': 'Materials specified',
26
+ z: 'Public note',
27
+ y: 'Link text',
28
+ u: 'https://www.test-uri.com/' },
29
+ indicator1: '4', indicator2: '')]
30
+ end
31
+
32
+ it 'returns web link text and url' do
33
+ expect(helper.web(record: record)).to contain_exactly({ link_text: 'Materials specified Public note',
34
+ link_url: 'https://www.test-uri.com/' })
35
+ end
36
+ end
37
+ end
@@ -21,7 +21,86 @@ describe 'PennMARC::Title' do
21
21
  end
22
22
 
23
23
  describe '.search_aux' do
24
- it 'returns search aux values', pending: 'Not implemented yet'
24
+ let(:leader) { 'ZZZZZnaaZa22ZZZZZzZZ4500' }
25
+ let(:record) do
26
+ marc_record fields: [
27
+ marc_field(tag: '130', subfields: { a: 'Uniform Title', c: '130 not included' }),
28
+ marc_field(tag: '880', subfields: { '6': '130', a: 'Alternative Uniform Title' }),
29
+ marc_field(tag: '773', subfields: { a: 'Host Uniform Title', s: '773 not included' }),
30
+ marc_field(tag: '700', subfields: { t: 'Personal Entry Title', s: '700 not included' }),
31
+ marc_field(tag: '505', subfields: { t: 'Invalid Formatted Contents Note Title' }, indicator1: 'invalid'),
32
+ marc_field(tag: '505', subfields: { t: 'Formatted Contents Note Title', s: '505 not included' },
33
+ indicator1: '0', indicator2: '0')
34
+ ], leader: leader
35
+ end
36
+
37
+ it 'returns auxiliary titles' do
38
+ expect(helper.search_aux(record)).to contain_exactly('Uniform Title', 'Host Uniform Title',
39
+ 'Alternative Uniform Title', 'Personal Entry Title',
40
+ 'Formatted Contents Note Title')
41
+ end
42
+
43
+ context 'when the leader indicates the record is a serial' do
44
+ let(:leader) { 'ZZZZZnasZa22ZZZZZzZZ4500' }
45
+
46
+ it 'returns auxiliary titles' do
47
+ expect(helper.search_aux(record)).to contain_exactly('Uniform Title', 'Host Uniform Title',
48
+ 'Alternative Uniform Title', 'Personal Entry Title',
49
+ 'Formatted Contents Note Title')
50
+ end
51
+ end
52
+ end
53
+
54
+ describe '.journal_search' do
55
+ let(:leader) { 'ZZZZZnasZa22ZZZZZzZZ4500' }
56
+ let(:record) do
57
+ marc_record fields: [
58
+ marc_field(tag: '245', subfields: { a: 'Some Journal Title' }),
59
+ marc_field(tag: '880', subfields: { a: 'Alternative Script', '6': '245' }),
60
+ marc_field(tag: '880', subfields: { a: 'Unrelated 880', '6': 'invalid' })
61
+ ], leader: leader
62
+ end
63
+
64
+ it 'returns journal search titles' do
65
+ expect(helper.journal_search(record)).to contain_exactly('Some Journal Title', 'Alternative Script')
66
+ end
67
+
68
+ context 'when the record is not a serial' do
69
+ let(:leader) { 'ZZZZZnaaZa22ZZZZZzZZ4500' }
70
+
71
+ it 'returns an empty array' do
72
+ expect(helper.journal_search_aux(record)).to be_empty
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '.journal_search_aux' do
78
+ let(:leader) { 'ZZZZZnasZa22ZZZZZzZZ4500' }
79
+ let(:record) do
80
+ marc_record fields: [
81
+ marc_field(tag: '130', subfields: { a: 'Uniform Title', c: '130 not included' }),
82
+ marc_field(tag: '880', subfields: { '6': '130', a: 'Alternative Uniform Title' }),
83
+ marc_field(tag: '773', subfields: { a: 'Host Uniform Title', s: '773 not included' }),
84
+ marc_field(tag: '700', subfields: { t: 'Personal Entry Title', s: '700 not included' }),
85
+ marc_field(tag: '505', subfields: { t: 'Invalid Formatted Contents Note Title' }, indicator1: 'invalid'),
86
+ marc_field(tag: '505', subfields: { t: 'Formatted Contents Note Title', s: '505 not included' },
87
+ indicator1: '0', indicator2: '0')
88
+ ], leader: leader
89
+ end
90
+
91
+ it 'returns auxiliary journal search titles' do
92
+ expect(helper.journal_search_aux(record)).to contain_exactly('Uniform Title', 'Alternative Uniform Title',
93
+ 'Host Uniform Title', 'Personal Entry Title',
94
+ 'Formatted Contents Note Title')
95
+ end
96
+
97
+ context 'when the record is not a serial' do
98
+ let(:leader) { 'ZZZZZnaaZa22ZZZZZzZZ4500' }
99
+
100
+ it 'returns an empty array' do
101
+ expect(helper.journal_search_aux(record)).to be_empty
102
+ end
103
+ end
25
104
  end
26
105
 
27
106
  describe '.show' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pennmarc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Kanning
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-11-09 00:00:00.000000000 Z
13
+ date: 2023-12-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -128,6 +128,7 @@ files:
128
128
  - spec/lib/pennmarc/helpers/genre_spec.rb
129
129
  - spec/lib/pennmarc/helpers/identifer_spec.rb
130
130
  - spec/lib/pennmarc/helpers/language_spec.rb
131
+ - spec/lib/pennmarc/helpers/link_spec.rb
131
132
  - spec/lib/pennmarc/helpers/location_spec.rb
132
133
  - spec/lib/pennmarc/helpers/note_spec.rb
133
134
  - spec/lib/pennmarc/helpers/production_spec.rb