pennmarc 1.0.24 → 1.0.26

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +8 -9
  3. data/lib/pennmarc/enriched.rb +1 -0
  4. data/lib/pennmarc/helpers/access.rb +8 -6
  5. data/lib/pennmarc/helpers/creator.rb +139 -65
  6. data/lib/pennmarc/helpers/edition.rb +5 -3
  7. data/lib/pennmarc/helpers/identifier.rb +12 -0
  8. data/lib/pennmarc/helpers/note.rb +24 -19
  9. data/lib/pennmarc/helpers/production.rb +113 -20
  10. data/lib/pennmarc/helpers/subject.rb +10 -4
  11. data/lib/pennmarc/helpers/title.rb +39 -26
  12. data/lib/pennmarc/test/marc_helpers.rb +83 -0
  13. data/lib/pennmarc/util.rb +98 -69
  14. data/lib/pennmarc/version.rb +1 -1
  15. data/lib/pennmarc.rb +7 -0
  16. data/spec/lib/pennmarc/helpers/access_spec.rb +11 -2
  17. data/spec/lib/pennmarc/helpers/citation_spec.rb +0 -2
  18. data/spec/lib/pennmarc/helpers/classification_spec.rb +0 -2
  19. data/spec/lib/pennmarc/helpers/creator_spec.rb +103 -2
  20. data/spec/lib/pennmarc/helpers/database_spec.rb +0 -2
  21. data/spec/lib/pennmarc/helpers/date_spec.rb +0 -2
  22. data/spec/lib/pennmarc/helpers/edition_spec.rb +4 -2
  23. data/spec/lib/pennmarc/helpers/format_spec.rb +0 -2
  24. data/spec/lib/pennmarc/helpers/genre_spec.rb +0 -2
  25. data/spec/lib/pennmarc/helpers/identifer_spec.rb +14 -2
  26. data/spec/lib/pennmarc/helpers/inventory_spec.rb +0 -2
  27. data/spec/lib/pennmarc/helpers/language_spec.rb +0 -2
  28. data/spec/lib/pennmarc/helpers/link_spec.rb +0 -2
  29. data/spec/lib/pennmarc/helpers/location_spec.rb +0 -2
  30. data/spec/lib/pennmarc/helpers/note_spec.rb +22 -29
  31. data/spec/lib/pennmarc/helpers/production_spec.rb +121 -22
  32. data/spec/lib/pennmarc/helpers/relation_spec.rb +0 -2
  33. data/spec/lib/pennmarc/helpers/series_spec.rb +0 -2
  34. data/spec/lib/pennmarc/helpers/subject_spec.rb +16 -2
  35. data/spec/lib/pennmarc/helpers/title_spec.rb +20 -2
  36. data/spec/lib/pennmarc/marc_util_spec.rb +0 -2
  37. data/spec/lib/pennmarc/parser_spec.rb +1 -1
  38. data/spec/spec_helper.rb +7 -0
  39. data/spec/support/fixture_helpers.rb +10 -0
  40. metadata +4 -3
  41. data/spec/support/marc_spec_helpers.rb +0 -85
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 59881d630f244bc6d8b76665df84f4b22297de2d51c44f5272df4226f4e27e75
4
- data.tar.gz: 8d4e78c359dea40f482adc5b4deaa3fbb75bbe15a4fce5955ad48de054ce82c4
3
+ metadata.gz: 8b4d6a53347f4532faa566debcc59bde1fb7043cfbd4530d0a06489a36db8599
4
+ data.tar.gz: 8a92c36c80406541ac454025e6c2adc53af80dab20af5af16b8995dc5bacfe9d
5
5
  SHA512:
