pennmarc 1.0.15 → 1.0.16

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: 06f7c00af4fabc0cb127ce3bfc16e02e2b13611db0aa8184fca48392a5776295
4
- data.tar.gz: c34a742ed5b5bc1f1eb1d76a564738e0e48df3607e59ec69851a7428876225cb
3
+ metadata.gz: 248327cbd8c1447d2ff8a6d857d47a12cc9c5952f3432f86ef559faa3ca4201c
4
+ data.tar.gz: a5fd079bf3e1a0bf6fcb77de70e78f0dbd75b330b7c3ddfeb70c3aa9a1b29837
5
5
  SHA512:
6
- metadata.gz: f64552e53b3547222f785bf3f24f593448a3e4587baa309d194ca48fee961d9846b40c590b4e2a8709592557c95a0c52b922c5c4fe9be89058cc85aefe2c2e76
7
- data.tar.gz: 999d8a04a067bf9a1a88d4d0adba0947fda547446b89d5f989541ea18c087d0ab804dde297f67704cbfcd6ca5be4047db4296ffb87f4e68b9c81e991b954c316
6
+ metadata.gz: e10f30375935e9b9f4adba832b935c350a6e7f2460d4742f3951edf6d5b7c7dddc261335bf634ccc282893b2d24135fe2551b027e5d8ecfaba6886ba6fd98d88
7
+ data.tar.gz: fe87ecedca3979f909d2e2d64cfd956b3e9c3836eb2dcb3f23f60aef07c1754a1621a4ab84cdeb5be699e0c1029a58a34c70b6fa3d18069d95c83d1635ed751a
@@ -16,15 +16,16 @@ module PennMARC
16
16
  # @param [MARC::Record] record
17
17
  # @return [Array]
18
18
  def facet(record)
19
- acc = record.filter_map do |field|
19
+ values = record.filter_map do |field|
20
20
  next AT_THE_LIBRARY if physical_holding_tag?(field)
21
21
  next ONLINE if electronic_holding_tag?(field)
22
22
  end
23
23
 
24
- return acc if acc.size == 2 # return early if all values are already present
24
+ return values if values.size == 2 # return early if all values are already present
25
25
 
26
- acc << ONLINE if acc.exclude?(ONLINE) && finding_aid_linkage?(record) # only check if ONLINE isn't already there
27
- acc
26
+ # only check if ONLINE isn't already there
27
+ values << ONLINE if values.exclude?(ONLINE) && finding_aid_linkage?(record)
28
+ values.uniq
28
29
  end
29
30
 
30
31
  private
@@ -27,7 +27,7 @@ module PennMARC
27
27
  WEBSITE_DATABASE = 'Website/Database'
28
28
 
29
29
  # Get any Format values from {https://www.oclc.org/bibformats/en/3xx/300.html 300},
30
- # 254, 255, 310, 342, 352 or {https://www.oclc.org/bibformats/en/3xx/340.html 340} field. based on the source
30
+ # 254, 255, 310, 342, 352, 362 or {https://www.oclc.org/bibformats/en/3xx/340.html 340} field. based on the source
31
31
  # field, different subfields are used.
32
32
  # @note ported from get_format_display
33
33
  # @param [MARC::Record] record
@@ -39,6 +39,9 @@ module PennMARC
39
39
  end
40
40
  results += record.fields('340').map { |f| join_subfields(f, &subfield_not_in?(%w[0 2 6 8])) }
41
41
  results += record.fields('880').map do |f|
42
+ # skip any 880s associated with non format fields
43
+ next unless subfield_value_in?(f, '6', %w[254 255 300 310 340 342 352 362])
44
+
42
45
  subfield_to_ignore = if subfield_value?(f, 6, /^300/)
43
46
  %w[3 6 8]
44
47
  elsif subfield_value?(f, 6, /^340/)
@@ -4,6 +4,7 @@ module PennMARC
4
4
  # Methods that return Library and Location values from Alma enhanced MARC fields
5
5
  class Location < Helper
6
6
  ONLINE_LIBRARY = 'Online library'
