pennmarc 1.0.12 → 1.0.14

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: 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 }