6
- metadata.gz: 4a3a43ff1cecadf978a8c50d59b382c2592925116cacd757f368ea0a698e9b370a94e74fd98e1a6a06a3b837ab0dbdce2efece991aaf3da4b53c74396a73ee2e
7
- data.tar.gz: 7f5468f9891c9fe15003160fb4d5a6c04cd8291ead5e803f0652368376e1fd67381195a7bdffd7ea9a7aa996c071b75240405dd956c46d879753f6811eca1b0a
6
+ metadata.gz: 8b1f0be0caba6a00adc8ccc5516556b2de250d93861157c49a60e17f52810b6eed7942efe8b51843d7428bb329c6dcad58a0887c17e9132004fe3d7d29729682
7
+ data.tar.gz: b06dad73899b13254391b74832d3b99745d82bc9f871801d7a617e2416f9d70039d45a9122a292377628b638c4044e692432688dbce48497cb6394fa48ba0d6e
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-17 17:00:02 UTC using RuboCop version 1.51.0.
3
+ # on 2024-07-02 15:41:46 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
@@ -14,7 +14,7 @@ Gemspec/RequireMFA:
14
14
  Exclude:
15
15
  - 'pennmarc.gemspec'
16
16
 
17
- # Offense count: 23
17
+ # Offense count: 25
18
18
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
19
19
  Metrics/AbcSize:
20
20
  Exclude:
@@ -26,18 +26,18 @@ Metrics/AbcSize:
26
26
  - 'lib/pennmarc/helpers/location.rb'
27
27
  - 'lib/pennmarc/helpers/note.rb'
28
28
  - 'lib/pennmarc/helpers/production.rb'
29
- - 'lib/pennmarc/helpers/relation.rb'
30
29
  - 'lib/pennmarc/helpers/series.rb'
31
30
  - 'lib/pennmarc/helpers/subject.rb'
32
31
  - 'lib/pennmarc/helpers/title.rb'
33
32
  - 'lib/pennmarc/util.rb'
34
33
 
35
- # Offense count: 5
34
+ # Offense count: 8
36
35
  # Configuration parameters: CountComments, Max, CountAsOne.
37
36
  Metrics/ClassLength:
38
37
  Exclude:
39
38
  - 'lib/pennmarc/helpers/creator.rb'
40
39
  - 'lib/pennmarc/helpers/format.rb'
40
+ - 'lib/pennmarc/helpers/production.rb'
41
41
  - 'lib/pennmarc/helpers/series.rb'
42
42
  - 'lib/pennmarc/helpers/subject.rb'
43
43
  - 'lib/pennmarc/helpers/title.rb'
@@ -54,13 +54,12 @@ Metrics/CyclomaticComplexity:
54
54
  - 'lib/pennmarc/helpers/language.rb'
55
55
  - 'lib/pennmarc/helpers/note.rb'
56
56
  - 'lib/pennmarc/helpers/production.rb'
57
- - 'lib/pennmarc/helpers/relation.rb'
58
57
  - 'lib/pennmarc/helpers/series.rb'
59
58
  - 'lib/pennmarc/helpers/subject.rb'
60
59
  - 'lib/pennmarc/helpers/title.rb'
61
60
  - 'lib/pennmarc/util.rb'
62
61
 
63
- # Offense count: 26
62
+ # Offense count: 28
64
63
  # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
65
64
  Metrics/MethodLength:
66
65
  Exclude:
@@ -82,7 +81,7 @@ Metrics/ModuleLength:
82
81
  Exclude:
83
82
  - 'lib/pennmarc/util.rb'
84
83
 
85
- # Offense count: 12
84
+ # Offense count: 14
86
85
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
87
86
  Metrics/PerceivedComplexity:
88
87
  Exclude:
@@ -123,7 +122,7 @@ Naming/VariableNumber:
123
122
  Exclude:
124
123
  - 'lib/pennmarc/util.rb'
125
124
 
126
- # Offense count: 7
125
+ # Offense count: 8
127
126
  # Configuration parameters: Max, CountAsOne.
128
127
  RSpec/ExampleLength:
129
128
  Exclude:
@@ -139,7 +138,7 @@ RSpec/FilePath:
139
138
  Exclude:
140
139
  - 'spec/lib/pennmarc/parser_spec.rb'
141
140
 