7
+ WEB_LOCATION_CODE = 'web'
7
8
 
8
9
  class << self
9
10
  # Retrieves library location from enriched marc 'itm' or 'hld' fields, giving priority to the item location over
@@ -41,43 +42,45 @@ module PennMARC
41
42
  # @param [Hash] location_map hash with location_code as key and location hash as value
42
43
  # @return [Array<String>]
43
44
  def location(record:, display_value:, location_map:)
44
- # get enriched marc location tag and subfield code
45
- location_tag_and_subfield_code(record) => {tag:, subfield_code:}
45
+ # get enriched marc location tag and relevant subfields
46
+ enriched_location_tag_and_subfields(record) => {tag:, location_code_sf:, call_num_sf:}
46
47
 
47
48
  locations = record.fields(tag).flat_map { |field|
48
49
  field.filter_map { |subfield|
49
50
  # skip unless subfield code does not match enriched marc tag subfield code
50
- next unless subfield.code == subfield_code
51
+ next unless subfield.code == location_code_sf
51
52
 
52
- # skip if subfield value is 'web'
53
- # we don't facet for 'web' which is the 'Penn Library Web' location used in Voyager.
54
- # this location should eventually go away completely with data cleanup in Alma.
55
- next if subfield.value == 'web'
53
+ location_code = subfield.value
56
54
 
57
- # skip unless subfield value is a key in location_map
58
- # sometimes "happening locations" are mistakenly used in holdings records.
59
- # that's a data problem that should be fixed.
60
- # here, if we encounter a code we can't map, we ignore it, for faceting purposes
61
- next unless location_map.key?(subfield.value.to_sym)
55
+ next if location_code_to_ignore?(location_map, location_code)
62
56
 
63
- location_map[subfield.value.to_sym][display_value.to_sym]
57
+ override = specific_location_override(display_value: display_value, location_code: location_code,
58
+ field: field, call_num_sf: call_num_sf)
59
+
60
+ if override.present?
61
+ override
62
+ else
63
+ location_map[location_code.to_sym][display_value.to_sym]
64
+ end
64
65
  }.flatten.compact_blank
65
66
  }.uniq
66
67
  if record.tags.intersect?([Enriched::Pub::ELEC_INVENTORY_TAG, Enriched::Api::ELEC_INVENTORY_TAG])
67
68
  locations << ONLINE_LIBRARY
68
69
  end
70
+
69
71
  locations
70
72
  end
71
73
 
72
74
  private
73
75
 
74
- # Determine enriched marc location tag ('itm' or 'hld') and subfield code, giving priority to using 'itm' tag and
75
- # subfield.
76
+ # Determine enriched marc location tag, location code subfield, and call number subfield,
77
+ # giving priority to using 'itm', 'AVA', or 'AVE' fields.
76
78
  # @param [MARC::Record]
77
79
  # @return [Hash<String, String>] containing location tag and subfield code
78
80
  # - `:tag` (String): The enriched marc location tag
79
- # - `:subfield_code` (String): The relevant subfield code
80
- def location_tag_and_subfield_code(record)
81
+ # - `:location_code_sf` (String): The subfield code where location code is stored
82
+ # - `:call_num_sf` (String): The subfield code where call number is stored
83
+ def enriched_location_tag_and_subfields(record)
81
84
  # in holdings records, the shelving location is always the permanent location.
82
85
  # in item records, the current location takes into account
83
86
  # temporary locations and permanent locations. if you update the item's perm location,
@@ -89,18 +92,57 @@ module PennMARC
89
92
  # if the record has an enriched item field present, use it
90
93
  if field_defined?(record, Enriched::Pub::ITEM_TAG)
91
94
  tag = Enriched::Pub::ITEM_TAG
92
- subfield_code = Enriched::Pub::ITEM_CURRENT_LOCATION
93
- # if the record has API inventory tags, use them
95
+ location_code_sf = Enriched::Pub::ITEM_CURRENT_LOCATION
96
+ call_num_sf = Enriched::Pub::ITEM_CALL_NUMBER
97
+ # if the record has API inventory tags, use them
94
98
  elsif field_defined?(record, Enriched::Api::PHYS_INVENTORY_TAG)
