pennmarc 1.0.12 → 1.0.15.pre

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +7 -12
  3. data/.rubocop_todo.yml +2 -2
  4. data/lib/pennmarc/enriched.rb +93 -0
  5. data/lib/pennmarc/helpers/access.rb +2 -2
  6. data/lib/pennmarc/helpers/citation.rb +4 -4
  7. data/lib/pennmarc/helpers/classification.rb +8 -8
  8. data/lib/pennmarc/helpers/creator.rb +70 -73
  9. data/lib/pennmarc/helpers/database.rb +6 -6
  10. data/lib/pennmarc/helpers/date.rb +3 -3
  11. data/lib/pennmarc/helpers/edition.rb +4 -2
  12. data/lib/pennmarc/helpers/format.rb +15 -14
  13. data/lib/pennmarc/helpers/helper.rb +1 -1
  14. data/lib/pennmarc/helpers/identifier.rb +16 -14
  15. data/lib/pennmarc/helpers/inventory.rb +92 -0
  16. data/lib/pennmarc/helpers/inventory_entry/base.rb +23 -0
  17. data/lib/pennmarc/helpers/inventory_entry/electronic.rb +20 -0
  18. data/lib/pennmarc/helpers/inventory_entry/physical.rb +38 -0
  19. data/lib/pennmarc/helpers/language.rb +3 -2
  20. data/lib/pennmarc/helpers/location.rb +19 -14
  21. data/lib/pennmarc/helpers/note.rb +10 -8
  22. data/lib/pennmarc/helpers/production.rb +9 -9
  23. data/lib/pennmarc/helpers/relation.rb +12 -9
  24. data/lib/pennmarc/helpers/series.rb +10 -8
  25. data/lib/pennmarc/helpers/subject.rb +12 -12
  26. data/lib/pennmarc/helpers/title.rb +20 -16
  27. data/lib/pennmarc/mappings/locations.yml +4 -0
  28. data/lib/pennmarc/util.rb +19 -4
  29. data/lib/pennmarc/version.rb +1 -1
  30. data/spec/lib/pennmarc/helpers/access_spec.rb +5 -5
  31. data/spec/lib/pennmarc/helpers/classification_spec.rb +6 -6
  32. data/spec/lib/pennmarc/helpers/creator_spec.rb +41 -7
  33. data/spec/lib/pennmarc/helpers/format_spec.rb +4 -4
  34. data/spec/lib/pennmarc/helpers/inventory_spec.rb +129 -0
  35. data/spec/lib/pennmarc/helpers/location_spec.rb +40 -9
  36. data/spec/lib/pennmarc/helpers/subject_spec.rb +37 -13
  37. metadata +10 -5
  38. data/lib/pennmarc/enriched_marc.rb +0 -49
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97610e7a00a2d206d84d5865ce9ebf14b5aa43e8081d9c4f559d198cc7adde23
4
- data.tar.gz: fcee05f6a1999b674cb699605ee1e16b04fdda87e6bb7e65c3cb54958ef19ba5
3
+ metadata.gz: cd846248b3c6fcde1cfd8a74312d84f46ce28f534effaff22ec112a5456afd2f
4
+ data.tar.gz: 7e5789d9ef2594767e438a5b1fd2978b1508afad975785059b3520d795fda15b
5
5
  SHA512:
6
- metadata.gz: '0099dbb3f521e56f8ba94527e13085058191acfdaa3625f30f050da3bb96d8ae224eb9c6e9fb54620ea6d4640800bc38bd71602a59d66871eedd26c7f6287b4e'
7
- data.tar.gz: 44cfade6b9c3ab3e0591553fbdf5c09f8be3467ccf71b0f003f7f0ff9dc8b08b1093d783b84c5f71ba3c98607a2d97f1e1ef62653cbe3af5361895d3cf964c1e
6
+ metadata.gz: 82a79402e68c4b48f3d38fa165042472aab3a8d811797145811af8a873d177ceb9020a2e71c9853f5c792a37d0533d7a87c7ec94506507c817e3dc09db345dcf
7
+ data.tar.gz: 87196088380e9b7f6750511434edcc49467ca9ee47e9577cb3f42ce75e595a2f7cb62605ede37d1a4b17b59876dec437a0beee131ee97eb19f1c411751c849cc
data/.gitlab-ci.yml CHANGED
@@ -1,4 +1,8 @@
1
1
  include:
