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