95
99
  tag = Enriched::Api::PHYS_INVENTORY_TAG
96
- subfield_code = Enriched::Api::PHYS_LOCATION_CODE
97
- # otherwise use Pub holding tags
100
+ location_code_sf = Enriched::Api::PHYS_LOCATION_CODE
101
+ call_num_sf = Enriched::Api::PHYS_CALL_NUMBER
102
+ # otherwise use Pub holding tags
98
103
  else
99
104
  tag = Enriched::Pub::PHYS_INVENTORY_TAG
100
- subfield_code = Enriched::Pub::PHYS_LOCATION_CODE
105
+ location_code_sf = Enriched::Pub::PHYS_LOCATION_CODE
106
+ call_num_sf = Enriched::Pub::HOLDING_CLASSIFICATION_PART
107
+ end
108
+
109
+ { tag: tag, location_code_sf: location_code_sf, call_num_sf: call_num_sf }
110
+ end
111
+
112
+ # Determines whether to ignore a location code.
113
+ # We ignore location codes that are not keys in the location map. Sometimes "happening locations" are
114
+ # mistakenly used in holdings records. That's a data problem that should be fixed. If we encounter a code we can't
115
+ # map, we ignore it, for faceting purposes. We also ignore the location code 'web'. We don't facet for 'web'
116
+ # which is the 'Penn Library Web' location used in Voyager. This location should eventually go away completely
117
+ # with data cleanup in Alma.
118
+ # @param [location_map] location_map hash with location_code as key and location hash as value
119
+ # @param [location_code] location_code retrieved from record
120
+ # @return [Boolean]
121
+ def location_code_to_ignore?(location_map, location_code)
122
+ location_map.key?(location_code.to_sym) == false || location_code == WEB_LOCATION_CODE
123
+ end
124
+
125
+ # Retrieves a specific location override based on location code and call number. Specific location overrides are
126
+ # located in `location_overrides.yml`.
127
+ # @param [String] display_value
128
+ # @param [String] location_code
129
+ # @param [MARC::Field] field
130
+ # @param [String] call_num_sf
131
+ # @return [String, Nil]
132
+ def specific_location_override(display_value:, location_code:, field:, call_num_sf:)
133
+ return unless display_value == :specific_location
134
+
135
+ locations_overrides = Mappers.location_overrides
136
+
137
+ call_numbers = subfield_values(field, call_num_sf)
138
+
139
+ override = locations_overrides.select do |_key, value|
140
+ value[:location_code] == location_code && call_numbers.any? { |num| num.match?(value[:call_num_pattern]) }
101
141
  end
102
142
 
103
- { tag: tag, subfield_code: subfield_code }
143
+ return if override.blank?
144
+
145
+ override[override.keys.first][:specific_location]
104
146
  end
105
147
  end
106
148
  end
@@ -166,12 +166,19 @@ module PennMARC
166
166
  system_details_notes.uniq
167
167
  end
168
168
 
169
+ # Retrieve "With" notes for display from field {https://www.loc.gov/marc/bibliographic/bd501.html 501}
170
+ # @param [Marc::Record] record
171
+ # @return [Array<String>]
172
+ def bound_with_show(record)
173
+ record.fields('501').filter_map { |field| join_subfields(field, &subfield_in?(['a'])).presence }.uniq
174
+ end
175
+
169
176
  private
170
177
 
171
178
  # For system details: extract subfield ǂ3 plus other subfields as specified by passed-in block. Pays special
172
179
  # attention to punctuation, joining subfield ǂ3 values with a colon-space (': ').
173
180
  # @param [MARC::DataField] field
174
- # @param [Proc] selector
181
+ # @param [Proc] &
175
182
  # @return [String]
176
183
  def sub3_and_other_subs(field, &)
177
184
  sub3 = field.filter_map { |sf| trim_trailing('period', sf.value) if sf.code == '3' }.join(': ')
@@ -160,7 +160,6 @@ module PennMARC
160
160
 
