pennmarc 1.0.12 → 1.0.15.pre

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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
 
@@ -34,11 +34,12 @@ module PennMARC
34
34
  # @param [MARC::Record] record
35
35
  # @return [Array<String>]
36
36
  def isbn_show(record)
37
- isbn_values = record.fields('020').filter_map do |field|
37
+ values = record.fields('020').filter_map do |field|
38
38
  joined_isbn = join_subfields(field, &subfield_in?(%w[a]))
39
39
  joined_isbn.presence
40
40
  end
41
- isbn_values + linked_alternate(record, '020', &subfield_in?(%w[a]))
41
+ isbn_values = values + linked_alternate(record, '020', &subfield_in?(%w[a]))
42
+ isbn_values.uniq
42
43
  end
43
44
 
44
45
  # Get ISSN values for display from the {https://www.oclc.org/bibformats/en/0xx/022.html 022 field} and related
@@ -47,11 +48,12 @@ module PennMARC
47
48
  # @param [MARC::Record] record
48
49
  # @return [Array<String>]
49
50
  def issn_show(record)
50
- issn_values = record.fields('022').filter_map do |field|
51
+ values = record.fields('022').filter_map do |field|
51
52
  joined_issn = join_subfields(field, &subfield_in?(%w[a]))
52
53
  joined_issn.presence
53
54
  end
54
- issn_values + linked_alternate(record, '022', &subfield_in?(%w[a]))
55
+ issn_values = values + linked_alternate(record, '022', &subfield_in?(%w[a]))
56
+ issn_values.uniq
55
57
  end
56
58
 
57
59
  # Get numeric OCLC ID of first {https://www.oclc.org/bibformats/en/0xx/035.html 035 field}
@@ -82,7 +84,7 @@ module PennMARC
82
84
  # @param [MARC::Record] record
83
85
  # @return [Array<String>]
84
86
  def oclc_id_search(record)
85
- record.fields('035').flat_map do |field|
87
+ record.fields('035').flat_map { |field|
86
88
  field.filter_map do |subfield|
87
89
  # skip unless subfield 'a' or 'z'
88
90
  next unless subfield.code.in?(%w[a z])
@@ -98,7 +100,7 @@ module PennMARC
98
100
 
99
101
  match[1]
100
102
  end
101
- end
103
+ }.uniq
102
104
  end
103
105
 
104
106
  # Get publisher issued identifiers from fields {https://www.oclc.org/bibformats/en/0xx/024.html 024},
@@ -109,7 +111,7 @@ module PennMARC
109
111
  # @param [MARC::Record] record
110
112
  # @return [Array<string>]
111
113
  def publisher_number_show(record)
112
- record.fields(%w[024 028 880]).filter_map do |field|
114
+ record.fields(%w[024 028 880]).filter_map { |field|
113
115
  next if field.tag == '880' && subfield_value_not_in?(field, '6', %w[024 028])
114
116
 
115
117
  # do not return doi values from 024 ǂ2
@@ -118,7 +120,7 @@ module PennMARC
118
120
  else
119
121
  join_subfields(field, &subfield_not_in?(%w[5 6])).presence
120
122
  end
121
- end
123
+ }.uniq
122
124
  end
123
125
 
124
126
  # Get publisher issued identifiers for searching of a record. Values extracted from fields
@@ -127,19 +129,19 @@ module PennMARC
127
129
  # @param [MARC::Record] record
128
130
  # @return [Array<String>]
129
131
  def publisher_number_search(record)
130
- record.fields(%w[024 028]).filter_map do |field|
132
+ record.fields(%w[024 028]).filter_map { |field|
131
133
  joined_identifiers = join_subfields(field, &subfield_in?(%w[a]))
132
134
  joined_identifiers.presence
133
- end
135
+ }.uniq
134
136
  end
135
137
 
136
138
  # Retrieve fingerprint for display from the {https://www.oclc.org/bibformats/en/0xx/026.html 026} field
137
139
  # @param [MARC::Record] record