2
+ - project: "devops/gitlab/ci-templates/general"
3
+ file:
4
+ - ".install_hashicorp_vault.yml"
5
+ - ".vault_jwt_auth.yml"
2
6
  - project: "devops/gitlab/ci-templates/ruby"
3
7
  ref: "sans-dind"
4
8
  file:
@@ -37,19 +41,10 @@ gem_publication:
37
41
  image: ruby:3.2.2
38
42
  variables:
39
43
  GEMSPEC_FILE: "${CI_PROJECT_NAME}.gemspec"
40
- VAULT_VERSION: "1.10.1"
44
+ VAULT_VERSION: "1.15.5"
41
45
  before_script:
42
- - |
43
- apt-get update && apt-get -y install \
44
- wget \
45
- unzip && \
46
- wget https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip -O /tmp/vault_${VAULT_VERSION}.zip && \
47
- unzip /tmp/vault_${VAULT_VERSION}.zip -d /usr/bin && \
48
- rm -fr /tmp/vault_${VAULT_VERSION}.zip && \
49
- apt-get remove -y wget unzip && \
50
- apt-get clean
51
- - export VAULT_ADDR=${VAULT_URL}
52
- - export VAULT_TOKEN="$(vault write -field=token auth/jwt/${CI_SERVER_HOST}/login role=${JWT_ROLE} jwt=${CI_JOB_JWT})"
46
+ - !reference [.install_hashicorp_vault, before_script]
47
+ - !reference [.vault_jwt_auth, before_script]
53
48
  - export GEM_HOST_API_KEY="$(vault kv get -field=rubygems_api_key ${VAULT_KV_ENDPOINT}${ENVIRONMENT})"
54
49
  - |
55
50
  gem -v
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:
@@ -11,9 +11,9 @@ module PennMARC
11
11
  # field 520 (Summary, Etc. Note).
12
12
  # https://www.loc.gov/marc/bibliographic/bd510.html
13
13
  # @param [MARC::Record] record
14
- # @return [Array] array of citations and any linked alternates
14
+ # @return [Array<String>] array of citations and any linked alternates
15
15
  def cited_in_show(record)
16
- datafield_and_linked_alternate(record, '510')
16
+ datafield_and_linked_alternate(record, '510').uniq
17
17
  end
18
18
 
19
19
  # Field 524 is the Preferred Citation of Described Materials Note. It is the Format for the citation of the
@@ -22,9 +22,9 @@ module PennMARC
22
22
  # introductory phrase that is generated as a display constant based on the first indicator value.
23
23
  # https://www.loc.gov/marc/bibliographic/bd524.html
24
24
  # @param [MARC::Record] record
25
- # @return [Array] array of citation of described materials note and any linked alternates
25
+ # @return [Array<String>] array of citation of described materials note and any linked alternates
26
26
  def cite_as_show(record)
27
- datafield_and_linked_alternate(record, '524')
27
+ datafield_and_linked_alternate(record, '524').uniq
28
28
  end
29
29
  end
30
30
  end
@@ -16,19 +16,19 @@ 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
29
29
  # @return [Array<String>] array of classifications
30
30
  def facet(record)
31
- record.fields(TAGS).flat_map do |field|
31
+ record.fields(TAGS).flat_map { |field|
32
32
  call_number_type = subfield_values(field, call_number_type_sf(field))&.first
33
33
  call_numbers = subfield_values(field, call_number_sf(field))
34
34
 
@@ -39,7 +39,7 @@ module PennMARC
39
39
 
40
40
  format_facet(class_code, call_number_type, title)
41
41
  end
42
- end
42
+ }.uniq
43
43
  end
44
44
 
45
45
  private
@@ -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
@@ -106,9 +67,9 @@ module PennMARC
106
67
  def show(record)
107
68
  fields = record.fields(TAGS)
108
69
  fields += record.fields('880').select { |field| subfield_value_in?(field, '6', TAGS) }
