pennmarc 1.3.3 → 1.3.4

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: cb5991760f2f82ed299e5602451881e8b71a88fec9cae1f570afe8e5c90d56f9
4
- data.tar.gz: d0fa00068ed63eb1ea9de2127f706c4076b56eb567ac29fc0e88c77a74b5605d
3
+ metadata.gz: 3409e098b2447b1f3eb3295e8df39d2b3e5133a9594f93f0b017132700b3fdd6
4
+ data.tar.gz: 02a38f6ffff4ce8032b4f60e9bcc2de3945c14b8d249bf83e2ffff26cb8c1a60
5
5
  SHA512:
6
- metadata.gz: edff4fba2a3e3edf56b7e7fb79992f595becf46e588fa81cb4ed327153deacc6a4bf2c8b040808830f5963e2c64db8a6165bfbdcb04698884ac5ebe4b997d4f0
7
- data.tar.gz: f3024891d0d48b13e18b3d25ec09e33c134cb77a152bb3849079589b893f99eef9489b8a81307cab14d941467d36242f829c0a9120d42b24064e862c9411fe80
6
+ metadata.gz: 7e3c34f9063ca03595f9e45adf23c4f8c4f27a369b874eaf9944ce11b60f16cf8298bfb7a169094c5fe9b6b1fc726eb68272b996085c5092a65d03bce9bde893
7
+ data.tar.gz: c304b6f45e769ecc13cb0b17230448a87d1f52b5fe496587663ed88383cc4a7288a4422dbca685c6229fee69fae8d6964ee9d332bb95c6aa0dd5e04ced5909b7
@@ -7,12 +7,15 @@ module PennMARC
7
7
  AT_THE_LIBRARY = 'At the library'
8
8
  HANDLE_BASE_URL = 'hdl.library.upenn.edu'
9
9
  COLENDA_BASE_URL = 'colenda.library.upenn.edu'
10
+ REQUIRED_ONLINE_MARC_INDICATOR_COUNT = 2
10
11
 
11
12
  class << self
12
13
  # Based on enhanced metadata fields added by Alma publishing process or API, determine if the record has
13
14
  # electronic access or has physical holdings, and is therefore "Online" or "At the library". If a record is "At
14
15
  # the library", but has a link to a finding aid in the 856 field (matching certain criteria), also add 'Online' as
15
16
  # an access method.
17
+ # Because Alma E-Collections don't return electronic inventory, check some MARC control fields and other fields
18
+ # for indicators of an online resource, but only if no other Online indicators are present.
16
19
  # @param record [MARC::Record]
17
20
  # @return [Array]
18
21
  def facet(record)
@@ -24,7 +27,7 @@ module PennMARC
24
27
  return values if values.size == 2 # return early if all values are already present
25
28
 
26
29
  # only check if ONLINE isn't already there
27
- values << ONLINE if values.exclude?(ONLINE) && resource_link?(record)
30
+ values << ONLINE if values.exclude?(ONLINE) && marc_indicators?(record)
28
31
  values.uniq
29
32
  end
30
33
 
@@ -44,6 +47,24 @@ module PennMARC
44
47
  field.tag.in? [Enriched::Pub::PHYS_INVENTORY_TAG, Enriched::Api::PHYS_INVENTORY_TAG]
45
48
  end
46
49
 
50
+ # In order to determine if a record has Online access, we should also check some MARC fields. This is because the
51
+ # Alma inventory publishing process does not include tags for E-Collections associated with a record. Here we
52
+ # check for obvious indicators:
53
+ # - a "Resource Link" matching certain criteria
54
+ # - a field indicating that the record is part of our curated "Databases" collection
55
+ # If there's still no online access, check for at least two other positive indicators in other MARC control fields
56
+ # and other tags.
57
+ # @param record [MARC::Record]
58
+ # @return [Boolean]
59
+ def marc_indicators?(record)
60
+ return true if resource_link?(record) || electronic_database?(record)
61
+
62
+ [eresource_form?(record),
63
+ eresource_material_designation?(record),
64
+ online_computer_file_form?(record),
65
+ online_carrier_type?(record)].count(true) >= REQUIRED_ONLINE_MARC_INDICATOR_COUNT
66
+ end
67
+
47
68
  # Check if a record contains an 856 entry with a Penn Handle server link meeting these criteria:
