pennmarc 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +119 -0
- data/README.md +82 -0
- data/legacy/indexer.rb +568 -0
- data/legacy/marc.rb +2964 -0
- data/legacy/test_file_output.json +49 -0
- data/lib/pennmarc/encoding_level.rb +43 -0
- data/lib/pennmarc/enriched_marc.rb +36 -0
- data/lib/pennmarc/heading_control.rb +11 -0
- data/lib/pennmarc/helpers/citation.rb +31 -0
- data/lib/pennmarc/helpers/creator.rb +237 -0
- data/lib/pennmarc/helpers/database.rb +89 -0
- data/lib/pennmarc/helpers/date.rb +85 -0
- data/lib/pennmarc/helpers/edition.rb +90 -0
- data/lib/pennmarc/helpers/format.rb +312 -0
- data/lib/pennmarc/helpers/genre.rb +71 -0
- data/lib/pennmarc/helpers/helper.rb +11 -0
- data/lib/pennmarc/helpers/identifier.rb +134 -0
- data/lib/pennmarc/helpers/language.rb +37 -0
- data/lib/pennmarc/helpers/link.rb +12 -0
- data/lib/pennmarc/helpers/location.rb +97 -0
- data/lib/pennmarc/helpers/note.rb +132 -0
- data/lib/pennmarc/helpers/production.rb +131 -0
- data/lib/pennmarc/helpers/relation.rb +135 -0
- data/lib/pennmarc/helpers/series.rb +118 -0
- data/lib/pennmarc/helpers/subject.rb +304 -0
- data/lib/pennmarc/helpers/title.rb +197 -0
- data/lib/pennmarc/mappings/language.yml +516 -0
- data/lib/pennmarc/mappings/locations.yml +1801 -0
- data/lib/pennmarc/mappings/relator.yml +263 -0
- data/lib/pennmarc/parser.rb +177 -0
- data/lib/pennmarc/util.rb +240 -0
- data/lib/pennmarc.rb +6 -0
- data/pennmarc.gemspec +22 -0
- data/spec/fixtures/marcxml/test.xml +167 -0
- data/spec/lib/pennmarc/helpers/citation_spec.rb +27 -0
- data/spec/lib/pennmarc/helpers/creator_spec.rb +183 -0
- data/spec/lib/pennmarc/helpers/database_spec.rb +60 -0
- data/spec/lib/pennmarc/helpers/date_spec.rb +105 -0
- data/spec/lib/pennmarc/helpers/edition_spec.rb +38 -0
- data/spec/lib/pennmarc/helpers/format_spec.rb +200 -0
- data/spec/lib/pennmarc/helpers/genre_spec.rb +89 -0
- data/spec/lib/pennmarc/helpers/identifer_spec.rb +105 -0
- data/spec/lib/pennmarc/helpers/language_spec.rb +30 -0
- data/spec/lib/pennmarc/helpers/location_spec.rb +70 -0
- data/spec/lib/pennmarc/helpers/note_spec.rb +233 -0
- data/spec/lib/pennmarc/helpers/production_spec.rb +193 -0
- data/spec/lib/pennmarc/helpers/relation_spec.rb +120 -0
- data/spec/lib/pennmarc/helpers/subject_spec.rb +262 -0
- data/spec/lib/pennmarc/helpers/title_spec.rb +169 -0
- data/spec/lib/pennmarc/marc_util_spec.rb +206 -0
- data/spec/lib/pennmarc/parser_spec.rb +13 -0
- data/spec/spec_helper.rb +104 -0
- data/spec/support/marc_spec_helpers.rb +84 -0
- metadata +171 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PennMARC
|
4
|
+
# Methods that return Library and Location values from Alma enhanced MARC fields
|
5
|
+
class Location < Helper
|
6
|
+
class << self
|
7
|
+
# Retrieves library location from enriched marc 'itm' or 'hld' fields, giving priority to the item location over
|
8
|
+
# 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.
|
10
|
+
# @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/
|
11
|
+
# Alma documentation for these added fields
|
12
|
+
# @param [MARC::Record] record
|
13
|
+
# @param [Hash] location_map hash with location_code as key and location hash as value
|
14
|
+
# @return [Array<String>] Array of library locations retrieved from location_map
|
15
|
+
def library(record, location_map)
|
16
|
+
location(record: record, location_map: location_map, display_value: 'library')
|
17
|
+
end
|
18
|
+
|
19
|
+
# Retrieves the specific location from enriched marc 'itm' or 'hld' fields, giving priority to the item location
|
20
|
+
# over the holdings location. Returns item library location if available. Otherwise, returns holdings library
|
21
|
+
# location.
|
22
|
+
# {PennMARC::EnrichedMarc} maps enriched marc fields and subfields created during Alma publishing.
|
23
|
+
# @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/
|
24
|
+
# Alma documentation for these added fields
|
25
|
+
# @param [MARC::Record] record
|
26
|
+
# @param [Hash] location_map hash with location_code as key and location hash as value
|
27
|
+
# @return [Array<String>] Array of specific locations retrieved from location_map
|
28
|
+
def specific_location(record, location_map)
|
29
|
+
location(record: record, location_map: location_map, display_value: 'specific_location')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Base method to retrieve location data from enriched marc 'itm' or 'hld' fields, giving priority to the item
|
33
|
+
# 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.
|
35
|
+
# @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/
|
36
|
+
# Alma documentation for these added fields
|
37
|
+
# @param [MARC::Record] record
|
38
|
+
# @param [Hash] location_map hash with location_code as key and location hash as value
|
39
|
+
# @param [Symbol | String] display_value field in location hash to retrieve
|
40
|
+
# @return [Array<String>]
|
41
|
+
def location(record:, location_map:, display_value:)
|
42
|
+
# get enriched marc location tag and subfield code
|
43
|
+
location_tag_and_subfield_code(record) => {tag:, subfield_code:}
|
44
|
+
|
45
|
+
locations = record.fields(tag).flat_map do |field|
|
46
|
+
field.filter_map do |subfield|
|
47
|
+
# skip unless subfield code does not match enriched marc tag subfield code
|
48
|
+
next unless subfield.code == subfield_code
|
49
|
+
|
50
|
+
# skip if subfield value is 'web'
|
51
|
+
# we don't facet for 'web' which is the 'Penn Library Web' location used in Voyager.
|
52
|
+
# this location should eventually go away completely with data cleanup in Alma.
|
53
|
+
next if subfield.value == 'web'
|
54
|
+
|
55
|
+
# skip unless subfield value is a key in location_map
|
56
|
+
# sometimes "happening locations" are mistakenly used in holdings records.
|
57
|
+
# that's a data problem that should be fixed.
|
58
|
+
# here, if we encounter a code we can't map, we ignore it, for faceting purposes
|
59
|
+
next unless location_map.key?(subfield.value.to_sym)
|
60
|
+
|
61
|
+
location_map[subfield.value.to_sym][display_value.to_sym]
|
62
|
+
end.flatten.compact_blank
|
63
|
+
end.uniq
|
64
|
+
locations << 'Online library' if record.fields(PennMARC::EnrichedMarc::TAG_ELECTRONIC_INVENTORY).any?
|
65
|
+
locations
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Determine enriched marc location tag ('itm' or 'hld') and subfield code, giving priority to using 'itm' tag and
|
71
|
+
# subfield.
|
72
|
+
# @param [MARC::Record]
|
73
|
+
# @return [Hash<String, String>] containing location tag and subfield code
|
74
|
+
# - `:tag` (String): The enriched marc location tag
|
75
|
+
# - `:subfield_code` (String): The relevant subfield code
|
76
|
+
def location_tag_and_subfield_code(record)
|
77
|
+
# in holdings records, the shelving location is always the permanent location.
|
78
|
+
# in item records, the current location takes into account
|
79
|
+
# temporary locations and permanent locations. if you update the item's perm location,
|
80
|
+
# the holding's shelving location changes.
|
81
|
+
#
|
82
|
+
# Since item records may reflect locations more accurately, we use them if they exist;
|
83
|
+
# if not, we use the holdings.
|
84
|
+
|
85
|
+
tag = PennMARC::EnrichedMarc::TAG_HOLDING
|
86
|
+
subfield_code = PennMARC::EnrichedMarc::SUB_HOLDING_SHELVING_LOCATION
|
87
|
+
|
88
|
+
if record.fields(PennMARC::EnrichedMarc::TAG_ITEM).any?
|
89
|
+
tag = PennMARC::EnrichedMarc::TAG_ITEM
|
90
|
+
subfield_code = PennMARC::EnrichedMarc::SUB_ITEM_CURRENT_LOCATION
|
91
|
+
end
|
92
|
+
|
93
|
+
{ tag: tag, subfield_code: subfield_code }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PennMARC
|
4
|
+
# Extracts notes from {https://www.oclc.org/bibformats/en/5xx.html 5xx} fields (mostly).
|
5
|
+
class Note < Helper
|
6
|
+
class << self
|
7
|
+
# Retrieve notes for display from fields {https://www.oclc.org/bibformats/en/5xx/500.html 500},
|
8
|
+
# {https://www.oclc.org/bibformats/en/5xx/502.html 502}, {https://www.oclc.org/bibformats/en/5xx/504.html 504},
|
9
|
+
# {https://www.oclc.org/bibformats/en/5xx/515.html 515}, {https://www.oclc.org/bibformats/en/5xx/518.html 518}
|
10
|
+
# {https://www.oclc.org/bibformats/en/5xx/525.html 525}, {https://www.oclc.org/bibformats/en/5xx/533.html 533},
|
11
|
+
# {https://www.oclc.org/bibformats/en/5xx/550.html 550}, {https://www.oclc.org/bibformats/en/5xx/580.html 580},
|
12
|
+
# {https://www.oclc.org/bibformats/en/5xx/586.html 586}, {https://www.oclc.org/bibformats/en/5xx/588.html 588},
|
13
|
+
# and their linked alternates.
|
14
|
+
# @todo legacy implementation used conditional to separate join logic for 588 field. However, this doesn't seem
|
15
|
+
# necessary because 588 only has subfields 'a', '5', '6', and '8'. Do we need to look into this?
|
16
|
+
# @param [MARC::Record] record
|
17
|
+
# @return [Array<String>]
|
18
|
+
def notes_show(record)
|
19
|
+
notes_fields = %w[500 502 504 515 518 525 533 550 580 586 588]
|
20
|
+
record.fields(notes_fields + ['880']).filter_map do |field|
|
21
|
+
next if field.tag == '880' && subfield_value_not_in?(field, '6', notes_fields)
|
22
|
+
|
23
|
+
join_subfields(field, &subfield_not_in?(%w[5 6 8]))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Retrieve local notes for display from fields {https://www.oclc.org/bibformats/en/5xx/561.html 561},
|
28
|
+
# {https://www.oclc.org/bibformats/en/5xx/562.html 562}, {https://www.oclc.org/bibformats/en/5xx/563.html 563},
|
29
|
+
# {https://www.oclc.org/bibformats/en/5xx/585.html 585}, {https://www.oclc.org/bibformats/en/5xx/590.html 590}.
|
30
|
+
# Includes linked alternates except for 561.
|
31
|
+
# @param [MARC::Record] record
|
32
|
+
# @return [Array<String>]
|
33
|
+
def local_notes_show(record)
|
34
|
+
local_notes = record.fields('561').filter_map do |field|
|
35
|
+
next unless subfield_value?(field, 'a', /^Athenaeum copy: /)
|
36
|
+
|
37
|
+
join_subfields(field, &subfield_in?(%w[a]))
|
38
|
+
end
|
39
|
+
|
40
|
+
additional_fields = %w[562 563 585 590]
|
41
|
+
|
42
|
+
local_notes + record.fields(additional_fields + ['880']).filter_map do |field|
|
43
|
+
next if field.tag == '880' && subfield_value_not_in?(field, '6', additional_fields)
|
44
|
+
|
45
|
+
join_subfields(field, &subfield_not_in?(%w[5 6 8]))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Retrieve provenance notes for display from fields {https://www.oclc.org/bibformats/en/5xx/561.html 561} and
|
50
|
+
# prefixed subject field {https://www.oclc.org/bibformats/en/6xx/650.html 650} and its linked alternate.
|
51
|
+
# Ignores 561 fields with subfield 'a' values that begin with 'Athenaeum copy: ' and 650 fields where subfield 'a'
|
52
|
+
# does not have the prefix 'PRO'.
|
53
|
+
# @param [MARC::Record] record
|
54
|
+
# @return [Array<String>]
|
55
|
+
def provenance_show(record)
|
56
|
+
provenance_notes = record.fields(%w[561 880]).filter_map do |field|
|
57
|
+
next unless field.indicator1.in?(['1', '', ' '])
|
58
|
+
|
59
|
+
next unless field.indicator2.in?([' ', ''])
|
60
|
+
|
61
|
+
next if field.tag == '880' && subfield_value_not_in?(field, '6', %w[561])
|
62
|
+
|
63
|
+
next if subfield_value?(field, 'a', /^Athenaeum copy: /)
|
64
|
+
|
65
|
+
join_subfields(field, &subfield_in?(%w[a]))
|
66
|
+
end
|
67
|
+
provenance_notes + prefixed_subject_and_alternate(record, 'PRO')
|
68
|
+
end
|
69
|
+
|
70
|
+
# Retrieve contents notes for display from fields {https://www.oclc.org/bibformats/en/5xx/505.html 505} and
|
71
|
+
# its linked alternate.
|
72
|
+
# @param [MARC::Record] record
|
73
|
+
# @return [Array<String>]
|
74
|
+
def contents_show(record)
|
75
|
+
record.fields(%w[505 880]).filter_map do |field|
|
76
|
+
next if field.tag == '880' && subfield_value_not_in?(field, '6', %w[505])
|
77
|
+
|
78
|
+
join_subfields(field, &subfield_not_in?(%w[6 8])).split('--')
|
79
|
+
end.flatten
|
80
|
+
end
|
81
|
+
|
82
|
+
# Retrieve access restricted notes for display from field {https://www.oclc.org/bibformats/en/5xx/506.html 506}.
|
83
|
+
# @param [MARC::Record] record
|
84
|
+
# @return [Array<String>]
|
85
|
+
def access_restriction_show(record)
|
86
|
+
record.fields('506').filter_map do |field|
|
87
|
+
join_subfields(field, &subfield_not_in?(%w[5 6]))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Retrieve finding aid notes for display from field {https://www.oclc.org/bibformats/en/5xx/555.html 555} and its
|
92
|
+
# linked alternate.
|
93
|
+
# @param [MARC::Record] record
|
94
|
+
# @return [Array<String>]
|
95
|
+
def finding_aid_show(record)
|
96
|
+
datafield_and_linked_alternate(record, '555')
|
97
|
+
end
|
98
|
+
|
99
|
+
# Retrieve participant notes for display from field {https://www.oclc.org/bibformats/en/5xx/511.html 511} and its
|
100
|
+
# linked alternate.
|
101
|
+
# @param [MARC::Record] record
|
102
|
+
# @return [Array<String>]
|
103
|
+
def participant_show(record)
|
104
|
+
datafield_and_linked_alternate(record, '511')
|
105
|
+
end
|
106
|
+
|
107
|
+
# Retrieve credits notes for display from field {https://www.oclc.org/bibformats/en/5xx/508.html 508} and its
|
108
|
+
# linked alternate.
|
109
|
+
# @param [MARC::Record] record
|
110
|
+
# @return [Array<String>]
|
111
|
+
def credits_show(record)
|
112
|
+
datafield_and_linked_alternate(record, '508')
|
113
|
+
end
|
114
|
+
|
115
|
+
# Retrieve biography notes for display from field {https://www.oclc.org/bibformats/en/5xx/545.html 545} and its
|
116
|
+
# linked alternate.
|
117
|
+
# @param [MARC::Record] record
|
118
|
+
# @return [Array<String>]
|
119
|
+
def biography_show(record)
|
120
|
+
datafield_and_linked_alternate(record, '545')
|
121
|
+
end
|
122
|
+
|
123
|
+
# Retrieve summary notes for display from field {https://www.oclc.org/bibformats/en/5xx/520.html 520} and its
|
124
|
+
# linked alternate.
|
125
|
+
# @param [MARC::Record] record
|
126
|
+
# @return [Array<String>]
|
127
|
+
def summary_show(record)
|
128
|
+
datafield_and_linked_alternate(record, '520')
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PennMARC
|
4
|
+
# Extracts data related to a resource's production, distribution, manufacture, and publication.
|
5
|
+
class Production < Helper
|
6
|
+
class << self
|
7
|
+
# Retrieve production values for display from {https://www.oclc.org/bibformats/en/2xx/264.html 264 field}.
|
8
|
+
# @param [MARC::Record] record
|
9
|
+
# @return [Array<String>]
|
10
|
+
def show(record)
|
11
|
+
get_264_or_880_fields(record, '0')
|
12
|
+
end
|
13
|
+
|
14
|
+
# Retrieve distribution values for display from {https://www.oclc.org/bibformats/en/2xx/264.html 264 field}.
|
15
|
+
# @param [MARC::Record] record
|
16
|
+
# @return [Array<String>]
|
17
|
+
def distribution_show(record)
|
18
|
+
get_264_or_880_fields(record, '2')
|
19
|
+
end
|
20
|
+
|
21
|
+
# Retrieve manufacture values for display from {https://www.oclc.org/bibformats/en/2xx/264.html 264 field}.
|
22
|
+
# @param [MARC::Record] record
|
23
|
+
# @return [Array<String>]
|
24
|
+
def manufacture_show(record)
|
25
|
+
get_264_or_880_fields(record, '3')
|
26
|
+
end
|
27
|
+
|
28
|
+
# Retrieve publication values. Return publication values from
|
29
|
+
# {https://www.oclc.org/bibformats/en/2xx/264.html 264 field} only if none found
|
30
|
+
# {https://www.oclc.org/bibformats/en/2xx/260.html 260}-262 fields.
|
31
|
+
# @param [MARC::Record] record
|
32
|
+
# @return [Array<String>]
|
33
|
+
def publication_values(record)
|
34
|
+
# first get inclusive dates
|
35
|
+
values = record.fields('245').first(1).flat_map { |field| subfield_values(field, 'f') }
|
36
|
+
added_2xx = record.fields(%w[260 261 262])
|
37
|
+
.first(1)
|
38
|
+
.map do |field|
|
39
|
+
join_subfields(field, &subfield_not_in?(['6'])).squish
|
40
|
+
end
|
41
|
+
|
42
|
+
if added_2xx.present?
|
43
|
+
values += added_2xx
|
44
|
+
else
|
45
|
+
fields264 = record.fields('264')
|
46
|
+
|
47
|
+
pub_place_name = fields264
|
48
|
+
.find(-> { [] }) { |field| field.indicator2 == '1' }
|
49
|
+
.filter_map { |sf| sf.value if sf.code.in?(%w[a b]) }
|
50
|
+
|
51
|
+
pub_date = fields264
|
52
|
+
.find(-> { [] }) { |field| field.indicator2 == '1' }
|
53
|
+
.filter_map { |sf| sf.value if sf.code.in?(['c']) }
|
54
|
+
|
55
|
+
copyright_date = fields264
|
56
|
+
.find(-> { [] }) { |field| field.indicator2 == '4' }
|
57
|
+
.filter_map { |sf| "#{pub_date.present? ? ', ' : ''}#{sf.value}" if sf.code.in?(['c']) }
|
58
|
+
|
59
|
+
joined264 = Array.wrap((pub_place_name + pub_date + copyright_date).join(' '))
|
60
|
+
|
61
|
+
values += joined264
|
62
|
+
end
|
63
|
+
values.filter_map { |value| value&.strip }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Retrieve publication values for display from fields
|
67
|
+
# {https://www.oclc.org/bibformats/en/2xx/245.html 245},
|
68
|
+
# {https://www.oclc.org/bibformats/en/2xx/260.html 260}-262 and their linked alternates,
|
69
|
+
# and {https://www.oclc.org/bibformats/en/2xx/264.html 264} and its linked alternate.
|
70
|
+
# @param [MARC::Record] record
|
71
|
+
# @return [Object]
|
72
|
+
def publication_show(record)
|
73
|
+
values = record.fields('245').first(1).flat_map { |field| subfield_values(field, 'f') }
|
74
|
+
|
75
|
+
values += record.fields(%w[260 261 262]).first(1).map do |field|
|
76
|
+
join_subfields(field, &subfield_not_in?(%w[6 8]))
|
77
|
+
end
|
78
|
+
|
79
|
+
values += record.fields('880').filter_map { |field| field if subfield_value?(field, '6', /^(260|261|262)/) }
|
80
|
+
.first(1).map { |field| join_subfields(field, &subfield_not_in?(%w[6 8])) }
|
81
|
+
|
82
|
+
values += record.fields('880').filter_map do |field|
|
83
|
+
next unless subfield_value?(field, '6', /^245/)
|
84
|
+
|
85
|
+
join_subfields(field, &subfield_in?(['f']))
|
86
|
+
end
|
87
|
+
|
88
|
+
values += get_264_or_880_fields(record, '1')
|
89
|
+
values.compact_blank
|
90
|
+
end
|
91
|
+
|
92
|
+
# Retrieve place of publication for display from {https://www.oclc.org/bibformats/en/7xx/752.html 752 field} and
|
93
|
+
# its linked alternate.
|
94
|
+
# @note legacy version returns array of hash objects including data for display link
|
95
|
+
# @param [MARC::Record] record
|
96
|
+
# @return [Object]
|
97
|
+
def place_of_publication_show(record)
|
98
|
+
record.fields(%w[752 880]).filter_map do |field|
|
99
|
+
next if field.tag == '880' && subfield_values(field, '6').exclude?('752')
|
100
|
+
|
101
|
+
place = join_subfields(field, &subfield_not_in?(%w[6 8 e w]))
|
102
|
+
place_extra = join_subfields(field, &subfield_in?(%w[e w]))
|
103
|
+
"#{place} #{place_extra}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# base method to retrieve production values from {https://www.oclc.org/bibformats/en/2xx/264.html 264 field} based
|
110
|
+
# on indicator2.
|
111
|
+
# distribution and manufacture share the same logic except for indicator2
|
112
|
+
# @param [MARC::Record] record
|
113
|
+
# @param [String] indicator2
|
114
|
+
# @return [Array<String>]
|
115
|
+
def get_264_or_880_fields(record, indicator2)
|
116
|
+
values = record.fields('264').filter_map do |field|
|
117
|
+
next unless field.indicator2 == indicator2
|
118
|
+
|
119
|
+
join_subfields(field, &subfield_in?(%w[a b c]))
|
120
|
+
end
|
121
|
+
values + record.fields('880').filter_map do |field|
|
122
|
+
next unless field.indicator2 == indicator2
|
123
|
+
|
124
|
+
next unless subfield_value?(field, '6', /^264/)
|
125
|
+
|
126
|
+
join_subfields(field, &subfield_in?(%w[a b c]))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PennMARC
|
4
|
+
# These MARC parsing method are grouped in virtue of their role as describing the relationship of a record to other
|
5
|
+
# records.
|
6
|
+
class Relation < Helper
|
7
|
+
class << self
|
8
|
+
CHRONOLOGY_PREFIX = 'CHR'
|
9
|
+
|
10
|
+
RELATED_WORK_FIELDS = %w[700 710 711 730].freeze
|
11
|
+
CONTAINS_FIELDS = %w[700 710 711 730 740].freeze
|
12
|
+
|
13
|
+
# Get values for "{https://www.oclc.org/bibformats/en/7xx/773.html Host Item}" for this record. Values contained
|
14
|
+
# in this field should be sufficient to locate host item record.
|
15
|
+
#
|
16
|
+
# @param [MARC::Record] record
|
17
|
+
# @return [Array] contained in values for display
|
18
|
+
def contained_in_show(record)
|
19
|
+
record.fields('773').map do |field|
|
20
|
+
join_subfields(field, &subfield_not_in?(%w[6 7 8 w]))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get "chronology" information from specially-prefixed 650 (subject) fields
|
25
|
+
# @todo why do we stuff chronology data in a 650 field?
|
26
|
+
# @param [MARC::Record] record
|
27
|
+
# @return [Array] array of chronology values
|
28
|
+
def chronology_show(record)
|
29
|
+
prefixed_subject_and_alternate(record, CHRONOLOGY_PREFIX)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get notes for Related Collections from {https://www.oclc.org/bibformats/en/5xx/544.html MARC 544}.
|
33
|
+
# @param [MARC::Record] record
|
34
|
+
# @return [Array]
|
35
|
+
def related_collections_show(record)
|
36
|
+
datafield_and_linked_alternate(record, '544')
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get notes for "Publication About" from {https://www.oclc.org/bibformats/en/5xx/581.html MARC 581}.
|
40
|
+
# @param [MARC::Record] record
|
41
|
+
# @return [Array]
|
42
|
+
def publications_about_show(record)
|
43
|
+
datafield_and_linked_alternate(record, '581')
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get related work from {RELATED_WORK_FIELDS} in the 7XX range. Require presence of sf t (title) and absence of
|
47
|
+
# an indicator2 value. Prefix returned values with sf i value. Also map relator codes found in sf 4. Ignore sf 0.
|
48
|
+
# @param [MARC::Record] record
|
49
|
+
# @param [Hash] relator_map
|
50
|
+
# @return [Array]
|
51
|
+
def related_work_show(record, relator_map)
|
52
|
+
values = record.fields(RELATED_WORK_FIELDS).filter_map do |field|
|
53
|
+
next unless field.indicator2.blank?
|
54
|
+
|
55
|
+
next unless subfield_defined?(field, 't')
|
56
|
+
|
57
|
+
values_with_title_prefix(field, sf_exclude: %w[0 4 6 8 i], relator_map: relator_map)
|
58
|
+
end
|
59
|
+
values + record.fields('880').filter_map do |field|
|
60
|
+
next unless field.indicator2.blank?
|
61
|
+
|
62
|
+
next unless subfield_value?(field, '6', /^(#{RELATED_WORK_FIELDS.join('|')})/)
|
63
|
+
|
64
|
+
next unless subfield_defined?(field, 't')
|
65
|
+
|
66
|
+
values_with_title_prefix(field, sf_exclude: %w[0 4 6 8 i], relator_map: relator_map)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Get "Contains" values from {CONTAINS_FIELDS} in the 7XX range. Must have indicator 2 value of 2 indicating an
|
71
|
+
# "Analytical Entry" meaning that the record is contained by the matching field. Map relator codes in sf 4. Ignore
|
72
|
+
# values in sf 0, 5, 6, and 8.
|
73
|
+
# @param [MARC::Record] record
|
74
|
+
# @param [Hash] relator_map
|
75
|
+
# @return [Array]
|
76
|
+
def contains_show(record, relator_map)
|
77
|
+
acc = record.fields(CONTAINS_FIELDS).filter_map do |field|
|
78
|
+
next unless field.indicator2 == '2'
|
79
|
+
|
80
|
+
values_with_title_prefix(field, sf_exclude: %w[0 4 5 6 8 i], relator_map: relator_map)
|
81
|
+
end
|
82
|
+
acc + record.fields('880').filter_map do |field|
|
83
|
+
next unless field.indicator2 == '2'
|
84
|
+
|
85
|
+
next unless subfield_value?(field, '6', /^(#{CONTAINS_FIELDS.join('|')})/)
|
86
|
+
|
87
|
+
values_with_title_prefix(field, sf_include: %w[0 5 6 8 i])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get "Constituent Unit" values from {https://www.oclc.org/bibformats/en/7xx/774.html MARC 774}. Include
|
92
|
+
# subfield values in i, a, s and t.
|
93
|
+
# @param [MARC::Record] record
|
94
|
+
# @return [Array]
|
95
|
+
def constituent_unit_show(record)
|
96
|
+
acc = record.fields('774').filter_map do |field|
|
97
|
+
join_subfields(field, &subfield_in?(%w[i a s t]))
|
98
|
+
end
|
99
|
+
acc + linked_alternate(record, '774', &subfield_in?(%w[i a s t]))
|
100
|
+
end
|
101
|
+
|
102
|
+
# Get "Has Supplement" values from {https://www.oclc.org/bibformats/en/7xx/770.html MARC 770}. Ignore
|
103
|
+
# subfield values in 6 and 8.
|
104
|
+
# @param [MARC::Record] record
|
105
|
+
# @return [Array]
|
106
|
+
def has_supplement_show(record)
|
107
|
+
datafield_and_linked_alternate(record, '770')
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# Handle common behavior when a relator field references a title in subfield i
|
113
|
+
# @param [MARC::DataField] field
|
114
|
+
# @param [Array, nil] sf_include subfields to include, optional
|
115
|
+
# @param [Array, nil] sf_exclude subfields to exclude, optional
|
116
|
+
# @param [Hash, nil] relator_map map relator in sf4 using this map, optional
|
117
|
+
# @return [String] extracted and processed values from field
|
118
|
+
def values_with_title_prefix(field, sf_include: nil, sf_exclude: nil, relator_map: nil)
|
119
|
+
raise ArgumentError('Define only sf_include or sf_exclude.') if sf_include.present? && sf_exclude.present?
|
120
|
+
|
121
|
+
subi = remove_paren_value_from_subfield_i(field) || ''
|
122
|
+
relator = translate_relator(subfield_values(field, '4').first, relator_map) if relator_map.present?
|
123
|
+
contains = if sf_include.present?
|
124
|
+
join_subfields(field, &subfield_not_in?(sf_include))
|
125
|
+
elsif sf_exclude.present?
|
126
|
+
join_subfields(field, &subfield_not_in?(sf_exclude))
|
127
|
+
end
|
128
|
+
[
|
129
|
+
subi,
|
130
|
+
[contains, relator].compact_blank.join(', ')
|
131
|
+
].compact_blank.join(': ')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PennMARC
|
4
|
+
# Do Series-y stuff
|
5
|
+
class Series < Helper
|
6
|
+
class << self
|
7
|
+
def show(record)
|
8
|
+
acc = []
|
9
|
+
|
10
|
+
tags_present = series_tags.select { |tag| record[tag].present? }
|
11
|
+
|
12
|
+
if %w[800 810 811 400 410 411].member?(tags_present.first)
|
13
|
+
record.fields(tags_present.first).each do |field|
|
14
|
+
# added 2017/04/10: filter out 0 (authority record numbers) added by Alma
|
15
|
+
series = join_subfields(field, &subfield_not_in(%w[0 5 6 8 e t w v n]))
|
16
|
+
pairs = field.map do |sf|
|
17
|
+
if %w[e w v n t].member?(sf.code)
|
18
|
+
[' ', sf.value]
|
19
|
+
elsif sf.code == '4'
|
20
|
+
[', ', relator_codes[sf.value]]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
series_append = pairs.flatten.join.strip
|
24
|
+
acc << { value: series, value_append: series_append, link_type: 'author_search' }
|
25
|
+
end
|
26
|
+
elsif %w[830 440 490].member?(tags_present.first)
|
27
|
+
record.fields(tags_present.first).each do |field|
|
28
|
+
# added 2017/04/10: filter out 0 (authority record numbers) added by Alma
|
29
|
+
series = join_subfields(field, &subfield_not_in(%w[0 5 6 8 c e w v n]))
|
30
|
+
series_append = join_subfields(field, &subfield_in(%w[c e w v n]))
|
31
|
+
acc << { value: series, value_append: series_append, link_type: 'title_search' }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
record.fields(tags_present.drop(1)).each do |field|
|
36
|
+
# added 2017/04/10: filter out 0 (authority record numbers) added by Alma
|
37
|
+
series = join_subfields(field, &subfield_not_in(%w[0 5 6 8]))
|
38
|
+
acc << { value: series, link: false }
|
39
|
+
end
|
40
|
+
|
41
|
+
record.fields('880')
|
42
|
+
.select { |f| has_subfield6_value(f, /^(800|810|811|830|400|410|411|440|490)/) }
|
43
|
+
.each do |field|
|
44
|
+
series = join_subfields(field, &subfield_not_in(%w[5 6 8]))
|
45
|
+
acc << { value: series, link: false }
|
46
|
+
end
|
47
|
+
|
48
|
+
acc
|
49
|
+
end
|
50
|
+
|
51
|
+
def values(record)
|
52
|
+
acc = []
|
53
|
+
added_8xx = false
|
54
|
+
record.fields(%w[800 810 811 830]).take(1).each do |field|
|
55
|
+
acc << get_series_8xx_field(field)
|
56
|
+
added_8xx = true
|
57
|
+
end
|
58
|
+
unless added_8xx
|
59
|
+
record.fields(%w[400 410 411 440 490]).take(1).map do |field|
|
60
|
+
acc << get_series_4xx_field(field)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
acc
|
64
|
+
end
|
65
|
+
|
66
|
+
def search(record)
|
67
|
+
acc += record.fields(%w[400 410 411])
|
68
|
+
.select { |f| f.indicator2 == '0' }
|
69
|
+
.map do |field|
|
70
|
+
join_subfields(field, &subfield_not_in(%w[4 6 8]))
|
71
|
+
end
|
72
|
+
acc += record.fields(%w[400 410 411])
|
73
|
+
.select { |f| f.indicator2 == '1' }
|
74
|
+
.map do |field|
|
75
|
+
join_subfields(field, &subfield_not_in(%w[4 6 8 a]))
|
76
|
+
end
|
77
|
+
acc += record.fields(%w[440])
|
78
|
+
.map do |field|
|
79
|
+
join_subfields(field, &subfield_not_in(%w[0 5 6 8 w]))
|
80
|
+
end
|
81
|
+
acc += record.fields(%w[800 810 811])
|
82
|
+
.map do |field|
|
83
|
+
join_subfields(field, &subfield_not_in(%w[0 4 5 6 7 8 w]))
|
84
|
+
end
|
85
|
+
acc += record do |field|
|
86
|
+
join_subfields(field, &subfield_not_in(%w[0 5 6 7 8 w]))
|
87
|
+
end
|
88
|
+
acc + record.fields(%w[533])
|
89
|
+
.map do |field|
|
90
|
+
field.find_all { |sf| sf.code == 'f' }
|
91
|
+
.map(&:value)
|
92
|
+
.map { |v| v.gsub(/\(|\)/, '') }
|
93
|
+
.join(' ')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_continues_display(record)
|
98
|
+
get_continues(record, '780')
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_continued_by_display(record)
|
102
|
+
get_continues(record, '785')
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# logic for 'Continues' and 'Continued By' is very similar
|
108
|
+
def get_continues(record, tag)
|
109
|
+
record.fields
|
110
|
+
.select { |f| f.tag == tag || (f.tag == '880' && has_subfield6_value(f, /^#{tag}/)) }
|
111
|
+
.select { |f| f.any?(&subfield_in(%w[i a s t n d])) }
|
112
|
+
.map do |field|
|
113
|
+
join_subfields(field, &subfield_in(%w[i a s t n d]))
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|