142
- # Offense count: 12
141
+ # Offense count: 23
143
142
  # Configuration parameters: Max, AllowedGroups.
144
143
  RSpec/NestedGroups:
145
144
  Exclude:
@@ -12,6 +12,7 @@ module PennMARC
12
12
  PHYS_INVENTORY_TAG = 'hld'
13
13
  ELEC_INVENTORY_TAG = 'prt'
14
14
  ITEM_TAG = 'itm'
15
+ RELATED_RECORD_TAG = 'rel'
15
16
 
16
17
  # Subfields for HLD tags
17
18
  # Follow MARC 852 spec: https://www.loc.gov/marc/holdings/hd852.html, but names are translated into Alma parlance
@@ -5,14 +5,13 @@ module PennMARC
5
5
  class Access < Helper
6
6
  ONLINE = 'Online'
7
7
  AT_THE_LIBRARY = 'At the library'
8
+ RESOURCE_LINK_BASE_URL = 'hdl.library.upenn.edu'
8
9
 
9
10
  class << self
10
11
  # Based on enhanced metadata fields added by Alma publishing process or API, determine if the record has
11
12
  # electronic access or has physical holdings, and is therefore "Online" or "At the library". If a record is "At
12
13
  # the library", but has a link to a finding aid in the 856 field (matching certain criteria), also add 'Online' as
13
14
  # an access method.
14
- # @todo What if none of these criteria match? Should we include "At the library" by default? Records with no value
15
- # in this field would be lost if the user selects a facet value.
16
15
  # @param [MARC::Record] record
17
16
  # @return [Array]
18
17
  def facet(record)
@@ -24,7 +23,7 @@ module PennMARC
24
23
  return values if values.size == 2 # return early if all values are already present
25
24
 
26
25
  # only check if ONLINE isn't already there
27
- values << ONLINE if values.exclude?(ONLINE) && finding_aid_linkage?(record)
26
+ values << ONLINE if values.exclude?(ONLINE) && resource_link?(record)
28
27
  values.uniq
29
28
  end
30
29
 
@@ -44,20 +43,23 @@ module PennMARC
44
43
  field.tag.in? [Enriched::Pub::PHYS_INVENTORY_TAG, Enriched::Api::PHYS_INVENTORY_TAG]
45
44
  end
46
45
 
47
- # Check if a record contains an 856 entry for an online finding aid, meeting these criteria:
46
+ # Check if a record contains an 856 entry with a Penn Handle server link meeting these criteria:
48
47
  # 1. Indicator 1 is 4 (HTTP resource)
49
48
  # 2. Indicator 2 is NOT 2 (indicating the linkage is to a "related" thing)
50
49
  # 3. The URL specified in subfield u (URI) is a Penn Handle link
50
+ # 4. The subfield z does NOT include the string 'Finding aid'
51
51
  # See: https://www.loc.gov/marc/bibliographic/bd856.html
52
+ # @note Some electronic records do not have Portfolios in Alma, so we rely upon the Resource Link in the 856 to
53
+ # get these records included in the Online category.
52
54
  # @param [MARC::Record] record
53
55
  # @return [Boolean]
54
- def finding_aid_linkage?(record)
56
+ def resource_link?(record)
55
57
  record.fields('856').filter_map do |field|
56
58
  next if field.indicator2 == '2' || field.indicator1 != '4'
57
59
 
58
60
  subz = subfield_values(field, 'z')
59
61
  subfield_values(field, 'u').filter_map do |value|
60
- return true if subz.include?('Finding aid') && value.include?('hdl.library.upenn.edu')
62
+ return true if subz.exclude?('Finding aid') && value.include?(RESOURCE_LINK_BASE_URL)
61
63
  end
62
64
  end
63
65
  false
@@ -7,26 +7,27 @@ module PennMARC
7
7
  # @todo can there ever be multiple 100 fields?
8
8
  # can ǂe and ǂ4 both be used at the same time? seems to result in duplicate values
9
9
  class Creator < Helper