138
140
  # @return [Array<String>]
139
141
  def fingerprint_show(record)
140
- record.fields('026').map do |field|
142
+ record.fields('026').map { |field|
141
143
  join_subfields(field, &subfield_not_in?(%w[2 5 6 8]))
142
- end
144
+ }.uniq
143
145
  end
144
146
 
145
147
  # Retrieve DOI values stored in {https://www.oclc.org/bibformats/en/0xx/024.html 024}.
@@ -147,14 +149,14 @@ module PennMARC
147
149
  # @param [MARC::Record] record
148
150
  # @return [Array<String>]
149
151
  def doi_show(record)
150
- record.fields('024').filter_map do |field|
152
+ record.fields('024').filter_map { |field|
151
153
  # skip unless indicator1 is '7'
152
154
  next unless field.indicator1.in?(%w[7])
153
155
  # skip unless ǂ2 is the string literal 'doi'
154
156
  next unless subfield_value_in?(field, '2', %w[doi])
155
157
 
156
158
  join_subfields(field, &subfield_in?(%w[a]))
157
- end
159
+ }.uniq
158
160
  end
159
161
 
160
162
  private
@@ -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
@@ -18,7 +18,8 @@ module PennMARC
18
18
  values = record.fields('546').map do |field|
19
19
  join_subfields field, &subfield_not_in?(%w[6 8])
20
20
  end
21
- values + linked_alternate(record, '546', &subfield_not_in?(%w[6 8]))
21
+ language_values = values + linked_alternate(record, '546', &subfield_not_in?(%w[6 8]))
22
+ language_values.uniq
22
23
  end
23
24
 
24
25
  # Get language values for searching and faceting of a record. The values are extracted from subfields
@@ -33,7 +34,7 @@ module PennMARC
33
34
  # @param [MARC::Record] record
34
35
  # @param [Hash] iso_639_2_mapping iso-639-2 spec hash for language code translation
35
36
  # @param [Hash] iso_639_3_mapping iso-639-3 spec hash for language code translation
36
- # @return [Array] array of language values
37
+ # @return [Array<String>] array of language values
37
38
  def values(record, iso_639_2_mapping: Mappers.iso_639_2_language, iso_639_3_mapping: Mappers.iso_639_3_language)
38
39
  values = record.fields('041').filter_map { |field|
39
40
  mapper = subfield_value?(field, '2', /iso639-3/) ? iso_639_3_mapping : iso_639_2_mapping
@@ -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 }
@@ -16,11 +16,11 @@ module PennMARC
16
16
  # @return [Array<String>]
17
17
  def notes_show(record)
18
18
  notes_fields = %w[500 502 504 515 518 525 533 540 550 580 586 588]
19
- record.fields(notes_fields + ['880']).filter_map do |field|
19
+ record.fields(notes_fields + ['880']).filter_map { |field|
20
20
  next if field.tag == '880' && subfield_value_not_in?(field, '6', notes_fields)
21
21
 
22
22
  join_subfields(field, &subfield_not_in?(%w[5 6 8]))
23
- end
23
+ }.uniq
24
24
  end
25
25
 
26
26
  # Retrieve local notes for display from fields {https://www.oclc.org/bibformats/en/5xx/561.html 561},
@@ -38,11 +38,12 @@ module PennMARC
38
38
 
39
39
  additional_fields = %w[562 563 585 590]
40
40
 
41
- local_notes + record.fields(additional_fields + ['880']).filter_map do |field|
41
+ notes = local_notes + record.fields(additional_fields + ['880']).filter_map do |field|
42
42
  next if field.tag == '880' && subfield_value_not_in?(field, '6', additional_fields)
43
43
 
44
44
  join_subfields(field, &subfield_not_in?(%w[5 6 8]))
45
45
  end
46
+ notes.uniq
46
47
  end
47
48
 
48
49
  # Retrieve provenance notes for display from fields {https://www.oclc.org/bibformats/en/5xx/561.html 561} and
