pennmarc 1.0.12 → 1.0.14

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: 97610e7a00a2d206d84d5865ce9ebf14b5aa43e8081d9c4f559d198cc7adde23
4
- data.tar.gz: fcee05f6a1999b674cb699605ee1e16b04fdda87e6bb7e65c3cb54958ef19ba5
3
+ metadata.gz: 76a91ac26d34008866dd6b66d66b9bbebf5d952d4bda7514d34a40b87f436244
4
+ data.tar.gz: 6d6813b9dcbadcf45905a48bb30dbc48fbe47ab081d8457d4936cb2263ba1c91
5
5
  SHA512:
6
- metadata.gz: '0099dbb3f521e56f8ba94527e13085058191acfdaa3625f30f050da3bb96d8ae224eb9c6e9fb54620ea6d4640800bc38bd71602a59d66871eedd26c7f6287b4e'
7
- data.tar.gz: 44cfade6b9c3ab3e0591553fbdf5c09f8be3467ccf71b0f003f7f0ff9dc8b08b1093d783b84c5f71ba3c98607a2d97f1e1ef62653cbe3af5361895d3cf964c1e
6
+ metadata.gz: a704d7a8957d042ad629446626fa8734e1d9b5c0ee65ba844343765d7c196ac0e8da3ae328213b1832556786b63a426c8ad2b4dc1e9b806795a723530d9367eb
7
+ data.tar.gz: 01f2d33cd2e7ec4a0aecb5f032bacd438a553d6d8667c5a0191e282f4722358fd23755b788a9ce21f32677d076e7224a04a73c24345f1146fd76198c42e9ebf3
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 10000`
3
- # on 2024-01-11 19:45:39 UTC using RuboCop version 1.51.0.
3
+ # on 2024-01-17 17:00:02 UTC using RuboCop version 1.51.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -60,7 +60,7 @@ Metrics/CyclomaticComplexity:
60
60
  - 'lib/pennmarc/helpers/title.rb'
61
61
  - 'lib/pennmarc/util.rb'
62
62
 
63
- # Offense count: 25
63
+ # Offense count: 26
64
64
  # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
65
65
  Metrics/MethodLength:
66
66
  Exclude:
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Constants for Alma's MARC enrichment, performed and included in the MARCXML either by the Publishing process or by
4
+ # API service
5
+ module PennMARC
6
+ module Enriched
7
+ # Enriched MARC fields added by configurable setting in the Publishing profile that generates the MARCXML
8
+ # TODO: review if we can/should modify the subfields used in the pub profile to create parity with API subfields as
9
+ # that could simplify this mapping tremendously
10
+ module Pub
11
+ # Enrichment Tag Names
12
+ PHYS_INVENTORY_TAG = 'hld'
13
+ ELEC_INVENTORY_TAG = 'prt'
14
+ ITEM_TAG = 'itm'
15
+
16
+ # Subfields for HLD tags
17
+ # Follow MARC 852 spec: https://www.loc.gov/marc/holdings/hd852.html, but names are translated into Alma parlance
18
+ PHYS_LOCATION_NAME = 'b' # e.g., Libra
19
+ PHYS_LOCATION_CODE = 'c' # e.g., stor
20
+ HOLDING_CLASSIFICATION_PART = 'h' # "classification part" first part of call num e.g., KF6450
21
+ HOLDING_ITEM_PART = 'i' # "item part?" second part of call num e.g., .C59 1989
22
+ PHYS_PUBLIC_NOTE = 'z'
23
+ PHYS_INTERNAL_NOTE = 'x'
24
+ PHYS_HOLDING_ID = '8'
25
+
26
+ # Subfields for ITM tags
27
+ ITEM_CURRENT_LOCATION = 'g'
28
+ ITEM_CALL_NUMBER_TYPE = 'h'
29
+ ITEM_CALL_NUMBER = 'i'
30
+ ITEM_DATE_CREATED = 'q'
31
+
32
+ # Subfields for PRT tags
33
+ ELEC_PORTFOLIO_ID = 'a'
34
+ ELEC_SERVICE_URL = 'b'
35
+ ELEC_COLLECTION_NAME = 'c'
36
+ ELEC_INTERFACE_NAME = 'e'
37
+ ELEC_PUBLIC_NOTE = 'f'
38
+ ELEC_COVERAGE_STMT = 'g'
39
+
40
+ # other values that could be added if we configured the Alma pub profile (and values are set on the record)
41
+ # - Authentication note
42
+ # - "static URL"
43
+ # - Electronic material type
44
+ # - Collection ID
45
+ # - create/update/activation date
46
+ # - license code
47
+ # - portfolio coverage info
48
+ # - from year, until year (month, day volume issue)
49
+ # - portfolio embargo info
50
+ # - years/months embargo'd
51
+
52
+ # TODO: evaluate this in context of changed boundwiths processing
53
+ # Franklin legacy note:
54
+ # a subfield code NOT used by the MARC 21 spec for 852 holdings records.
55
+ # we add this subfield during preprocessing to store boundwith record IDs.
56
+ # BOUND_WITH_ID = 'y'
57
+ end
58
+
59
+ # MARC enrichment originating from Alma API
60
+ # @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/ Alma docs
61
+ # We cannot modify these subfield settings
62
+ module Api
63
+ # Enrichment Tag Names
64
+ PHYS_INVENTORY_TAG = 'AVA'
65
+ ELEC_INVENTORY_TAG = 'AVE'
66
+
67
+ # Physical Holding (AVA) subfields
68
+ PHYS_CALL_NUMBER = 'd'
69
+ PHYS_CALL_NUMBER_TYPE = 'k'
70
+ PHYS_LIBRARY_CODE = 'b'
71
+ PHYS_LIBRARY_NAME = 'q'
72
+ PHYS_LOCATION_CODE = 'j'
73
+ PHYS_LOCATION_NAME = 'c'
74
+ PHYS_HOLDING_ID = '8'
75
+ PHYS_AVAILABILITY = 'e'
76
+ PHYS_TOTAL_ITEMS = 'f'
77
+ PHYS_UNAVAILABLE_ITEMS = 'g'
78
+ PHYS_SUMMARY_INFO = 'v'
79
+ PHYS_PRIORITY = 'p'
80
+
81
+ # Electronic Portfolio (AVE) subfields
82
+ ELEC_LIBRARY_CODE = 'l'
83
+ ELEC_COLLECTION_NAME = 'm'
84
+ ELEC_PUBLIC_NOTE = 'n'
85
+ ELEC_SERVICE_URL = 'u'
86
+ ELEC_COVERAGE_STMT = 's'
87
+ ELEC_INTERFACE_NAME = 't'
88
+ ELEC_PORTFOLIO_ID = '8'
89
+ ELEC_COLLECTION_ID = 'c'
90
+ ELEC_ACTIVATION_STATUS = 'e'
91
+ end
92
+ end
93
+ end
@@ -33,14 +33,14 @@ module PennMARC
33
33
  # @param [MARC::Field] field
34
34
  # @return [Boolean]
35
35
  def electronic_holding_tag?(field)
36
- field.tag.in? [EnrichedMarc::TAG_ELECTRONIC_INVENTORY, EnrichedMarc::AlmaApi::TAG_ELECTRONIC_INVENTORY]
36
+ field.tag.in? [Enriched::Pub::ELEC_INVENTORY_TAG, Enriched::Api::ELEC_INVENTORY_TAG]
37
37
  end
38
38
 
39
39
  # Does the record have added physical holding info?
40
40
  # @param [MARC::Field] field
41
41
  # @return [Boolean]
42
42
  def physical_holding_tag?(field)
43
- field.tag.in? [EnrichedMarc::TAG_HOLDING, EnrichedMarc::AlmaApi::TAG_PHYSICAL_INVENTORY]
43
+ field.tag.in? [Enriched::Pub::PHYS_INVENTORY_TAG, Enriched::Api::PHYS_INVENTORY_TAG]
44
44
  end
45
45
 
46
46
  # Check if a record contains an 856 entry for an online finding aid, meeting these criteria:
@@ -16,13 +16,13 @@ module PennMARC
16
16
  }.freeze
17
17
 
18
18
  # Enriched MARC tags that hold classification data
19
- TAGS = [EnrichedMarc::TAG_ITEM, EnrichedMarc::AlmaApi::TAG_PHYSICAL_INVENTORY].freeze
19
+ TAGS = [Enriched::Pub::ITEM_TAG, Enriched::Api::PHYS_INVENTORY_TAG].freeze
20
20
 
21
21
  class << self
22
22
  # Parse classification values for faceting. We retrieve classification values from enriched MARC fields 'itm' or
23
23
  # 'AVA' originating respectively from the Alma publishing process or from the Alma Api. We return the
24
24
  # highest level LOC or Dewey classifications from each available call number, joining the class code with
25
- # its title in a single string. See {PennMARC::EnrichedMarc} and {PennMARC::EnrichedMarc::AlmaApi} for more
25
+ # its title in a single string. See {PennMARC::Enriched} and {PennMARC::Enriched::Api} for more
26
26
  # information on the enriched MARC fields.
27
27
  # @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/ AVA docs
28
28
  # @param [MARC::Record] record
@@ -48,18 +48,18 @@ module PennMARC
48
48
  # @param [MARC::DataField] field
49
49
  # @return [String]
50
50
  def call_number_sf(field)
51
- return EnrichedMarc::SUB_ITEM_CALL_NUMBER if field.tag == EnrichedMarc::TAG_ITEM
51
+ return Enriched::Pub::ITEM_CALL_NUMBER if field.tag == Enriched::Pub::ITEM_TAG
52
52
 
53
- EnrichedMarc::AlmaApi::SUB_PHYSICAL_CALL_NUMBER
53
+ Enriched::Api::PHYS_CALL_NUMBER
54
54
  end
55
55
 
56
56
  # Retrieve subfield code that stores call number type on enriched marc field
57
57
  # @param [MARC::DataField] field
58
58
  # @return [String]
59
59
  def call_number_type_sf(field)
60
- return EnrichedMarc::SUB_ITEM_CALL_NUMBER_TYPE if field.tag == EnrichedMarc::TAG_ITEM
60
+ return Enriched::Pub::ITEM_CALL_NUMBER_TYPE if field.tag == Enriched::Pub::ITEM_TAG
61
61
 
62
- EnrichedMarc::AlmaApi::SUB_PHYSICAL_CALL_NUMBER_TYPE
62
+ Enriched::Api::PHYS_CALL_NUMBER_TYPE
63
63
  end
64
64
 
65
65
  # retrieve title of classification based on single char classification code and call number type
@@ -10,82 +10,43 @@ module PennMARC
10
10
  class << self
11
11
  # Main tags for Author/Creator information
12
12
  TAGS = %w[100 110].freeze
13
+
13
14
  # Aux tags for Author/Creator information, for use in search_aux method
14
15
  AUX_TAGS = %w[100 110 111 400 410 411 700 710 711 800 810 811].freeze
15
16
 
17
+ CONFERENCE_SEARCH_TAGS = %w[111 711 811].freeze
18
+
19
+ # subfields NOT to join when combining raw subfield values
20
+ NAME_EXCLUDED_SUBFIELDS = %w[a 1 4 5 6 8 t].freeze
21
+
16
22
  # Author/Creator search field. Includes all subfield values (even ǂ0 URIs) from
17
23
  # {https://www.oclc.org/bibformats/en/1xx/100.html 100 Main Entry--Personal Name} and
18
24
  # {https://www.oclc.org/bibformats/en/1xx/110.html 110 Main Entry--Corporate Name}. Maps any relator codes found
19
25
  # in ǂ4. To better handle name searches, returns names as both "First Last" and "Last, First" if a comma is found
20
- # in ǂa. Also indexes any linked values in the 880. Some of the search fields remain incomplete and may need to be
21
- # further investigated and ported when search result relevancy is considered.
22
- # @todo this seems bad - why include relator labels? URIs? punctuation? leaving mostly as-is for now,
23
- # but this should be reexamined in the relevancy-tuning phase. URIs should def be removed. and shouldn't
24
- # indicator1 tell us the order of the name?
26
+ # in ǂa. Also indexes any linked values in the 880.
27
+ # @todo are we including too many details here and gumming up our index? consider UIRs, relator labels, dates...
28
+ # @todo shouldn't indicator1 tell us the order of the name? do we not trust the indicator?
25
29
  # @note ported from get_author_creator_1_search_values
26
30
  # @param [MARC::Record] record
27
31
  # @param [Hash] relator_map
28
32
  # @return [Array<String>] array of author/creator values for indexing
29
33
  def search(record, relator_map: Mappers.relator)
30
- creator_subfields = %w[a 1 4 6 8]
31
- acc = record.fields(TAGS).map do |field|
32
- pieces = field.filter_map do |sf|
33
- if sf.code == 'a'
34
- convert_name_order(sf.value)
35
- elsif creator_subfields.exclude?(sf.code)
36
- sf.value
37
- elsif sf.code == '4'
38
- relator = translate_relator(sf.value, relator_map)
39
- next if relator.blank?
40
-
41
- relator
42
- end
43
- end
44
- value = join_and_squish(pieces)
45
- if value.end_with?('.', '-')
46
- value
47
- else
48
- "#{value}."
49
- end
50
- end
51
- # a second iteration over the same fields produces name entries with the names not reordered
52
- secondary_subfields = %w[4 6 8]
53
- acc += record.fields(TAGS).map do |field|
54
- pieces = field.filter_map do |sf|
55
- if secondary_subfields.exclude?(sf.code)
56
- sf.value
57
- elsif sf.code == '4'
58
- relator = translate_relator(sf.value, relator_map)
59
- next if relator.blank?
60
-
61
- relator
62
- end
63
- end
64
- value = join_and_squish(pieces)
65
- if value.end_with?('.', '-')
66
- value
67
- else
68
- "#{value}."
69
- end
70
- end
71
- acc += record.fields(%w[880]).filter_map do |field|
72
- next unless field.any? { |sf| sf.code == '6' && sf.value.in?(%w[100 110]) }
73
-
74
- suba = field.find_all(&subfield_in?(%w[a])).map { |sf|
75
- convert_name_order(sf.value)
76
- }.first
77
- oth = join_and_squish(field.find_all(&subfield_not_in?(%w[6 8 a t])).map(&:value))
78
- join_and_squish [suba, oth]
79
- end
80
- acc.uniq
34
+ name_search_values record: record, tags: TAGS, relator_map: relator_map
81
35
  end
82
36
 
83
37
  # Auxiliary Author/Creator search field
38
+ # This duplicates the values returned by the search method, but adds in additional MARC tags to include
39
+ # creator-adjacent entities. The added 4xx tags are mostly obsolete, but the 7xx tags are important. See:
40
+ # {https://www.loc.gov/marc/bibliographic/bd700.html MARC 700},
41
+ # {https://www.loc.gov/marc/bibliographic/bd710.html MARC 710},
42
+ # and {https://www.loc.gov/marc/bibliographic/bd711.html MARC 711}. The 800, 810 and 8111 tags are similar in
43
+ # theme to the 7xx fields but apply to serial records.
84
44
  # @note ported from get_author_creator_2_search_values
85
- # @todo port this later
86
45
  # @param [MARC::Record] record
87
46
  # @return [Array<String>] array of extended author/creator values for indexing
88
- def search_aux(record); end
47
+ def search_aux(record, relator_map: Mappers.relator)
48
+ name_search_values record: record, tags: AUX_TAGS, relator_map: relator_map
49
+ end
89
50
 
90
51
  # All author/creator values for display (like #show, but multivalued?) - no 880 linkage
91
52
  # @note ported from get_author_creator_values (indexed as author_creator_a) - shown on results page
@@ -180,9 +141,14 @@ module PennMARC
180
141
  end
181
142
  end
182
143
 
183
- # @todo this supports "Conference" fielded search and may not be needed
184
- # @note see get_conference_search_values
185
- def conference_search(record); end
144
+ # Conference name values for searching
145
+ # @param [MARC::Record] record
146
+ # @return [Array<String>]
147
+ def conference_search(record)
148
+ record.fields(CONFERENCE_SEARCH_TAGS).filter_map do |field|
149
+ join_subfields(field, &subfield_in?(%w[a c d e]))
150
+ end
151
+ end
186
152
 
187
153
  # Retrieve contributor values for display from fields {https://www.oclc.org/bibformats/en/7xx/700.html 700}
188
154
  # and {https://www.oclc.org/bibformats/en/7xx/710.html 710} and their linked alternates. Joins subfields
@@ -222,6 +188,32 @@ module PennMARC
222
188
 
223
189
  private
224
190
 
191
+ # @param [MARC::Record] record
192
+ # @param [Array] tags to consider
193
+ # @param [Hash] relator_map
194
+ # @return [Array<String>] name values from given tags
195
+ def name_search_values(record:, tags:, relator_map:)
196
+ acc = record.fields(tags).filter_map do |field|
197
+ name_from_main_entry field, relator_map, should_convert_name_order: false
198
+ end
199
+
200
+ acc += record.fields(tags).filter_map do |field|
201
+ name_from_main_entry field, relator_map, should_convert_name_order: true
202
+ end
203
+
204
+ acc += record.fields(['880']).filter_map do |field|
205
+ next unless field.any? { |sf| sf.code == '6' && sf.value.in?(tags) }
206
+
207
+ suba = field.find_all(&subfield_in?(%w[a])).filter_map { |sf|
208
+ convert_name_order(sf.value)
209
+ }.first
210
+ oth = join_and_squish(field.find_all(&subfield_not_in?(%w[6 8 a t])).map(&:value))
211
+ join_and_squish [suba, oth]
212
+ end
213
+
214
+ acc.uniq
215
+ end
216
+
225
217
  # Trim punctuation method extracted from Traject macro, to ensure consistent output
226
218
  # @todo move to Util?
227
219
  # @param [String] string
@@ -244,11 +236,14 @@ module PennMARC
244
236
 
245
237
  # Extract the information we care about from 1xx fields, map relator codes, and use appropriate punctuation
246
238
  # @param [MARC::Field] field
239
+ # @param [Hash] mapping
240
+ # @param [Boolean] should_convert_name_order
247
241
  # @return [String] joined subfield values for value from field
248
- def name_from_main_entry(field, mapping)
249
- name_subfields = %w[0 1 4 6 8]
242
+ def name_from_main_entry(field, mapping, should_convert_name_order: false)
250
243
  s = field.filter_map { |sf|
251
- if name_subfields.exclude?(sf.code)
244
+ if sf.code == 'a'
245
+ should_convert_name_order ? convert_name_order(sf.value) : sf.value
246
+ elsif NAME_EXCLUDED_SUBFIELDS.exclude?(sf.code)
252
247
  " #{sf.value}"
253
248
  elsif sf.code == '4'
254
249
  relator = translate_relator(sf.value, mapping)
@@ -20,13 +20,13 @@ module PennMARC
20
20
  end
21
21
 
22
22
  # Retrieve date added (subfield 'q') from enriched marc 'itm' field.
23
- # {PennMARC::EnrichedMarc} maps enriched marc fields and subfields created during Alma publishing. The enriched
23
+ # {PennMARC::Enriched} maps enriched marc fields and subfields created during Alma publishing. The enriched
24
24
  # metadata provided by the Alma API does not include the date created value, so we can't work with that here.
25
25
  # @param [MARC::Record] record
26
26
  # @return [DateTime, nil] The date added, or nil if date found in record is invalid
27
27
  def added(record)
28
- record.fields(EnrichedMarc::TAG_ITEM).flat_map { |field|
29
- subfield_values(field, EnrichedMarc::SUB_ITEM_DATE_CREATED).filter_map do |date_added|
28
+ record.fields(Enriched::Pub::ITEM_TAG).flat_map { |field|
29
+ subfield_values(field, Enriched::Pub::ITEM_DATE_CREATED).filter_map do |date_added|
30
30
  # On 2022-05-02, this field value (as exported in enriched publishing
31
31
  # job from Alma) began truncating time to day-level granularity. We have
32
32
  # no guarantee that this won't switch back in the future, so for the
@@ -165,14 +165,14 @@ module PennMARC
165
165
  # @param [MARC::Record] record
166
166
  # @return [Array]
167
167
  def call_nums(record)
168
- if field_defined?(record, EnrichedMarc::TAG_HOLDING)
169
- record.fields(EnrichedMarc::TAG_HOLDING).map do |field|
170
- join_subfields(field, &subfield_in?([EnrichedMarc::SUB_HOLDING_CLASSIFICATION_PART,
171
- EnrichedMarc::SUB_HOLDING_ITEM_PART]))
168
+ if field_defined?(record, Enriched::Pub::PHYS_INVENTORY_TAG)
169
+ record.fields(Enriched::Pub::PHYS_INVENTORY_TAG).map do |field|
170
+ join_subfields(field, &subfield_in?([Enriched::Pub::HOLDING_CLASSIFICATION_PART,
171
+ Enriched::Pub::HOLDING_ITEM_PART]))
172
172
  end
173
- elsif field_defined?(record, EnrichedMarc::AlmaApi::TAG_PHYSICAL_INVENTORY)
174
- record.fields(EnrichedMarc::AlmaApi::TAG_PHYSICAL_INVENTORY).map do |field|
175
- join_subfields(field, &subfield_in?([EnrichedMarc::AlmaApi::SUB_PHYSICAL_CALL_NUMBER_TYPE]))
173
+ elsif field_defined?(record, Enriched::Api::PHYS_INVENTORY_TAG)
174
+ record.fields(Enriched::Api::PHYS_INVENTORY_TAG).map do |field|
175
+ join_subfields(field, &subfield_in?([Enriched::Api::PHYS_CALL_NUMBER_TYPE]))
176
176
  end
177
177
  else
178
178
  []
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../enriched_marc'
3
+ require_relative '../enriched'
4
4
  require_relative '../mappers'
5
5
  require_relative '../util'
6
6
 
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'inventory_entry/electronic'
4
+ require_relative 'inventory_entry/physical'
5
+
6
+ module PennMARC
7
+ # Methods for extracting holdings information ("inventory") when available
8
+ class Inventory < Helper
9
+ PHYSICAL_INVENTORY_TAGS = [
10
+ Enriched::Pub::PHYS_INVENTORY_TAG,
11
+ Enriched::Api::PHYS_INVENTORY_TAG
12
+ ].freeze
13
+
14
+ ELECTRONIC_INVENTORY_TAGS = [
15
+ Enriched::Pub::ELEC_INVENTORY_TAG,
16
+ Enriched::Api::ELEC_INVENTORY_TAG
17
+ ].freeze
18
+
19
+ class << self
20
+ # Hash of Physical holdings information
21
+ # @param [MARC::Record] record
22
+ # @return [Array, nil]
23
+ def physical(record)
24
+ source = enrichment_source(record)
25
+ return unless source
26
+
27
+ record.fields(PHYSICAL_INVENTORY_TAGS).map do |entry|
28
+ InventoryEntry::Physical.new(entry, source).to_h
29
+ end
30
+ end
31
+
32
+ # Hash of Electronic inventory information
33
+ # @param [MARC::Record] record
34
+ # @return [Array, nil]
35
+ def electronic(record)
36
+ source = enrichment_source(record)
37
+ return unless source
38
+
39
+ record.fields(ELECTRONIC_INVENTORY_TAGS).map do |entry|
40
+ InventoryEntry::Electronic.new(entry, source).to_h
41
+ end
42
+ end
43
+
44
+ # Count of all electronic portfolios
45
+ # @param [MARC::Record] record
46
+ # @return [Integer]
47
+ def electronic_portfolio_count(record)
48
+ record.count { |field| field.tag.in? %w[AVE prt] }
49
+ end
50
+
51
+ # Count of all physical holdings
52
+ # @param [MARC::Record] record
53
+ # @return [Integer]
54
+ def physical_holding_count(record)
55
+ record.count { |field| field.tag.in? %w[AVA hld] }
56
+ end
57
+
58
+ private
59
+
60
+ # Determine the source of the MARC inventory enrichment
61
+ # @param [MARC::Record] record
62
+ # @return [Symbol, nil]
63
+ def enrichment_source(record)
64
+ if pub_enrichment_tags?(record)
65
+ :pub
66
+ elsif api_enrichment_tags?(record)
67
+ :api
68
+ end
69
+ end
70
+
71
+ # Does the record include tags from Publishing inventory enrichment?
72
+ # @todo move to Util?
73
+ # @param [MARC::Record] record
74
+ # @return [Boolean]
75
+ def pub_enrichment_tags?(record)
76
+ record.tags.intersect?(
77
+ [Enriched::Pub::PHYS_INVENTORY_TAG, Enriched::Pub::ELEC_INVENTORY_TAG, Enriched::Pub::ITEM_TAG]
78
+ )
79
+ end
80
+
81
+ # Does the record include tags from API inventory enrichment?
82
+ # @todo move to Util?
83
+ # @param [MARC::Record] record
84
+ # @return [Boolean]
85
+ def api_enrichment_tags?(record)
86
+ record.tags.intersect?(
87
+ [Enriched::Api::PHYS_INVENTORY_TAG, Enriched::Api::ELEC_INVENTORY_TAG]
88
+ )
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PennMARC
4
+ module InventoryEntry
5
+ # Base class for InventoryEntry classes, defines required interface
6
+ class Base
7
+ attr_reader :source, :field, :mapper
8
+
9
+ # @param [MARC::DataField] inventory_field
10
+ # @param [Symbol] source
11
+ def initialize(inventory_field, source)
12
+ @source = source
13
+ @field = inventory_field
14
+ @mapper = @source == :api ? Enriched::Api : Enriched::Pub
15
+ end
16
+
17
+ # @return [Hash]
18
+ def to_h
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module PennMARC
6
+ module InventoryEntry
7
+ # Represent a Electronic inventory entry - simple because the subfield specification is identical across
8
+ # entries returned by the API and Alma Publishing enrichment
9
+ class Electronic < Base
10
+ # @return [Hash{Symbol->Unknown}]
11
+ def to_h
12
+ { portfolio_id: field[mapper::ELEC_PORTFOLIO_ID],
13
+ url: field[mapper::ELEC_SERVICE_URL],
14
+ collection_name: field[mapper::ELEC_COLLECTION_NAME],
15
+ coverage: field[mapper::ELEC_COVERAGE_STMT],
16
+ note: field[mapper::ELEC_PUBLIC_NOTE] }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module PennMARC
6
+ module InventoryEntry
7
+ # Represent a Physical inventory entry
8
+ class Physical < Base
9
+ # Call number from inventory entry
10
+ # @return [String (frozen)]
11
+ def call_num
12
+ if source == :pub
13
+ "#{field[mapper::HOLDING_CLASSIFICATION_PART]}#{field[mapper::HOLDING_ITEM_PART]}"
14
+ elsif source == :api
15
+ field[mapper::PHYS_CALL_NUMBER]
16
+ end
17
+ end
18
+
19
+ # Priority for inventory entry
20
+ # @note we currently don't return priority in our publishing enrichment
21
+ # @return [String, nil]
22
+ def priority
23
+ return nil if source == :pub
24
+
25
+ field[mapper::PHYS_PRIORITY]
26
+ end
27
+
28
+ # @return [Hash{Symbol->Unknown}]
29
+ def to_h
30
+ { holding_id: field[mapper::PHYS_HOLDING_ID],
31
+ location_name: field[mapper::PHYS_LOCATION_NAME],
32
+ location_code: field[mapper::PHYS_LOCATION_CODE],
33
+ call_num: call_num,
34
+ priority: priority }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -3,10 +3,12 @@
3
3
  module PennMARC
4
4
  # Methods that return Library and Location values from Alma enhanced MARC fields
5
5
  class Location < Helper
6
+ ONLINE_LIBRARY = 'Online library'
7
+
6
8
  class << self
7
9
  # Retrieves library location from enriched marc 'itm' or 'hld' fields, giving priority to the item location over
8
10
  # the holdings location. Returns item's location if available. Otherwise, returns holding's location.
9
- # {PennMARC::EnrichedMarc} maps enriched marc fields and subfields created during Alma publishing.
11
+ # {PennMARC::Enriched} maps enriched marc fields and subfields created during Alma publishing.
10
12
  # @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/
11
13
  # Alma documentation for these added fields
12
14
  # @param [MARC::Record] record
@@ -19,7 +21,7 @@ module PennMARC
19
21
  # Retrieves the specific location from enriched marc 'itm' or 'hld' fields, giving priority to the item location
20
22
  # over the holdings location. Returns item library location if available. Otherwise, returns holdings library
21
23
  # location.
22
- # {PennMARC::EnrichedMarc} maps enriched marc fields and subfields created during Alma publishing.
24
+ # {PennMARC::Enriched} maps enriched marc fields and subfields created during Alma publishing.
23
25
  # @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/
24
26
  # Alma documentation for these added fields
25
27
  # @param [MARC::Record] record
@@ -31,7 +33,7 @@ module PennMARC
31
33
 
32
34
  # Base method to retrieve location data from enriched marc 'itm' or 'hld' fields, giving priority to the item
33
35
  # location over the holdings location. Returns item location if available. Otherwise, returns holdings location.
34
- # {PennMARC::EnrichedMarc} maps enriched marc fields and subfields created during Alma publishing.
36
+ # {PennMARC::Enriched} maps enriched marc fields and subfields created during Alma publishing.
35
37
  # @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/
36
38
  # Alma documentation for these added fields
37
39
  # @param [MARC::Record] record
@@ -61,7 +63,9 @@ module PennMARC
61
63
  location_map[subfield.value.to_sym][display_value.to_sym]
62
64
  }.flatten.compact_blank
63
65
  }.uniq
64
- locations << 'Online library' if record.fields(PennMARC::EnrichedMarc::TAG_ELECTRONIC_INVENTORY).any?
66
+ if record.tags.intersect?([Enriched::Pub::ELEC_INVENTORY_TAG, Enriched::Api::ELEC_INVENTORY_TAG])
67
+ locations << ONLINE_LIBRARY
68
+ end
65
69
  locations
66
70
  end
67
71
 
@@ -82,17 +86,18 @@ module PennMARC
82
86
  # Since item records may reflect locations more accurately, we use them if they exist;
83
87
  # if not, we use the holdings.
84
88
 
85
- tag = PennMARC::EnrichedMarc::TAG_HOLDING
86
- subfield_code = PennMARC::EnrichedMarc::SUB_HOLDING_SHELVING_LOCATION
87
-
88
89
  # if the record has an enriched item field present, use it
89
- if field_defined?(record, PennMARC::EnrichedMarc::TAG_ITEM)
90
- tag = PennMARC::EnrichedMarc::TAG_ITEM
91
- subfield_code = PennMARC::EnrichedMarc::SUB_ITEM_CURRENT_LOCATION
92
- # otherwise, use the api enriched field value
93
- elsif field_defined?(record, EnrichedMarc::AlmaApi::TAG_PHYSICAL_INVENTORY)
94
- tag = EnrichedMarc::AlmaApi::TAG_PHYSICAL_INVENTORY
95
- subfield_code = EnrichedMarc::AlmaApi::SUB_HOLDING_LOCATION_CODE
90
+ if field_defined?(record, Enriched::Pub::ITEM_TAG)
91
+ tag = Enriched::Pub::ITEM_TAG
92
+ subfield_code = Enriched::Pub::ITEM_CURRENT_LOCATION
93
+ # if the record has API inventory tags, use them
94
+ elsif field_defined?(record, Enriched::Api::PHYS_INVENTORY_TAG)
95
+ tag = Enriched::Api::PHYS_INVENTORY_TAG
96
+ subfield_code = Enriched::Api::PHYS_LOCATION_CODE
97
+ # otherwise use Pub holding tags
98
+ else
99
+ tag = Enriched::Pub::PHYS_INVENTORY_TAG
100
+ subfield_code = Enriched::Pub::PHYS_LOCATION_CODE
96
101
  end
97
102
 
98
103
  { tag: tag, subfield_code: subfield_code }