10
- class << self
11
- # Main tags for Author/Creator information
12
- TAGS = %w[100 110].freeze
10
+ # Main tags for Author/Creator information
11
+ TAGS = %w[100 110].freeze
13
12
 
14
- # Aux tags for Author/Creator information, for use in search_aux method
15
- AUX_TAGS = %w[100 110 111 400 410 411 700 710 711 800 810 811].freeze
13
+ # Aux tags for Author/Creator information, for use in search_aux method
14
+ AUX_TAGS = %w[100 110 111 400 410 411 700 710 711 800 810 811].freeze
16
15
 
17
- CONFERENCE_SEARCH_TAGS = %w[111 711 811].freeze
16
+ CONFERENCE_SEARCH_TAGS = %w[111 711 811].freeze
17
+ CORPORATE_SEARCH_TAGS = %w[110 710 810].freeze
18
18
 
19
- # subfields NOT to join when combining raw subfield values
20
- NAME_EXCLUDED_SUBFIELDS = %w[a 1 4 5 6 8 t].freeze
19
+ # subfields NOT to join when combining raw subfield values
20
+ NAME_EXCLUDED_SUBFIELDS = %w[a 1 4 5 6 8 t].freeze
21
21
 
22
- CONTRIBUTOR_TAGS = %w[700 710].freeze
22
+ CONTRIBUTOR_TAGS = %w[700 710].freeze
23
23
 
24
- FACET_SOURCE_MAP = {
25
- 100 => 'abcdjq', 110 => 'abcdjq', 111 => 'abcen',
26
- 700 => 'abcdjq', 710 => 'abcdjq', 711 => 'abcen',
27
- 800 => 'abcdjq', 810 => 'abcdjq', 811 => 'abcen'
28
- }.freeze
24
+ FACET_SOURCE_MAP = {
25
+ 100 => 'abcdjq', 110 => 'abcdjq', 111 => 'abcen',
26
+ 700 => 'abcdjq', 710 => 'abcdjq', 711 => 'abcen',
27
+ 800 => 'abcdjq', 810 => 'abcdjq', 811 => 'abcen'
28
+ }.freeze
29
29
 
30
+ class << self
30
31
  # Author/Creator search field. Includes all subfield values (even ǂ0 URIs) from
31
32
  # {https://www.oclc.org/bibformats/en/1xx/100.html 100 Main Entry--Personal Name} and
32
33
  # {https://www.oclc.org/bibformats/en/1xx/110.html 110 Main Entry--Corporate Name}. Maps any relator codes found
@@ -35,8 +36,8 @@ module PennMARC
35
36
  # @todo are we including too many details here and gumming up our index? consider UIRs, relator labels, dates...
36
37
  # @todo shouldn't indicator1 tell us the order of the name? do we not trust the indicator?
37
38
  # @note ported from get_author_creator_1_search_values
38
- # @param [MARC::Record] record
39
- # @param [Hash] relator_map
39
+ # @param record [MARC::Record]
40
+ # @param relator_map [Hash]
40
41
  # @return [Array<String>] array of author/creator values for indexing
41
42
  def search(record, relator_map: Mappers.relator)
42
43
  name_search_values record: record, tags: TAGS, relator_map: relator_map
@@ -50,7 +51,7 @@ module PennMARC
50
51
  # and {https://www.loc.gov/marc/bibliographic/bd711.html MARC 711}. The 800, 810 and 8111 tags are similar in
51
52
  # theme to the 7xx fields but apply to serial records.
52
53
  # @note ported from get_author_creator_2_search_values
53
- # @param [MARC::Record] record
54
+ # @param record [MARC::Record]
54
55
  # @return [Array<String>] array of extended author/creator values for indexing
55
56
  def search_aux(record, relator_map: Mappers.relator)
56
57
  name_search_values record: record, tags: AUX_TAGS, relator_map: relator_map
@@ -60,7 +61,7 @@ module PennMARC
60
61
  # and {https://www.loc.gov/marc/bibliographic/bd110.html 110} and their linked alternates. First, joins subfields