109
- fields.filter_map do |field|
70
+ fields.filter_map { |field|
110
71
  join_subfields(field, &subfield_not_in?(%w[0 1 4 6 8 e w]))
111
- end
72
+ }.uniq
112
73
  end
113
74
 
114
75
  # Author/Creator sort. Does not map and include any relator codes.
@@ -134,11 +95,11 @@ module PennMARC
134
95
  700 => 'abcdjq', 710 => 'abcdjq', 711 => 'abcen',
135
96
  800 => 'abcdjq', 810 => 'abcdjq', 811 => 'abcen'
136
97
  }
137
- source_map.flat_map do |field_num, subfields|
98
+ source_map.flat_map { |field_num, subfields|
138
99
  record.fields(field_num.to_s).map do |field|
139
100
  trim_punctuation(join_subfields(field, &subfield_in?(subfields.chars)))
140
101
  end
141
- end
102
+ }.uniq
142
103
  end
143
104
 
144
105
  # Conference for display, intended for results display
@@ -147,9 +108,9 @@ module PennMARC
147
108
  # @param [Hash] relator_map
148
109
  # @return [Array<String>] array of conference values
149
110
  def conference_show(record, relator_map: Mappers.relator)
150
- record.fields('111').filter_map do |field|
111
+ record.fields('111').filter_map { |field|
151
112
  name_from_main_entry field, relator_map
152
- end
113
+ }.uniq
153
114
  end
154
115
 
155
116
  # Conference detailed display, intended for record show page.
@@ -169,7 +130,7 @@ module PennMARC
169
130
  conf_extra = join_subfields field, &subfield_in?(%w[e j w])
170
131
  join_and_squish [conf, conf_extra].compact_blank
171
132
  end
172
- values + record.fields('880').filter_map do |field|
133
+ conferences = values + record.fields('880').filter_map do |field|
173
134
  next unless subfield_value_in? field, '6', %w[111 711]
174
135
 
175
136
  next if subfield_defined? field, 'i'
@@ -178,11 +139,17 @@ module PennMARC
178
139
  conf_extra = join_subfields(field, &subfield_in?(%w[4 e j w]))
179
140
  join_and_squish [conf, conf_extra]
180
141
  end
142
+ conferences.uniq
181
143
  end
182
144
 
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
145
+ # Conference name values for searching
146
+ # @param [MARC::Record] record
147
+ # @return [Array<String>]
148
+ def conference_search(record)
149
+ record.fields(CONFERENCE_SEARCH_TAGS).filter_map { |field|
150
+ join_subfields(field, &subfield_in?(%w[a c d e]))
151
+ }.uniq
152
+ end
186
153
 
187
154
  # Retrieve contributor values for display from fields {https://www.oclc.org/bibformats/en/7xx/700.html 700}
188
155
  # and {https://www.oclc.org/bibformats/en/7xx/710.html 710} and their linked alternates. Joins subfields
@@ -193,7 +160,7 @@ module PennMARC
193
160
  # @return [Array<String>]
194
161
  def contributor_show(record, relator_map: Mappers.relator)
195
162
  indicator_2_options = ['', ' ', '0']
196
- contributors = record.fields(%w[700 710]).filter_map do |field|
163
+ values = record.fields(%w[700 710]).filter_map do |field|
197
164
  next unless indicator_2_options.member?(field.indicator2)
198
165
  next if subfield_defined? field, 'i'
199
166
 
@@ -210,7 +177,7 @@ module PennMARC
210
177
  }.join
211
178
  "#{contributor} #{contributor_append}".squish
212
179
  end
213
- contributors + record.fields('880').filter_map do |field|
180
+ contributors = values + record.fields('880').filter_map do |field|
214
181
  next unless subfield_value_in?(field, '6', %w[700 710])
215
182
  next if subfield_defined?(field, 'i')
216
183
 
@@ -218,10 +185,37 @@ module PennMARC
218
185
  contributor_append = join_subfields(field, &subfield_in?(%w[e u 3]))
219
186
  "#{contributor} #{contributor_append}".squish
220
187
  end
188
+ contributors.uniq
221
189
  end
222
190
 
223
191
  private
224
192
 