48
69
  # 1. Indicator 1 is 4 (HTTP resource)
49
70
  # 2. Indicator 2 is NOT 2 (indicating the linkage is to a "related" thing)
@@ -58,6 +79,58 @@ module PennMARC
58
79
  record.fields('856').any? { |field| valid_resource_field?(field) }
59
80
  end
60
81
 
82
+ # Does the record have an 006 suggesting an electronic resource?
83
+ # Check position 6 "Form of item" for an `o` indicating "Online"
84
+ # @see https://www.loc.gov/marc/bibliographic/bd006.html
85
+ # @see https://www.loc.gov/marc/bibliographic/bd008c.html
86
+ # @param record [MARC::Record]
87
+ # @return [Boolean]
88
+ def eresource_form?(record)
89
+ return false unless field_defined?(record, '006')
90
+
91
+ record.fields('006').first.value[6] == 'o'
92
+ end
93
+
94
+ # Does the record have an 007 indicating an electronic resource?
95
+ # Check pos 0 for a `c` ("Electronic resource") and position 1 for an `r` ("Remote")
96
+ # @see https://www.loc.gov/marc/bibliographic/bd007c.html
97
+ # @param record [MARC::Record]
98
+ # @return [Boolean]
99
+ def eresource_material_designation?(record)
100
+ return false unless field_defined?(record, '007')
101
+
102
+ record.fields('007').first.value[0..1] == 'cr'
103
+ end
104
+
105
+ # Does the record have an 008 indicating an electronic resource?
106
+ # https://www.loc.gov/marc/bibliographic/bd008c.html
107
+ # @param record [MARC::Record]
108
+ # @return [Boolean]
109
+ def online_computer_file_form?(record)
110
+ return false unless field_defined?(record, '008')
111
+
112
+ record.fields('008').first.value[23] == 'o'
113
+ end
114
+
115
+ # Does the record have an 338 indicating an electronic resource?
116
+ # @see https://www.loc.gov/marc/bibliographic/bd338.html, https://www.loc.gov/standards/valuelist/rdacarrier.html
117
+ # @param record [MARC::Record]
118
+ # @return [Boolean]
119
+ def online_carrier_type?(record)
120
+ return false unless field_defined?(record, '338')
121
+
122
+ return false unless subfield_values_for(tag: '338', subfield: 'a', record: record).include?('online resource')
123
+
124
+ subfield_values_for(tag: '338', subfield: 'b', record: record).include?('cr')
125
+ end
126
+
127
+ # Databases are always electronic resources
128
+ # @param record [MARC::Record]
129
+ # @return [Boolean]
130
+ def electronic_database?(record)
131
+ Database.type_facet(record).any?
132
+ end
133
+
61
134
  # Check if a field contains valid resource
62
135
  # @param field [MARC::Field]
63
136
  # @return [Boolean]
@@ -90,9 +90,10 @@ module PennMARC
90
90
  # @return [Array<String>] array of author/creator values for display
91
91
  def extended_show(record, relator_map: Mappers.relator)
92
92
  fields = record.fields(%w[100 700])