61
62
  # other than $0, $1, $4, $6, $8, $e, and w. Then, appends any encoded relators found in $4.
62
63
  # If there are no valid encoded relators, uses the value found in $e.
63
- # @param [MARC::Record] record
64
+ # @param record [MARC::Record]
64
65
  # @return [Array<String>] array of author/creator values for display
65
66
  def show(record, relator_map: Mappers.relator)
66
67
  fields = record.fields(TAGS)
@@ -72,8 +73,8 @@ module PennMARC
72
73
 
73
74
  # Hash with main creator show values as the fields and the corresponding facet as the values.
74
75
  # Does not include linked 880s.
75
- # @param [MARC::Record] record
76
- # @param [Hash] relator_map
76
+ # @param record [MARC::Record]
77
+ # @param relator_map [Hash]
77
78
  # @return [Hash]
78
79
  def show_facet_map(record, relator_map: Mappers.relator)
79
80
  creators = record.fields(TAGS).filter_map do |field|
@@ -84,11 +85,82 @@ module PennMARC
84
85
  creators.to_h { |h| [h[:show], h[:facet]] }
85
86
  end
86
87
 
88
+ # Returns the list of authors with name (subfield $a) only
89
+ # @param [MARC::Record] record
90
+ # @param [Boolean] main_tags_only, if true, only use TAGS; otherwise use both TAGS and CONTRIBUTOR_TAGS
91
+ # @param [Boolean] first_initial_only: true to only use the first initial instead of first name
92
+ # @return [Array<String>] names of the authors
93
+ def authors_list(record, main_tags_only: false, first_initial_only: false)
94
+ tags = if main_tags_only
95
+ TAGS
96
+ else
97
+ TAGS + CONTRIBUTOR_TAGS
98
+ end
99
+
100
+ fields = record.fields(tags)
101
+ fields.filter_map { |field|
102
+ if first_initial_only
103
+ abbreviate_name(field['a']) if field['a']
104
+ else
105
+ field['a']
106
+ end
107
+ }.uniq
108
+ end
109
+
110
+ # Show the authors and contributors grouped together by relators with only names
111
+ # @param [MARC::Record] record
112
+ # @param [Hash] relator_map
113
+ # @param [Boolean] include_authors: true to include author fields TAGS
114
+ # @param [Boolean] name_only: true to include only the name subfield $a
115
+ # @param [Boolean] vernacular: true to include field 880 with subfield $6
116
+ # @return [Hash]
117
+ def contributors_list(record, relator_map: Mappers.relator, include_authors: true, name_only: true,
118
+ vernacular: false)
119
+ indicator_2_options = ['', ' ', '0']
120
+ tags = CONTRIBUTOR_TAGS
121
+
122
+ fields = record.fields(tags)
123
+ fields += record.fields('880').select { |field| subfield_value_in?(field, '6', CONTRIBUTOR_TAGS) } if vernacular
124
+
125
+ contributors = {}
126
+ fields.each do |field|
127
+ next if indicator_2_options.exclude?(field.indicator2) && field.tag.in?(CONTRIBUTOR_TAGS)
128
+ next if subfield_defined? field, 'i'
129
+
130
+ relator = relator(field: field, relator_term_sf: 'e', relator_map: relator_map)
131
+ relator = 'Contributor' if relator.blank?
132
+ relator = trim_punctuation(relator).capitalize
133
+
134
+ name = if name_only
135
+ field['a']
136
+ else
137
+ join_subfields(field, &subfield_in?(%w[a b c d j q u 3])) + ", #{relator}"
138
+ end
139
+
140
+ if contributors.key?(relator)
141
+ contributors[relator].push(name)
142
+ else
143
+ contributors[relator] = [name]
144
+ end
145
+ end
146
+
147
+ # add the authors
148
+ if include_authors
149
+ authors = authors_list(record, main_tags_only: true)
150
+ if contributors.key?('Author')
151
+ contributors['Author'] += authors
152
+ else
153
+ contributors['Author'] = authors
154
+ end
155
+ end
156
+ contributors
157
+ end
158
+
87
159
  # All author/creator values for display (like #show, but multivalued?) - no 880 linkage