161
161
  # Format a term hash as a string for display
162
162
  #
163
- # @todo confirm punctuation handling
164
163
  # @todo support search field formatting?
165
164
  # @param [Symbol] type
166
165
  # @param [Hash] term components and information as a hash
@@ -171,11 +170,12 @@ module PennMARC
171
170
  # attempt to handle poorly coded record
172
171
  normalize_single_subfield(term[:parts].first) if term[:count] == 1 && term[:parts].first.present?
173
172
 
174
- case type.to_sym
173
+ case type
175
174
  when :facet
176
- term[:parts].join('--').strip
175
+ trim_trailing(:period, term[:parts].join('--').strip)
177
176
  when :display
178
- "#{term[:parts].join('--')} #{term[:append].join(' ')}".strip
177
+ display_value = "#{term[:parts].join('--')} #{term[:append].join(' ')}".strip
178
+ display_value.ends_with?('.') ? display_value : "#{display_value}."
179
179
  end
180
180
  end
181
181
 
@@ -225,9 +225,8 @@ module PennMARC
225
225
  # because we're using (where? - MK) "subdivision count" as a heuristic for the quality level of the
226
226
  # heading. - MG
227
227
  # @todo do i need all this?
228
- # @todo do i need to handle punctuation? see append_new_part
229
228
  # @param [MARC::DataField] field
230
- # @return [Hash{Symbol => Integer, Array}, Nil]
229
+ # @return [Hash{Symbol => Integer, Array, Boolean}, Nil]
231
230
  def build_subject_hash(field)
232
231
  term_info = { count: 0, parts: [], append: [], uri: nil,
233
232
  local: field.indicator2 == '4' || field.tag.starts_with?('69'), # local subject heading
@@ -243,6 +242,9 @@ module PennMARC
243
242
  # filter out PRO/CHR entirely (but only need to check on local heading types)
244
243
  return nil if term_info[:local] && subfield.value =~ /^%?(PRO|CHR)([ $]|$)/
245
244
 
245
+ # remove trailing punctuation of previous subject part
246
+ trim_trailing_commas_or_periods!(term_info[:parts].last)
247
+
246
248
  term_info[:parts] << subfield.value.strip
247
249
  term_info[:count] += 1
248
250
  when '2'
@@ -252,11 +254,16 @@ module PennMARC
252
254
  term_info[:append] << subfield.value.strip # TODO: map relator code?
253
255
  when 'b', 'c', 'd', 'p', 'q', 't'
254
256
  # these are appended to the last component (part) if possible (i.e., when joined, should have no delimiter)
255
- term_info[:parts].last << ", #{subfield.value.strip}"
257
+ # assume that there is an 'a' preceding value
258
+ term_info[:parts].last << " #{subfield.value.strip}"
256
259
  term_info[:count] += 1
257
260
  else
258
261
  # the usual case; add a new component to `parts`
259
262
  # this typically includes g, v, x, y, z, 4
263
+
264
+ # remove trailing punctuation of previous subject part
265
+ trim_trailing_commas_or_periods!(term_info[:parts].last)
266
+
260
267
  term_info[:parts] << subfield.value.strip
261
268
  term_info[:count] += 1
262
269
  end
@@ -299,6 +306,15 @@ module PennMARC
299
306
  first_part.gsub!(/([[[:alpha:]])])\s+-\s+([[:upper:]]|[[:digit:]]{2,})/, '\1--\2')
300
307
  first_part.gsub!(/([[[:alnum:]])])\s+-\s+([[:upper:]])/, '\1--\2')
301
308
  end
309
+
310
+ # removes trailing comma or period, manipulating the string in place
311
+ # @param [String, NilClass] subject_part
312
+ # @return [String, NilClass]
313
+ def trim_trailing_commas_or_periods!(subject_part)
314
+ return if subject_part.blank?
315
+
316
+ trim_trailing!(:comma, subject_part) || trim_trailing!(:period, subject_part)
317
+ end
302
318
  end
303
319
  end
304
320
  end
@@ -18,6 +18,11 @@ module PennMARC
18
18
  @location ||= load_map('locations.yml')