@@ -63,7 +64,8 @@ module PennMARC
63
64
 
64
65
  join_subfields(field, &subfield_in?(%w[a]))
65
66
  end
66
- provenance_notes + prefixed_subject_and_alternate(record, 'PRO')
67
+ notes = provenance_notes + prefixed_subject_and_alternate(record, 'PRO')
68
+ notes.uniq
67
69
  end
68
70
 
69
71
  # Retrieve contents notes for display from fields {https://www.oclc.org/bibformats/en/5xx/505.html 505} and
@@ -75,16 +77,16 @@ module PennMARC
75
77
  next if field.tag == '880' && subfield_value_not_in?(field, '6', %w[505])
76
78
 
77
79
  join_subfields(field, &subfield_not_in?(%w[6 8])).split('--')
78
- }.flatten
80
+ }.flatten.uniq
79
81
  end
80
82
 
81
83
  # Retrieve access restricted notes for display from field {https://www.oclc.org/bibformats/en/5xx/506.html 506}.
82
84
  # @param [MARC::Record] record
83
85
  # @return [Array<String>]
84
86
  def access_restriction_show(record)
85
- record.fields('506').filter_map do |field|
87
+ record.fields('506').filter_map { |field|
86
88
  join_subfields(field, &subfield_not_in?(%w[5 6]))
87
- end
89
+ }.uniq
88
90
  end
89
91
 
90
92
  # Retrieve finding aid notes for display from field {https://www.oclc.org/bibformats/en/5xx/555.html 555} and its
@@ -161,7 +163,7 @@ module PennMARC
161
163
 
162
164
  sub3_and_other_subs(field, &subfield_in?(%w[a b c d e f]))
163
165
  end
164
- system_details_notes
166
+ system_details_notes.uniq
165
167
  end
166
168
 
167
169
  private
@@ -8,21 +8,21 @@ module PennMARC
8
8
  # @param [MARC::Record] record
9
9
  # @return [Array<String>]
10
10
  def show(record)
11
- get_264_or_880_fields(record, '0')
11
+ get_264_or_880_fields(record, '0').uniq
12
12
  end
13
13
 
14
14
  # Retrieve distribution values for display from {https://www.oclc.org/bibformats/en/2xx/264.html 264 field}.
15
15
  # @param [MARC::Record] record
16
16
  # @return [Array<String>]
17
17
  def distribution_show(record)
18
- get_264_or_880_fields(record, '2')
18
+ get_264_or_880_fields(record, '2').uniq
19
19
  end
20
20
 
21
21
  # Retrieve manufacture values for display from {https://www.oclc.org/bibformats/en/2xx/264.html 264 field}.
22
22
  # @param [MARC::Record] record
23
23
  # @return [Array<String>]
24
24
  def manufacture_show(record)
25
- get_264_or_880_fields(record, '3')
25
+ get_264_or_880_fields(record, '3').uniq
26
26
  end
27
27
 
28
28
  # Retrieve publication values. Return publication values from
@@ -60,7 +60,7 @@ module PennMARC
60
60
 
61
61
  values += joined264
62
62
  end
63
- values.filter_map { |value| value&.strip }
63
+ values.filter_map { |value| value&.strip }.uniq
64
64
  end
65
65
 
66
66
  # Retrieve publication values for display from fields
@@ -68,7 +68,7 @@ module PennMARC
68
68
  # {https://www.oclc.org/bibformats/en/2xx/260.html 260}-262 and their linked alternates,
69
69
  # and {https://www.oclc.org/bibformats/en/2xx/264.html 264} and its linked alternate.
70
70
  # @param [MARC::Record] record
71
- # @return [Object]
71
+ # @return [Array<String>]
72
72
  def publication_show(record)
73
73
  values = record.fields('245').first(1).flat_map { |field| subfield_values(field, 'f') }
74
74
 
@@ -86,22 +86,22 @@ module PennMARC
86
86
  end
87
87
 
88
88
  values += get_264_or_880_fields(record, '1')