193
+ # @param [MARC::Record] record
194
+ # @param [Array] tags to consider
195
+ # @param [Hash] relator_map
196
+ # @return [Array<String>] name values from given tags
197
+ def name_search_values(record:, tags:, relator_map:)
198
+ acc = record.fields(tags).filter_map do |field|
199
+ name_from_main_entry field, relator_map, should_convert_name_order: false
200
+ end
201
+
202
+ acc += record.fields(tags).filter_map do |field|
203
+ name_from_main_entry field, relator_map, should_convert_name_order: true
204
+ end
205
+
206
+ acc += record.fields(['880']).filter_map do |field|
207
+ next unless field.any? { |sf| sf.code == '6' && sf.value.in?(tags) }
208
+
209
+ suba = field.find_all(&subfield_in?(%w[a])).filter_map { |sf|
210
+ convert_name_order(sf.value)
211
+ }.first
212
+ oth = join_and_squish(field.find_all(&subfield_not_in?(%w[6 8 a t])).map(&:value))
213
+ join_and_squish [suba, oth]
214
+ end
215
+
216
+ acc.uniq
217
+ end
218
+
225
219
  # Trim punctuation method extracted from Traject macro, to ensure consistent output
226
220
  # @todo move to Util?
227
221
  # @param [String] string
@@ -244,11 +238,14 @@ module PennMARC
244
238
 
245
239
  # Extract the information we care about from 1xx fields, map relator codes, and use appropriate punctuation
246
240
  # @param [MARC::Field] field
241
+ # @param [Hash] mapping
242
+ # @param [Boolean] should_convert_name_order
247
243
  # @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]