93
+ fields += record.fields('880').select { |field| subfield_value?(field, '6', /^(#{TAGS.join('|')})/) }
93
94
  fields.filter_map { |field|
94
- # for 700 entries, only include ones with relator code ('4') = aut, or code 'e' = 'author'
95
- next if field.tag == '700' && !(field['4']&.downcase == 'aut' || field['e']&.downcase&.start_with?('author'))
95
+ # for 700 and 880 entries, only include ones with relator code ('4') = aut, or code 'e' = 'author'
96
+ next if field.tag.in?(%w[700 880]) && !describes_author?(field)
96
97
 
97
98
  parse_show_value(field, relator_map: relator_map)
98
99
  }.uniq
@@ -106,7 +107,7 @@ module PennMARC
106
107
  def extended_show_facet_map(record, relator_map: Mappers.relator)
107
108
  creators = record.fields(%w[100 700]).filter_map do |field|
108
109
  # for 700 entries, only include ones with relator code ('4') = aut, or code 'e' = 'author'
109
- next if field.tag == '700' && !(field['4']&.downcase == 'aut' || field['e']&.downcase&.start_with?('author'))
110
+ next if field.tag == '700' && !describes_author?(field)
110
111
 
111
112
  show = parse_show_value(field, relator_map: relator_map)
112
113
  facet = parse_facet_value(field, FACET_SOURCE_MAP[field.tag.to_i].chars)
@@ -465,6 +466,13 @@ module PennMARC
465
466
 
466
467
  append_relator(field: field, joined_subfields: conf, relator_term_sf: 'j', relator_map: relator_map)
467
468
  end
469
+
470
+ # Does the given field describe an author record, as indicated by the relator fields of sf 4 or sf e ?
471
+ # @param [MARC::Field] field
472
+ # @return [Boolean]
473
+ def describes_author?(field)
474
+ field['4']&.downcase == 'aut' || field['e']&.downcase&.start_with?('author')
475
+ end
468
476
  end
469
477
  end
470
478
  end
@@ -66,10 +66,6 @@ carpco:
66
66
  library: Special Collections
67
67
  display: Carpenters' Company of the City and County of Philadelphia
68
68
  aeon: true
69
- catoffice:
70
- specific_location: Athenaeum of Philadelphia - Cataloging Office
71
- library: Athenaeum of Philadelphia
72
- display: Athenaeum of Philadelphia - Cataloging Office
73
69
  circcoll:
74
70
  specific_location: Athenaeum of Philadelphia - Circulating Collection
75
71
  library: Athenaeum of Philadelphia
@@ -83,10 +79,6 @@ cidirbusrm:
83
79
  library: Athenaeum of Philadelphia
84
80
  display: Athenaeum of Philadelphia - City Directories Busch Room
85
81
  aeon: true
86
- collcare:
87
- specific_location: Athenaeum of Philadelphia - Collections Care Manager
88
- library: Athenaeum of Philadelphia
89
- display: Athenaeum of Philadelphia - Collections Care Manager
90
82
  fic:
91
83
  specific_location: Athenaeum of Philadelphia - Fiction
92
84
  library: Athenaeum of Philadelphia
@@ -128,11 +120,6 @@ rarecoll:
128
120
  library: Athenaeum of Philadelphia
129
121
  display: Athenaeum of Philadelphia - Rare Books Collection
130
122
  aeon: true
131
- reevecoll:
132
- specific_location: Athenaeum of Philadelphia - Reeve Collection
133
- library: Athenaeum of Philadelphia
134
- display: Athenaeum of Philadelphia - Reeve Collection
135
- aeon: true
136
123
  ref:
137
124
  specific_location: Athenaeum of Philadelphia - Reference
138
125
  library: Athenaeum of Philadelphia
@@ -293,13 +280,6 @@ cjsarcths:
293
280
  - Library at the Katz Center for Advanced Judaic Studies
294
281
  display: Library at the Katz Center - Archives Thesis
295
282
  aeon: true
296
- cjsartif:
297
- specific_location: Library at the Katz Center - Reading Room
298
- library:
299
- - Special Collections
300
- - Library at the Katz Center for Advanced Judaic Studies
301
- display: Library at the Katz Center - Artifacts
302
- aeon: true
303
283
  cjsbox1:
304
284
  specific_location: Library at the Katz Center - Stacks
305
285
  library: Library at the Katz Center for Advanced Judaic Studies
@@ -312,22 +292,10 @@ cjsbox3:
312
292
  specific_location: Library at the Katz Center - Stacks
313
293
  library: Library at the Katz Center for Advanced Judaic Studies
314
294
  display: Library at the Katz Center - Stacks Box 3
315
- cjsbox4:
316
- specific_location: Library at the Katz Center - Stacks
317
- library: Library at the Katz Center for Advanced Judaic Studies
318
- display: Library at the Katz Center - Stacks Box 4
319
295
  cjscdrom:
320
296
  specific_location: Library at the Katz Center - Reading Room
321
297
  library: Library at the Katz Center for Advanced Judaic Studies
322
298
  display: Library at the Katz Center - CD-ROM
323
- cjscirc:
324
- specific_location: Library at the Katz Center - Reading Room
325
- library: Library at the Katz Center for Advanced Judaic Studies
326
- display: Library at the Katz Center - Circulation
327
- cjscomp:
328
- specific_location: Library at the Katz Center - Reading Room
329
- library: Library at the Katz Center for Advanced Judaic Studies
330
- display: Library at the Katz Center - Computer Room
331
299
  cjsdir:
332
300
  specific_location: Library at the Katz Center - Stacks
333
301
  library: Library at the Katz Center for Advanced Judaic Studies
@@ -438,10 +406,6 @@ cjspergal:
438
406
  specific_location: Library at the Katz Center - Reading Room
439
407
  library: Library at the Katz Center for Advanced Judaic Studies
440
408
  display: Library at the Katz Center - Periodicals Gallery
441
- cjsperover:
442
- specific_location: Library at the Katz Center - Stacks
443
- library: Library at the Katz Center for Advanced Judaic Studies
444
- display: Library at the Katz Center - Periodicals Oversize
445
409
  cjsrar:
446
410
  specific_location: Library at the Katz Center - Rare Book Room
447
411
  library:
@@ -531,12 +495,6 @@ cjsreffol+:
531
495
  specific_location: Library at the Katz Center - Reading Room
532
496
  library: Library at the Katz Center for Advanced Judaic Studies
533
497
  display: Library at the Katz Center - Reference Oversize
534
- cjsres:
535
- specific_location: Library at the Katz Center - Reading Room
536
- library:
537
- - Library at the Katz Center for Advanced Judaic Studies
538
- - Reserve
539
- display: Library at the Katz Center - Reserves
540
498
  cjsspec:
541
499
  specific_location: Library at the Katz Center - Stacks
542
500
  library:
@@ -842,12 +800,7 @@ mideast:
842
800
  specific_location: Van Pelt - Stapleton Seminar Room
843
801
  library: Van Pelt-Dietrich Library Center
844
802
  display: Van Pelt - Stapleton Seminar Room (523)
845
- mideastres:
846
- specific_location: Van Pelt - Middle East Seminar Reserve
847
- library:
848
- - Van Pelt-Dietrich Library Center
849
- - Reserve
850
- display: Van Pelt - Middle East Seminar Reserve
803
+
851
804
  mscirc:
852
805
  specific_location: Penn Museum Library - Circulation
853
806
  library: Penn Museum Library
@@ -1073,12 +1026,6 @@ sasiarefe:
1073
1026
  specific_location: Van Pelt - Govindaraju/Chandrasekhara South Asia Studies Seminar Room
1074
1027
  library: Van Pelt-Dietrich Library Center
1075
1028
  display: Van Pelt - Govindaraju/Chandrasekhara South Asia Studies Seminar Room (551)
1076
- sasiarese:
1077
- specific_location: Van Pelt - South Asian Reserve
1078
- library:
1079
- - Van Pelt-Dietrich Library Center
1080
- - Reserve
1081
- display: Van Pelt - South Asia Reserve (551)
1082
1029
  scg123:
1083
1030
  specific_location: Kislak Center for Special Collections - Rare Book Collection
1084
1031
  library:
@@ -1479,10 +1426,6 @@ stor:
1479
1426
  specific_location: LIBRA
1480
1427
  library: LIBRA
1481
1428
  display: LIBRA
1482
- storcirc:
1483
- specific_location: LIBRA - Circulation
1484
- library: LIBRA
1485
- display: LIBRA - Circulation
1486
1429
  storcrstxt:
1487
1430
  specific_location: GIC Collection at Penn Libraries
1488
1431
  library:
@@ -1526,13 +1469,6 @@ scrunning:
1526
1469
  - LIBRA
1527
1470
  display: LIBRA Running Press Collection
1528
1471
  aeon: true
1529
- storvilain:
1530
- specific_location: LIBRA Vilain-Wieck Collection
1531
- library:
1532
- - Special Collections
1533
- - LIBRA
1534
- display: LIBRA Vilain-Wieck Collection
1535
- aeon: true
1536
1472
  univarch:
1537
1473
  specific_location: University Archives
1538
1474
  library: University Archives
@@ -1649,7 +1585,7 @@ vetewellrm:
1649
1585
  - Veterinary
1650
1586
  - 'Veterinary: Atwood Library (Campus)'
1651
1587
  display: 'Veterinary: Atwood Library (Campus)- Wellness Room'
1652
- vpexhibit:
1588
+ vpexhibits:
1653
1589
  specific location: Van Pelt - Exhibits and Events
1654
1590
  library: Van Pelt-Dietrich Library Center
1655
1591
  display: Van Pelt - Exhibits and Events -- First Floor, Circulation Desk
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PennMARC
4
- VERSION = '1.3.3'
4
+ VERSION = '1.3.4'
5
5
  end
@@ -105,5 +105,64 @@ describe 'PennMARC::Access' do
105
105
  end
106
106
  end
107
107
  end
108
+
109
+ context 'with an electronic record but no electronic inventory provided' do
110
+ let(:record) { marc_record fields: fields }
111
+
112
+ context 'with physical inventory' do
113
+ let(:fields) do
114
+ [marc_field(tag: PennMARC::Enriched::Pub::PHYS_INVENTORY_TAG),
115
+ marc_field(tag: '944', subfields: { a: 'Database & Article Index',
116
+ b: 'Dictionaries and Thesauri (language based)' })]
117
+ end
118
+
119
+ it 'adds in additional Online access value' do
120
+ expect(helper.facet(record)).to contain_exactly PennMARC::Access::AT_THE_LIBRARY, PennMARC::Access::ONLINE
121
+ end
122
+ end
123
+
124
+ context 'with a 944 indicating an online database' do
125
+ let(:fields) do
126
+ [marc_field(tag: '944', subfields: { a: 'Database & Article Index',
127
+ b: 'Dictionaries and Thesauri (language based)' })]
128
+ end
129
+
130
+ it 'returns expected Online access value' do
131
+ expect(helper.facet(record)).to contain_exactly(PennMARC::Access::ONLINE)
132
+ end
133
+ end
134
+
135
+ context 'with a single MARC indicator check suggesting an online database' do
136
+ let(:fields) do
137
+ [marc_control_field(tag: '006', value: ' o ')]
138
+ end
139
+
140
+ it 'does not return Online access value' do
141
+ expect(helper.facet(record)).not_to include PennMARC::Access::ONLINE
142
+ end
143
+ end
144
+
145
+ context 'with two MARC indicators suggesting an online database' do
146
+ let(:fields) do
147
+ [marc_control_field(tag: '006', value: ' o '),
148
+ marc_control_field(tag: '007', value: 'cr')]
149
+ end
150
+
151
+ it 'returns expected Online access value' do
152
+ expect(helper.facet(record)).to contain_exactly(PennMARC::Access::ONLINE)
153
+ end
154
+ end
155
+
156
+ context 'with other two MARC indicators suggesting an online database' do
157
+ let(:fields) do
158
+ [marc_control_field(tag: '008', value: '970325c19959999nyuwr d o 0 2eng '),
159
+ marc_field(tag: '338', subfields: { a: 'online resource', b: 'cr' })]
160
+ end
161
+
162
+ it 'returns expected Online access value' do
163
+ expect(helper.facet(record)).to contain_exactly(PennMARC::Access::ONLINE)
164
+ end
165
+ end
166
+ end
108
167
  end
109
168
  end
@@ -100,12 +100,12 @@ describe 'PennMARC::Creator' do
100
100
  let(:fields) do
101
101
  [marc_field(tag: '100', subfields: { a: 'Surname, Name', '0': 'http://cool.uri/12345', d: '1900-2000',
102
102
  e: 'author.', '4': 'http://cool.uri/vocabulary/relators/aut' }),
103
- marc_field(tag: '880', subfields: { a: 'Surname, Alternative', '6': '100' })]
103
+ marc_field(tag: '880', subfields: { a: 'Surname, Alternative', e: 'author', '6': '100' })]
104
104
  end
105
105
 
106
- it 'returns single author values with no URIs anywhere (the same as show)' do
106
+ it 'returns single author values and linked alternate with no URIs anywhere (the same as show)' do
107
107
  values = helper.extended_show(record)
108
- expect(values).to contain_exactly 'Surname, Name 1900-2000, author.'
108
+ expect(values).to contain_exactly 'Surname, Name 1900-2000, author.', 'Surname, Alternative, author.'
109
109
  expect(values.join.downcase).not_to include 'http'
110
110
  end
111
111
  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.3.3
4
+ version: 1.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Kanning
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2025-09-19 00:00:00.000000000 Z
15
+ date: 2025-10-02 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activesupport