89
- values.compact_blank
89
+ values.compact_blank.uniq
90
90
  end
91
91
 
92
92
  # Retrieve place of publication for display from {https://www.oclc.org/bibformats/en/7xx/752.html 752 field} and
93
93
  # its linked alternate.
94
94
  # @note legacy version returns array of hash objects including data for display link
95
95
  # @param [MARC::Record] record
96
- # @return [Object]
96
+ # @return [Array<String>]
97
97
  def place_of_publication_show(record)
98
- record.fields(%w[752 880]).filter_map do |field|
98
+ record.fields(%w[752 880]).filter_map { |field|
99
99
  next if field.tag == '880' && subfield_values(field, '6').exclude?('752')
100
100
 
101
101
  place = join_subfields(field, &subfield_not_in?(%w[6 8 e w]))
102
102
  place_extra = join_subfields(field, &subfield_in?(%w[e w]))
103
103
  "#{place} #{place_extra}"
104
- end
104
+ }.uniq
105
105
  end
106
106
 
107
107
  private
@@ -14,11 +14,11 @@ module PennMARC
14
14
  # in this field should be sufficient to locate host item record.
15
15
  #
16
16
  # @param [MARC::Record] record
17
- # @return [Array] contained in values for display
17
+ # @return [Array<String>] contained in values for display
18
18
  def contained_in_show(record)
19
- record.fields('773').map do |field|
19
+ record.fields('773').map { |field|
20
20
  join_subfields(field, &subfield_not_in?(%w[6 7 8 w]))
21
- end
21
+ }.uniq
22
22
  end
23
23
 
24
24
  # Get "chronology" information from specially-prefixed 650 (subject) fields
@@ -56,7 +56,7 @@ module PennMARC
56
56
 
57
57
  values_with_title_prefix(field, sf_exclude: %w[0 4 6 8 i], relator_map: relator_map)
58
58
  end
59
- values + record.fields('880').filter_map do |field|
59
+ relation_values = values + record.fields('880').filter_map do |field|
60
60
  next if field.indicator2.present?
61
61
 