88
160
  # Performs additional normalization of author names
89
161
  # @note ported from get_author_creator_values (indexed as author_creator_a) - shown on results page
90
- # @param [MARC::Record] record
91
- # @param [Hash] relator_map
162
+ # @param record [MARC::Record]
163
+ # @param relator_map [Hash]
92
164
  # @return [Array<String>] array of author/creator values for display
93
165
  def show_aux(record, relator_map: Mappers.relator)
94
166
  record.fields(TAGS).map { |field|
@@ -99,7 +171,7 @@ module PennMARC
99
171
  # Author/Creator sort. Does not map and include any relator codes.
100
172
  # @todo This includes any URI from ǂ0 which could help to disambiguate in sorts, but ǂ1 is excluded...
101
173
  # @note ported from get_author_creator_sort_values
102
- # @param [MARC::Record] record
174
+ # @param record [MARC::Record]
103
175
  # @return [String] string with author/creator value for sorting
104
176
  def sort(record)
105
177
  field = record.fields(TAGS).first
@@ -107,11 +179,11 @@ module PennMARC
107
179
  end
108
180
 
109
181
  # Author/Creator for faceting. Grabs values from a plethora of fields, joins defined subfields, then trims some
110
- # punctuation (@see trim_punctuation)
182
+ # punctuation (@see Util.trim_punctuation)
111
183
  # @todo should trim_punctuation apply to each subfield value, or the joined values? i think the joined values
112
184
  # @note ported from author_creator_xfacet2_input - is this the best choice? check the copyField declarations -
113
185
  # franklin uses author_creator_f
114
- # @param [MARC::Record] record
186
+ # @param record [MARC::Record]
115
187
  # @return [Array<String>] array of author/creator values for faceting
116
188
  def facet(record)
117
189
  FACET_SOURCE_MAP.flat_map { |field_num, subfields|
@@ -123,8 +195,8 @@ module PennMARC
123
195
 
124
196
  # Conference for display, intended for results display
125
197
  # @note ported from get_conference_values
126
- # @param [MARC::Record] record
127
- # @param [Hash] relator_map
198
+ # @param record [MARC::Record]
199
+ # @param relator_map [Hash]
128
200
  # @return [Array<String>] array of conference values
129
201
  def conference_show(record, relator_map: Mappers.relator)
130
202
  record.fields('111').filter_map { |field|
@@ -139,7 +211,7 @@ module PennMARC
139
211
  # using subfields $e and $w. We append any relators, preferring those defined in $4 and using $j as a fallback.
140
212
  # @note ported from get_conference_values
141
213
  # @todo what is ǂi for?
142
- # @param [MARC::Record] record
214
+ # @param record [MARC::Record]
143
215
  # @return [Array<String>] array of conference values
144
216
  def conference_detail_show(record, relator_map: Mappers.relator)
145
217
  conferences = record.fields(%w[111 711]).filter_map do |field|
@@ -164,8 +236,8 @@ module PennMARC
164
236
  # Return hash of detailed conference values mapped to their corresponding facets from fields
165
237
  # {https://www.loc.gov/marc/bibliographic/bd111.html 111} and
166
238
  # {https://www.loc.gov/marc/bibliographic/bd711.html 711}. Does not include linked 880s.
167
- # @param [MARC::Record] record
168
- # @param [Hash] relator_map
239
+ # @param record [MARC::Record]
240
+ # @param relator_map [Hash]
169
241
  # @return [Hash]
170
242
  def conference_detail_show_facet_map(record, relator_map: Mappers.relator)
171
243
  conferences = record.fields(%w[111 711]).filter_map do |field|
@@ -180,7 +252,7 @@ module PennMARC
180
252
  end
181
253
 
182
254
  # Conference name values for searching
183
- # @param [MARC::Record] record
255
+ # @param record [MARC::Record]
184
256
  # @return [Array<String>]
185
257
  def conference_search(record)
186
258
  record.fields(CONFERENCE_SEARCH_TAGS).filter_map { |field|
@@ -188,14 +260,23 @@ module PennMARC
188
260
  }.uniq
189
261
  end
190
262
 
263
+ # Corporate author search values for searching
264
+ # @param record [MARC::Record]
265
+ # @return [Array<String>]
266
+ def corporate_search(record)
267
+ record.fields(CORPORATE_SEARCH_TAGS).filter_map do |field|
268
+ join_subfields(field, &subfield_in?(%w[a b c d]))
269
+ end
270
+ end
271
+
191
272
  # Retrieve contributor values for display from fields {https://www.oclc.org/bibformats/en/7xx/700.html 700}
192
273
  # and {https://www.oclc.org/bibformats/en/7xx/710.html 710} and their linked alternates. Joins subfields
193
274
  # 'a', 'b', 'c', 'd', 'j', and 'q', 'u', and '3'. Then appends resulting string with any encoded relationships
194
275
  # found in $4. If there are no valid encoded relationships, uses the value found in $e.
195
276
  # @note legacy version returns array of hash objects including data for display link
196
277
  # @todo is it okay to include 880 $4 here? Legacy includes $4 in main author display 880 but not here.
197
- # @param [MARC::Record] record
198
- # @ param [Hash] relator_map
278
+ # @param record [MARC::Record]
279
+ # @param relator_map [Hash]
199
280
  # @return [Array<String>]
200
281
  def contributor_show(record, relator_map: Mappers.relator)
201
282
  indicator_2_options = ['', ' ', '0']
@@ -212,9 +293,9 @@ module PennMARC
212
293
 
213
294
  private
214
295
 
215
- # @param [MARC::Record] record
216
- # @param [Array] tags to consider
217
- # @param [Hash] relator_map
296
+ # @param record [MARC::Record]
297
+ # @param tags [Array] to consider
298
+ # @param relator_map [Hash]
218
299
  # @return [Array<String>] name values from given tags
219
300
  def name_search_values(record:, tags:, relator_map:)
220
301
  acc = record.fields(tags).filter_map do |field|
@@ -238,30 +319,10 @@ module PennMARC
238
319
  acc.uniq
239
320
  end
240
321
 
241
- # Trim punctuation method extracted from Traject macro, to ensure consistent output
242
- # @todo move to Util?
243
- # @param [String] string
244
- # @return [String] string with relevant punctuation removed
245
- def trim_punctuation(string)
246
- return string unless string
247
-
248
- string = string.sub(%r{ *[ ,/;:] *\Z}, '')
249
-
250
- # trailing period if it is preceded by at least three letters (possibly preceded and followed by whitespace)
251
- string = string.sub(/( *[[:word:]]{3,})\. *\Z/, '\1')
252
-
253
- # single square bracket characters if they are the start and/or end chars and there are no internal square
254
- # brackets.
255
- string = string.sub(/\A\[?([^\[\]]+)\]?\Z/, '\1')
256
-
257
- # trim any leading or trailing whitespace
258
- string.strip
259
- end
260
-
261
322
  # Extract the information we care about from 1xx fields, map relator codes, and use appropriate punctuation
262
- # @param [MARC::Field] field
263
- # @param [Hash] mapping
264
- # @param [Boolean] should_convert_name_order
323
+ # @param field [MARC::Field]
324
+ # @param mapping [Hash]
325
+ # @param should_convert_name_order [Boolean]
265
326
  # @return [String] joined subfield values for value from field
266
327
  def name_from_main_entry(field, mapping, should_convert_name_order: false)
267
328
  relator_term_sf = relator_term_subfield(field)
@@ -284,7 +345,7 @@ module PennMARC
284
345
  end
285
346
 
286
347
  # Convert "Lastname, First" to "First Lastname"
287
- # @param [String] name value for processing
348
+ # @param name [String] value for processing
288
349
  # @return [String]
289
350
  def convert_name_order(name)
290
351
  return name unless name.include? ','
@@ -294,17 +355,30 @@ module PennMARC
294
355
  "#{after_comma} #{before_comma}".squish
295
356
  end
296
357
 
358
+ # Convert "Lastname, First" to "Lastname, F"
359
+ # @param [String] name
360
+ def abbreviate_name(name)
361
+ name_parts = name.split(', ')
362
+ return '' if name_parts.empty?
363
+
364
+ first_name_parts = name_parts.last.split
365
+ temp_name = "#{name_parts.first}, #{first_name_parts.first[0, 1]}."
366
+ first_name_parts.shift
367
+ temp_name += " #{first_name_parts.join(' ')}" unless first_name_parts.empty?
368
+ temp_name
369
+ end
370
+
297
371
  # Parse creator facet value from given creator field and desired subfields
298
- # @param [MARC::Field] field
299
- # @param [Array<String>] subfields
372
+ # @param field [MARC::Field]
373
+ # @param subfields [Array<String>]
300
374
  # @return [String]
301
375
  def parse_facet_value(field, subfields)
302
376
  trim_punctuation(join_subfields(field, &subfield_in?(subfields)))
303
377
  end
304
378
 
305
379
  # Parse creator show value from given main creator fields (100/110).
306
- # @param [MARC::Field] field
307
- # @param [Hash] relator_map
380
+ # @param field [MARC::Field]
381
+ # @param relator_map [Hash]
308
382
  # @return [String]
309
383
  def parse_show_value(field, relator_map: Mappers.relator)
310
384
  creator = join_subfields(field, &subfield_not_in?(%w[0 1 4 6 8 e w]))
@@ -314,7 +388,7 @@ module PennMARC
314
388
  # Parse detailed conference show value from given conference field (111/711). If there is no $i, we join subfield
315
389
  # values other than $0, $4, $5, $6, $8, $e, $j, and $w to create conference value. We join subfields $e and $w to
316
390
  # determine the subunit value. We append any relators, preferring those defined in $4 and using $j as a fallback.
317
- # @param [MARC::Field] field
391
+ # @param field [MARC::Field]
318
392
  # @return [String]
319
393
  def parse_conference_detail_show_value(field, relator_map: Mappers.relator)
320
394
  conf = if subfield_undefined? field, 'i'
@@ -13,10 +13,12 @@ module PennMARC
13
13
  # https://www.loc.gov/marc/bibliographic/bd250.html
14
14
  # @param [MARC::Record] record
15
15
  # @return [Array<String>] array of editions and their alternates
16
- def show(record)
17
- editions = record.fields('250').map { |field|
16
+ def show(record, with_alternate: true)
17
+ editions = record.fields('250').map do |field|
18
18
  join_subfields(field, &subfield_not_in?(%w[6 8]))
19
- } + linked_alternate_not_6_or_8(record, '250')
19
+ end
20
+ editions += linked_alternate_not_6_or_8(record, '250') if with_alternate
21
+
20
22
  editions.uniq
21
23
  end
22
24
 
@@ -159,6 +159,18 @@ module PennMARC
159
159
  }.uniq
160
160
  end
161
161
 
162
+ # Gets any Host record MMS ID values from an Enriched::Pub::RELATED_RECORD_TAG field added during Alma enrichment.
163
+ # This aids in our handling of "bound with" records.
164
+ # @param [MARC::Record] record
165
+ # @return [Array<String>]
166
+ def host_record_id(record)
167
+ record.fields(Enriched::Pub::RELATED_RECORD_TAG).filter_map { |field|
168
+ next unless subfield_value?(field, 'c', /contains/i)
169
+
170
+ subfield_values field, :w
171
+ }.flatten.uniq
172
+ end
173
+
162
174
  private
163
175
 
164
176
  # Determine if subfield 'a' is an OCLC id.