19
19
  end
20
20
 
21
+ # @return [Hash]
22
+ def location_overrides
23
+ @location_overrides ||= load_map('location_overrides.yml')
24
+ end
25
+
21
26
  # @return [Hash]
22
27
  def relator
23
28
  @relator ||= load_map('relator.yml')
@@ -0,0 +1,5 @@
1
+ albrecht:
2
+ location_code: 'vanp'
3
+ call_num_pattern: '^M.*'
4
+ specific_location: 'Van Pelt - Albrecht Music Library'
5
+
data/lib/pennmarc/util.rb CHANGED
@@ -5,6 +5,13 @@ require_relative 'heading_control'
5
5
  module PennMARC
6
6
  # class to hold "utility" methods used in MARC parsing methods
7
7
  module Util
8
+ TRAILING_PUNCTUATIONS_PATTERNS = { semicolon: /\s*;\s*$/,
9
+ colon: /\s*:\s*$/,
10
+ equal: /=$/,
11
+ slash: %r{\s*/\s*$},
12
+ comma: /\s*,\s*$/,
13
+ period: /\.\s*$/ }.freeze # TODO: revise to exclude "etc."
14
+
8
15
  # Check if a given record has a field present by tag (e.g., '041')
9
16
  # @param [MARC::Record] record
10
17
  # @param [String] marc_field
@@ -115,14 +122,17 @@ module PennMARC
115
122
 
116
123
  # @param [Symbol|String] trailer to target for removal
117
124
  # @param [String] string to modify
125
+ # @return [String]
118
126
  def trim_trailing(trailer, string)
119
- map = { semicolon: /\s*;\s*$/,
120
- colon: /\s*:\s*$/,
121
- equal: /=$/,
122
- slash: %r{\s*/\s*$},
123
- comma: /\s*,\s*$/,
124
- period: /\.\s*$/ } # TODO: revise to exclude "etc."
125
- string.sub map[trailer.to_sym], ''
127
+ string.sub TRAILING_PUNCTUATIONS_PATTERNS[trailer.to_sym], ''
128
+ end
129
+
130
+ # trim trailing punctuation, manipulating string in place
131
+ # @param [Symbol|String] trailer to target for removal
132
+ # @param [String] string to modify
133
+ # @return [String, Nil] string to modify
134
+ def trim_trailing!(trailer, string)
135
+ string.sub! TRAILING_PUNCTUATIONS_PATTERNS[trailer.to_sym], ''
126
136
  end
127
137
 
128
138
  # Intelligently append given punctuation to the end of a string
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PennMARC
4
- VERSION = '1.0.15'
4
+ VERSION = '1.0.16'
5
5
  end
@@ -181,6 +181,20 @@ describe 'PennMARC::Format' do
181
181
  expect(value.join(' ')).not_to include 'excluded'
182
182
  end
183
183
  end
184
+
185
+ context 'with an unrelated linked alternate' do
186
+ let(:fields) do
187
+ [marc_field(tag: '300', subfields: { a: '96 pages ;', c: '23 cm ' }),
188
+ marc_field(tag: '880', subfields: { '6': '700', a: 'Schweizer, Shlomo,', d: '1903-',
189
+ '0': 'https://id.loc.gov/authorities/names/no95018724' })]
190
+ end
191
+
192
+ it 'returns the expected format values' do
193
+ value = helper.show(record)
194
+ expect(value).to contain_exactly '96 pages ; 23 cm'
195
+ expect(value.join(' ')).not_to include 'https://id.loc.gov/authorities/names/no95018724'
196
+ end
197
+ end
184
198
  end
185
199
 
186
200
  describe '.other_show' do
@@ -111,4 +111,20 @@ describe 'PennMARC::Location' do
111
111
  end
112
112
  end
113
113
  end