62
62
  next unless subfield_value?(field, '6', /^(#{RELATED_WORK_FIELDS.join('|')})/)
@@ -65,6 +65,7 @@ module PennMARC
65
65
 
66
66
  values_with_title_prefix(field, sf_exclude: %w[0 4 6 8 i], relator_map: relator_map)
67
67
  end
68
+ relation_values.uniq
68
69
  end
69
70
 
70
71
  # Get "Contains" values from {CONTAINS_FIELDS} in the 7XX range. Must have indicator 2 value of 2 indicating an
@@ -72,20 +73,21 @@ module PennMARC
72
73
  # values in sf 0, 5, 6, and 8.
73
74
  # @param [MARC::Record] record
74
75
  # @param [Hash] relator_map
75
- # @return [Array]
76
+ # @return [Array<String>]
76
77
  def contains_show(record, relator_map: Mappers.relator)
77
- acc = record.fields(CONTAINS_FIELDS).filter_map do |field|
78
+ values = record.fields(CONTAINS_FIELDS).filter_map do |field|
78
79
  next unless field.indicator2 == '2'
79
80
 
80
81
  values_with_title_prefix(field, sf_exclude: %w[0 4 5 6 8 i], relator_map: relator_map)
81
82
  end
82
- acc + record.fields('880').filter_map do |field|
83
+ contains_values = values + record.fields('880').filter_map do |field|
83
84
  next unless field.indicator2 == '2'
84
85
 
85
86
  next unless subfield_value?(field, '6', /^(#{CONTAINS_FIELDS.join('|')})/)
86
87
 
87
88
  values_with_title_prefix(field, sf_include: %w[0 5 6 8 i])
88
89
  end
90
+ contains_values.uniq
89
91
  end
90
92
 
91
93
  # Get "Constituent Unit" values from {https://www.oclc.org/bibformats/en/7xx/774.html MARC 774}. Include
@@ -93,10 +95,11 @@ module PennMARC
93
95
  # @param [MARC::Record] record
94
96
  # @return [Array]
95
97
  def constituent_unit_show(record)
96
- acc = record.fields('774').filter_map do |field|
98
+ values = record.fields('774').filter_map do |field|
97
99
  join_subfields(field, &subfield_in?(%w[i a s t]))
98
100
  end
99
- acc + linked_alternate(record, '774', &subfield_in?(%w[i a s t]))
101
+ constituent_values = values + linked_alternate(record, '774', &subfield_in?(%w[i a s t]))
102
+ constituent_values.uniq
100
103
  end
101
104
 
102
105
  # Get "Has Supplement" values from {https://www.oclc.org/bibformats/en/7xx/770.html MARC 770}. Ignore
@@ -31,7 +31,8 @@ module PennMARC
31
31
  end || []
32
32
 
33
33
  values += remaining_show_entries(record, tags_present)
34
- values + series_880_fields(record)
34
+ series_values = values + series_880_fields(record)
35
+ series_values.uniq
35
36
  end
36
37
 
37
38
  # Values from series fields for display.
@@ -75,7 +76,7 @@ module PennMARC
75
76
 
76
77
  filtered_values.map { |v| v.gsub(/\(|\)/, '') }.join(' ')
77
78
  end
78
- values
79
+ values.uniq
79
80
  end
80
81
 
81
82
  # Information concerning the immediate predecessor of the target item (chronological relationship). When a note
@@ -83,9 +84,9 @@ module PennMARC
83
84
  # indicator position for display.
84
85
  # https://www.loc.gov/marc/bibliographic/bd780.html
85
86
  # @param [MARC::Record] record
86
- # @return [String] continues fields string
87
+ # @return [Array<String>] continues fields string
87
88
  def get_continues_show(record)
88
- continues(record, '780')
89
+ continues(record, '780').uniq
89
90
  end
90
91
 
91
92
  # Information concerning the immediate successor to the target item (chronological relationship). When a note is
@@ -93,9 +94,9 @@ module PennMARC
93
94
  # position for display.
94
95
  # https://www.loc.gov/marc/bibliographic/bd785.html
95
96
  # @param [MARC::Record] record
96
- # @return [String] continued by fields string
97
+ # @return [Array<String>] continued by fields string
97
98
  def get_continued_by_show(record)
98
- continues(record, '785')
99
+ continues(record, '785').uniq
99
100
  end
100
101
 
101
102
  private
@@ -127,7 +128,7 @@ module PennMARC
127
128
  # @note added 2017/04/10: filter out 0 (authority record numbers) added by Alma
128
129
  # @param [MARC::Record] record
129
130
  # @param [String] first_tag
130
- # @return [Array<Hash>] array of author show entry hashes
131
+ # @return [Array<String>] array of author show entry strings
131
132
  def title_show_entries(record, first_tag)
132
133
  record.fields(first_tag).map do |field|
133
134
  series = join_subfields(field, &subfield_not_in?(%w[0 5 6 8 c e w v n]))
@@ -156,6 +157,7 @@ module PennMARC
156
157
  # links that field to the 880 field. The data in field 880 may be in more than one script. This function exists
157
158
  # because it differs than the tradition use of linked_alternate.
158
159
  # @param [MARC::Record] record
160
+ # @return [Array<String>]
159
161
  def series_880_fields(record)
160
162
  record.fields('880').filter_map do |field|
161
163
  next unless subfield_value?(field, '6', /^(800|810|811|830|400|410|411|440|490)/)
@@ -189,7 +191,7 @@ module PennMARC
189
191
  # Get subfields from a given field (continues or continued_by).
190
192
  # @param [MARC::Record] record
191
193
  # @param [String] tag
192
- # @return [String] joined subfields
194
+ # @return [Array<String>] joined subfields
193
195
  def continues(record, tag)
194
196
  record.fields.filter_map do |field|
195
197
  next unless field.tag == tag || (field.tag == '880' && subfield_value?(field, '6', /^#{tag}/))