pennmarc 1.0.24 → 1.0.26

Sign up to get free protection for your applications and to get access to all the features.
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.