pennmarc 1.0.6 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
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