114
+
115
+ context 'with a specific location override' do
116
+ let(:record) do
117
+ marc_record(fields: [marc_field(tag: enriched_marc::Pub::ITEM_TAG,
118
+ subfields: { enriched_marc::Pub::ITEM_CURRENT_LOCATION => 'vanp',
119
+ enriched_marc::Pub::ITEM_CALL_NUMBER => 'ML3534 .D85 1984' }),
120
+ marc_field(tag: enriched_marc::Pub::ITEM_TAG,
121
+ subfields: { enriched_marc::Pub::ITEM_CURRENT_LOCATION => 'stor',
122
+ enriched_marc::Pub::ITEM_CALL_NUMBER => 'L3534 .D85 1984' })])
123
+ end
124
+
125
+ it 'returns expected values' do
126
+ expect(helper.location(record: record, display_value: :specific_location, location_map: mapping))
127
+ .to(contain_exactly(PennMARC::Mappers.location_overrides[:albrecht][:specific_location], 'LIBRA'))
128
+ end
129
+ end
114
130
  end
@@ -296,5 +296,23 @@ text for URI http://www.universal.resource/locator'.squish,
296
296
  )
297
297
  end
298
298
  end
299
+
300
+ describe '.bound_with_show' do
301
+ let(:record) { marc_record(fields: fields) }
302
+
303
+ let(:fields) do
304
+ [
305
+ marc_field(tag: '501', subfields: { a: 'With: Peer Gynt (Suite) no. 1-2 / Edvard Grieg' }),
306
+ marc_field(tag: '501', subfields: { a: 'With: Schumann, C. Romances, piano, op. 11. No. 2' })
307
+ ]
308
+ end
309
+
310
+ let(:values) { helper.bound_with_show(record) }
311
+
312
+ it 'returns expected values' do
313
+ expect(values).to contain_exactly('With: Peer Gynt (Suite) no. 1-2 / Edvard Grieg',
314
+ 'With: Schumann, C. Romances, piano, op. 11. No. 2')
315
+ end
316
+ end
299
317
  end
300
318
  end
@@ -101,7 +101,7 @@ describe 'PennMARC::Subject' do
101
101
  let(:fields) do
102
102
  [marc_field(tag: '650', indicator2: '7',
103
103
  subfields: {
104
- a: 'Libraries', d: '22nd Century', x: 'History', e: 'relator',
104
+ a: 'Libraries,', d: '22nd Century,', x: 'History.', e: 'relator',
105
105
  '2': 'fast', '0': 'http://fast.org/libraries'
106
106
  })]
107
107
  end
@@ -127,6 +127,21 @@ describe 'PennMARC::Subject' do
127
127
  expect(values.first).to eq 'Libraries, 22nd Century--History'
128
128
  end
129
129
  end
130
+
131
+ context 'with a record with trailing periods' do
132
+ let(:fields) do
133
+ [marc_field(tag: '600', indicator2: '0',
134
+ subfields: {
135
+ a: 'R.G.', q: '(Robert Gordon).',
136
+ t: 'Spiritual order and Christian liberty proved to be consistent in the Churches of Christ. '
137
+ })]
138
+ end
139
+
140
+ it 'drops the final trailing period' do
141
+ expect(values).to contain_exactly('R.G. (Robert Gordon). Spiritual order and Christian liberty proved ' \
142
+ 'to be consistent in the Churches of Christ')
143
+ end
144
+ end
130
145
  end
131
146
 
132
147
  describe '.show' do
@@ -146,8 +161,8 @@ describe 'PennMARC::Subject' do
146
161
  end
147
162
 
148
163
  it 'shows all valid subject headings without duplicates' do
149
- expect(helper.show(record)).to contain_exactly('Nephrology--Periodicals', 'Nephrology', 'Kidney Diseases',
150
- 'Local Heading')
164
+ expect(helper.show(record)).to contain_exactly('Nephrology--Periodicals.', 'Nephrology.', 'Kidney Diseases.',
165
+ 'Local Heading.')
151
166
  end
152
167
  end
153
168
 
@@ -155,7 +170,7 @@ describe 'PennMARC::Subject' do
155
170
  let(:fields) do