244
+ def name_from_main_entry(field, mapping, should_convert_name_order: false)
250
245
  s = field.filter_map { |sf|
251
- if name_subfields.exclude?(sf.code)
246
+ if sf.code == 'a'
247
+ should_convert_name_order ? convert_name_order(sf.value) : sf.value
248
+ elsif NAME_EXCLUDED_SUBFIELDS.exclude?(sf.code)
252
249
  " #{sf.value}"
253
250
  elsif sf.code == '4'
254
251
  relator = translate_relator(sf.value, mapping)
@@ -19,13 +19,13 @@ module PennMARC
19
19
  # @param [Marc::Record]
20
20
  # @return [Array<string>] Array of types
21
21
  def type_facet(record)
22
- record.fields('944').filter_map do |field|
22
+ record.fields('944').filter_map { |field|
23
23
  # skip unless specified database format type present
24
24
  next unless subfield_value?(field, 'a', /#{DATABASES_FACET_VALUE}/o)
25
25
 
26
26
  type = field.find { |subfield| subfield.code == 'b' }
27
27
  type&.value
28
- end
28
+ }.uniq
29
29
  end
30
30
 
31
31
  # Retrieves database subject category/communities of interest (subfield 'a') from
@@ -37,13 +37,13 @@ module PennMARC
37
37
  def category_facet(record)
38
38
  return [] unless curated_db?(record)
39
39
 
40
- record.fields('943').filter_map do |field|
40
+ record.fields('943').filter_map { |field|
41
41
  # skip unless Community of Interest code is in subfield '2'
42
42
  next unless subfield_value?(field, '2', /#{COI_CODE}/o)
43
43
 
44
44
  category = field.find { |subfield| subfield.code == 'a' }
45
45
  category&.value
46
- end
46
+ }.uniq
47
47
  end
48
48
 
49
49
  # Concatenates database subject category with database sub subject category in the format "category--subcategory"
@@ -58,7 +58,7 @@ module PennMARC
58
58
  def subcategory_facet(record)
59
59
  return [] unless curated_db?(record)
60
60
 
61
- record.fields('943').filter_map do |field|
61
+ record.fields('943').filter_map { |field|
62
62
  # skip unless Community of Interest code is in subfield '2'
63
63
  next unless subfield_value?(field, '2', /#{COI_CODE}/o)
64
64
 
@@ -73,7 +73,7 @@ module PennMARC
73
73
  next if subcategory.blank?
74
74
 
75
75
  "#{category.value}--#{subcategory.value}"
76
- end
76
+ }.uniq
77
77
  end
78
78
 
79
79
  private
@@ -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
@@ -14,9 +14,10 @@ module PennMARC
14
14
  # @param [MARC::Record] record
15
15
  # @return [Array<String>] array of editions and their alternates
16
16
  def show(record)
17
- record.fields('250').map { |field|
17
+ editions = record.fields('250').map { |field|
18
18
  join_subfields(field, &subfield_not_in?(%w[6 8]))
19
19
  } + linked_alternate_not_6_or_8(record, '250')
20
+ editions.uniq
20
21
  end
21
22
 
22
23
  # Edition values for display in search results. Just grab the first 250 field.
@@ -42,12 +43,13 @@ module PennMARC
42
43
 
43
44
  other_edition_value(field, relator_map)
44
45
  end
45
- values + record.fields('880').filter_map do |field|
46
+ editions = values + record.fields('880').filter_map do |field|
46
47
  next unless field.indicator2.blank? && subfield_value_in?(field, '6', %w[775]) &&
47
48
  subfield_defined?(field, 'i')
48
49
 
49
50
  other_edition_value(field, relator_map)
50
51
  end
52
+ editions.uniq
51
53
  end
52
54
 
53
55
  private
@@ -48,7 +48,7 @@ module PennMARC
48
48
  end
49
49
  join_subfields(f, &subfield_not_in?(subfield_to_ignore))
50
50
  end
51
- results.compact_blank
51
+ results.compact_blank.uniq
52
52
  end
53
53
 
54
54
  # Get Format values for faceting. Format values are determined using complex logic for each possible format value.
@@ -122,23 +122,24 @@ module PennMARC
122
122
  OTHER
123
123
  end
124
124
  end
125
- formats.concat(curated_format(record))
125
+ formats.concat(curated_format(record)).uniq
126
126
  end
127
127
 
128
128
  # Show "Other Format" values from {https://www.oclc.org/bibformats/en/7xx/776.html 776} and any 880 linkage.
129
129
  # @todo is 774 an error in the linked field in legacy? i changed to 776 here
130
130
  # @param [MARC::Record] record
131
- # @return [Array] other format values for display
131
+ # @return [Array<String>] other format values for display
132
132
  def other_show(record)
133
- other_formats = record.fields('776').filter_map do |field|
133
+ values = record.fields('776').filter_map do |field|
134
134
  value = join_subfields(field, &subfield_in?(%w[i a s t o]))
135
135
  next if value.blank?
136
136
 
137
137
  value
138
138
  end
139
- other_formats + linked_alternate(record, '776') do |sf|
139
+ other_formats = values + linked_alternate(record, '776') do |sf|
140
140
  sf.code.in? %w[i a s t o]
141
141
  end
142
+ other_formats.uniq
142
143
  end
143
144
 
144
145
  # Retrieve cartographic reference data for map/atlas formats for display from
@@ -146,9 +147,9 @@ module PennMARC
146
147
  # @param [MARC::Record] record
147
148
  # @return [Array<String>]
148
149
  def cartographic_show(record)
149
- record.fields(%w[255 342]).map do |field|
150
+ record.fields(%w[255 342]).map { |field|
150
151
  join_subfields(field, &subfield_not_in?(%w[6 8]))
151
- end
152
+ }.uniq
152
153
  end
153
154
 
154
155
  # Check if a set of locations has any locations that include the term 'manuscripts'
@@ -165,14 +166,14 @@ module PennMARC
165
166
  # @param [MARC::Record] record
166
167
  # @return [Array]
167
168
  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]))
169
+ if field_defined?(record, Enriched::Pub::PHYS_INVENTORY_TAG)
170
+ record.fields(Enriched::Pub::PHYS_INVENTORY_TAG).map do |field|
171
+ join_subfields(field, &subfield_in?([Enriched::Pub::HOLDING_CLASSIFICATION_PART,
172
+ Enriched::Pub::HOLDING_ITEM_PART]))
172
173
  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]))
174
+ elsif field_defined?(record, Enriched::Api::PHYS_INVENTORY_TAG)
175
+ record.fields(Enriched::Api::PHYS_INVENTORY_TAG).map do |field|
176
+ join_subfields(field, &subfield_in?([Enriched::Api::PHYS_CALL_NUMBER_TYPE]))
176
177
  end
177
178
  else
178
179
  []