156
171
  [marc_field(tag: '650', indicator2: '0', subfields: {
157
172
  a: 'Subways',
158
- z: ['Pennsylvania', 'Philadelphia Metropolitan Area'],
173
+ z: ['Pennsylvania,', 'Philadelphia Metropolitan Area,'],
159
174
  v: 'Maps',
160
175
  y: '1989',
161
176
  e: 'relator'
@@ -163,7 +178,7 @@ describe 'PennMARC::Subject' do
163
178
  end
164
179
 
165
180
  it 'properly formats the heading parts' do
166
- expect(values.first).to eq 'Subways--Pennsylvania--Philadelphia Metropolitan Area--Maps--1989 relator'
181
+ expect(values.first).to eq 'Subways--Pennsylvania--Philadelphia Metropolitan Area--Maps--1989 relator.'
167
182
  end
168
183
  end
169
184
 
@@ -171,20 +186,37 @@ describe 'PennMARC::Subject' do
171
186
  let(:fields) do
172
187
  [marc_field(tag: '600', indicator2: '7', subfields: {
173
188
  a: 'Franklin, Benjamin,',
174
- d: '1706-1790',
189
+ d: '1706-1790.',
175
190
  '2': 'fast',
176
191
  '0': 'http://id.worldcat.org/fast/34115'
177
192
  }),
178
193
  marc_field(tag: '600', indicator1: '1', indicator2: '0', subfields: {
179
194
  a: 'Franklin, Benjamin,',
180
- d: '1706-1790.',
195
+ d: '1706-1790',
181
196
  x: 'As inventor.'
197
+ }),
198
+ marc_field(tag: '650', indicator1: '1', indicator2: '0', subfields: {
199
+ a: 'Franklin stoves.'
182
200
  })]
183
201
  end
184
202
 
185
- it 'returns what Franklin shows', pending: 'proper handling of punctuation in subject parts' do
203
+ it 'properly handles punctuation in subject parts' do
186
204
  expect(values).to contain_exactly 'Franklin, Benjamin, 1706-1790.',
187
- 'Franklin, Benjamin, 1706-1790--As inventor.'
205
+ 'Franklin, Benjamin, 1706-1790--As inventor.', 'Franklin stoves.'
206
+ end
207
+ end
208
+
209
+ context 'with a record without trailing period in last subject part' do
210
+ let(:fields) do
211
+ [marc_field(tag: '651', indicator2: '7',
212
+ subfields: {
213
+ a: 'New York State (State)', z: 'New York', '2': 'fast', '0': '(OCoLC)fst01204333',
214
+ '1': 'https://id.oclc.org/worldcat/entity/E39QbtfRvQh7864Jh4rDGBFDWc'
215
+ })]
216
+ end
217
+
218
+ it 'adds a trailing period' do
219
+ expect(values).to contain_exactly('New York State (State)--New York.')
188
220
  end
189
221
  end
190
222
 
@@ -200,40 +232,40 @@ describe 'PennMARC::Subject' do
200
232
  end
201
233
 
202
234
  it 'properly formats the heading parts' do
203
- expect(values.first).to eq 'Chicago (Ill.)--Moral conditions--Church minutes--1875-1878'
235
+ expect(values.first).to eq 'Chicago (Ill.)--Moral conditions--Church minutes--1875-1878.'
204
236
  end
205
237
  end
206
238
 
207
239
  context 'with a robust 611 heading including many subfields' do
208
240
  let(:fields) do
209
241
  [marc_field(tag: '611', indicator2: '0', subfields: {
210
- a: 'Conference',
211
- c: ['(Johannesburg, South Africa', 'Cape Town, South Africa'],
242
+ a: 'Conference,',
243
+ c: ['(Johannesburg, South Africa,', 'Cape Town, South Africa,'],
212
244
  d: '2002)',
213
245
  n: '2nd'
214
246
  })]
215
247
  end
216
248
 
217
249
  it 'properly formats the heading parts' do
218
- expect(values.first).to eq 'Conference, (Johannesburg, South Africa, Cape Town, South Africa, 2002)--2nd'
250
+ expect(values.first).to eq 'Conference, (Johannesburg, South Africa, Cape Town, South Africa, 2002)--2nd.'
219
251
  end
220
252
  end
221
253
 
222
254
  context 'with a robust 600 heading including many subfields' do
223
255
  let(:fields) do
224
256
  [marc_field(tag: '600', indicator2: '0', subfields: {
225
- a: 'Person, Significant Author',
226
- b: 'Numerator',
227
- c: %w[Title Rank],
228
- d: '1899-1971',
257
+ a: 'Person, Significant Author,',
258
+ b: 'Numerator,',
259
+ c: ['Title,', 'Rank,'],
260
+ d: '1899-1971,',
229
261
  t: 'Collection',
230
- v: 'Early works to 1950'
262
+ v: 'Early works to 1950.'
231
263
  })]
232
264
  end
233
265
 
234
266
  it 'properly formats the heading parts' do
235
267
  expect(values.first).to eq('Person, Significant Author, Numerator, Title, Rank, 1899-1971, Collection--' \
236
- 'Early works to 1950')
268
+ 'Early works to 1950.')
237
269
  end
238
270
  end
239
271
  end
@@ -249,7 +281,7 @@ describe 'PennMARC::Subject' do
249
281
  let(:values) { helper.childrens_show(record) }
250
282
 
251
283
  it 'includes heading terms only from subject tags with an indicator 2 of "1"' do
252
- expect(values).to contain_exactly 'Frogs--Fiction', 'Toads--Fiction'
284
+ expect(values).to contain_exactly 'Frogs--Fiction.', 'Toads--Fiction.'
253
285
  end
254
286
  end
255
287
 
@@ -267,7 +299,7 @@ describe 'PennMARC::Subject' do
267
299
  end
268
300
 
269
301
  it 'includes heading terms only from subject tags with indicator 2 of "2"' do
270
- expect(helper.medical_show(record)).to contain_exactly 'Nephrology'
302
+ expect(helper.medical_show(record)).to contain_exactly 'Nephrology.'
271
303
  end
272
304
  end
273
305
 
@@ -280,7 +312,7 @@ describe 'PennMARC::Subject' do
280
312
  end
281
313
 
282
314
  it 'includes heading terms only from subject tags with indicator 2 of "4" or in the 69X range' do
283
- expect(helper.local_show(record)).to contain_exactly 'Local--Heading', 'Super Local.'
315
+ expect(helper.local_show(record)).to contain_exactly 'Local--Heading.', 'Super Local.'
284
316
  end
285
317
  end
286
318
  end
@@ -72,13 +72,14 @@ module MarcSpecHelpers
72
72
  # location_map[:stor][:library] #=> 'LIBRA'
73
73
  # @return [Hash]
74
74
  def location_map
75
- {
76
- dent: { specific_location: 'Levy Dental Medicine Library - Stacks',
75
+ { dent: { specific_location: 'Levy Dental Medicine Library - Stacks',
77
76
  library: ['Health Sciences Libraries', 'Levy Dental Medicine Library'],
78
77
  display: 'Levy Dental Medicine Library - Stacks' },
79
78
  stor: { specific_location: 'LIBRA',
80
79
  library: 'LIBRA',
81
- display: 'LIBRA' }
82
- }
80
+ display: 'LIBRA' },
81
+ vanp: { specific_location: 'Van Pelt - Stacks',
82
+ library: 'Van Pelt-Dietrich Library Center',
83
+ display: 'Van Pelt Library' } }
83
84
  end
84
85
  end
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.15
4
+ version: 1.0.16
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: 2024-02-06 00:00:00.000000000 Z
13
+ date: 2024-02-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -119,6 +119,7 @@ files:
119
119
  - lib/pennmarc/mappings/iso639-2-languages.yml
120
120
  - lib/pennmarc/mappings/iso639-3-languages.yml
121
121
  - lib/pennmarc/mappings/loc_classification.yml
122
+ - lib/pennmarc/mappings/location_overrides.yml
122
123
  - lib/pennmarc/mappings/locations.yml
123
124
  - lib/pennmarc/mappings/relator.yml
124
125
  - lib/pennmarc/parser.rb