pennmarc 1.0.12 → 1.0.15.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +7 -12
- data/.rubocop_todo.yml +2 -2
- data/lib/pennmarc/enriched.rb +93 -0
- data/lib/pennmarc/helpers/access.rb +2 -2
- data/lib/pennmarc/helpers/citation.rb +4 -4
- data/lib/pennmarc/helpers/classification.rb +8 -8
- data/lib/pennmarc/helpers/creator.rb +70 -73
- data/lib/pennmarc/helpers/database.rb +6 -6
- data/lib/pennmarc/helpers/date.rb +3 -3
- data/lib/pennmarc/helpers/edition.rb +4 -2
- data/lib/pennmarc/helpers/format.rb +15 -14
- data/lib/pennmarc/helpers/helper.rb +1 -1
- data/lib/pennmarc/helpers/identifier.rb +16 -14
- data/lib/pennmarc/helpers/inventory.rb +92 -0
- data/lib/pennmarc/helpers/inventory_entry/base.rb +23 -0
- data/lib/pennmarc/helpers/inventory_entry/electronic.rb +20 -0
- data/lib/pennmarc/helpers/inventory_entry/physical.rb +38 -0
- data/lib/pennmarc/helpers/language.rb +3 -2
- data/lib/pennmarc/helpers/location.rb +19 -14
- data/lib/pennmarc/helpers/note.rb +10 -8
- data/lib/pennmarc/helpers/production.rb +9 -9
- data/lib/pennmarc/helpers/relation.rb +12 -9
- data/lib/pennmarc/helpers/series.rb +10 -8
- data/lib/pennmarc/helpers/subject.rb +12 -12
- data/lib/pennmarc/helpers/title.rb +20 -16
- data/lib/pennmarc/mappings/locations.yml +4 -0
- data/lib/pennmarc/util.rb +19 -4
- data/lib/pennmarc/version.rb +1 -1
- data/spec/lib/pennmarc/helpers/access_spec.rb +5 -5
- data/spec/lib/pennmarc/helpers/classification_spec.rb +6 -6
- data/spec/lib/pennmarc/helpers/creator_spec.rb +41 -7
- data/spec/lib/pennmarc/helpers/format_spec.rb +4 -4
- data/spec/lib/pennmarc/helpers/inventory_spec.rb +129 -0
- data/spec/lib/pennmarc/helpers/location_spec.rb +40 -9
- data/spec/lib/pennmarc/helpers/subject_spec.rb +37 -13
- metadata +10 -5
- data/lib/pennmarc/enriched_marc.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd846248b3c6fcde1cfd8a74312d84f46ce28f534effaff22ec112a5456afd2f
|
4
|
+
data.tar.gz: 7e5789d9ef2594767e438a5b1fd2978b1508afad975785059b3520d795fda15b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82a79402e68c4b48f3d38fa165042472aab3a8d811797145811af8a873d177ceb9020a2e71c9853f5c792a37d0533d7a87c7ec94506507c817e3dc09db345dcf
|
7
|
+
data.tar.gz: 87196088380e9b7f6750511434edcc49467ca9ee47e9577cb3f42ce75e595a2f7cb62605ede37d1a4b17b59876dec437a0beee131ee97eb19f1c411751c849cc
|
data/.gitlab-ci.yml
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
include:
|
2
|
+
- project: "devops/gitlab/ci-templates/general"
|
3
|
+
file:
|
4
|
+
- ".install_hashicorp_vault.yml"
|
5
|
+
- ".vault_jwt_auth.yml"
|
2
6
|
- project: "devops/gitlab/ci-templates/ruby"
|
3
7
|
ref: "sans-dind"
|
4
8
|
file:
|
@@ -37,19 +41,10 @@ gem_publication:
|
|
37
41
|
image: ruby:3.2.2
|
38
42
|
variables:
|
39
43
|
GEMSPEC_FILE: "${CI_PROJECT_NAME}.gemspec"
|
40
|
-
VAULT_VERSION: "1.
|
44
|
+
VAULT_VERSION: "1.15.5"
|
41
45
|
before_script:
|
42
|
-
-
|
43
|
-
|
44
|
-
wget \
|
45
|
-
unzip && \
|
46
|
-
wget https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip -O /tmp/vault_${VAULT_VERSION}.zip && \
|
47
|
-
unzip /tmp/vault_${VAULT_VERSION}.zip -d /usr/bin && \
|
48
|
-
rm -fr /tmp/vault_${VAULT_VERSION}.zip && \
|
49
|
-
apt-get remove -y wget unzip && \
|
50
|
-
apt-get clean
|
51
|
-
- export VAULT_ADDR=${VAULT_URL}
|
52
|
-
- export VAULT_TOKEN="$(vault write -field=token auth/jwt/${CI_SERVER_HOST}/login role=${JWT_ROLE} jwt=${CI_JOB_JWT})"
|
46
|
+
- !reference [.install_hashicorp_vault, before_script]
|
47
|
+
- !reference [.vault_jwt_auth, before_script]
|
53
48
|
- export GEM_HOST_API_KEY="$(vault kv get -field=rubygems_api_key ${VAULT_KV_ENDPOINT}${ENVIRONMENT})"
|
54
49
|
- |
|
55
50
|
gem -v
|
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-
|
3
|
+
# on 2024-01-17 17:00:02 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
|
@@ -60,7 +60,7 @@ Metrics/CyclomaticComplexity:
|
|
60
60
|
- 'lib/pennmarc/helpers/title.rb'
|
61
61
|
- 'lib/pennmarc/util.rb'
|
62
62
|
|
63
|
-
# Offense count:
|
63
|
+
# Offense count: 26
|
64
64
|
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
|
65
65
|
Metrics/MethodLength:
|
66
66
|
Exclude:
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Constants for Alma's MARC enrichment, performed and included in the MARCXML either by the Publishing process or by
|
4
|
+
# API service
|
5
|
+
module PennMARC
|
6
|
+
module Enriched
|
7
|
+
# Enriched MARC fields added by configurable setting in the Publishing profile that generates the MARCXML
|
8
|
+
# TODO: review if we can/should modify the subfields used in the pub profile to create parity with API subfields as
|
9
|
+
# that could simplify this mapping tremendously
|
10
|
+
module Pub
|
11
|
+
# Enrichment Tag Names
|
12
|
+
PHYS_INVENTORY_TAG = 'hld'
|
13
|
+
ELEC_INVENTORY_TAG = 'prt'
|
14
|
+
ITEM_TAG = 'itm'
|
15
|
+
|
16
|
+
# Subfields for HLD tags
|
17
|
+
# Follow MARC 852 spec: https://www.loc.gov/marc/holdings/hd852.html, but names are translated into Alma parlance
|
18
|
+
PHYS_LOCATION_NAME = 'b' # e.g., Libra
|
19
|
+
PHYS_LOCATION_CODE = 'c' # e.g., stor
|
20
|
+
HOLDING_CLASSIFICATION_PART = 'h' # "classification part" first part of call num e.g., KF6450
|
21
|
+
HOLDING_ITEM_PART = 'i' # "item part?" second part of call num e.g., .C59 1989
|
22
|
+
PHYS_PUBLIC_NOTE = 'z'
|
23
|
+
PHYS_INTERNAL_NOTE = 'x'
|
24
|
+
PHYS_HOLDING_ID = '8'
|
25
|
+
|
26
|
+
# Subfields for ITM tags
|
27
|
+
ITEM_CURRENT_LOCATION = 'g'
|
28
|
+
ITEM_CALL_NUMBER_TYPE = 'h'
|
29
|
+
ITEM_CALL_NUMBER = 'i'
|
30
|
+
ITEM_DATE_CREATED = 'q'
|
31
|
+
|
32
|
+
# Subfields for PRT tags
|
33
|
+
ELEC_PORTFOLIO_ID = 'a'
|
34
|
+
ELEC_SERVICE_URL = 'b'
|
35
|
+
ELEC_COLLECTION_NAME = 'c'
|
36
|
+
ELEC_INTERFACE_NAME = 'e'
|
37
|
+
ELEC_PUBLIC_NOTE = 'f'
|
38
|
+
ELEC_COVERAGE_STMT = 'g'
|
39
|
+
|
40
|
+
# other values that could be added if we configured the Alma pub profile (and values are set on the record)
|
41
|
+
# - Authentication note
|
42
|
+
# - "static URL"
|
43
|
+
# - Electronic material type
|
44
|
+
# - Collection ID
|
45
|
+
# - create/update/activation date
|
46
|
+
# - license code
|
47
|
+
# - portfolio coverage info
|
48
|
+
# - from year, until year (month, day volume issue)
|
49
|
+
# - portfolio embargo info
|
50
|
+
# - years/months embargo'd
|
51
|
+
|
52
|
+
# TODO: evaluate this in context of changed boundwiths processing
|
53
|
+
# Franklin legacy note:
|
54
|
+
# a subfield code NOT used by the MARC 21 spec for 852 holdings records.
|
55
|
+
# we add this subfield during preprocessing to store boundwith record IDs.
|
56
|
+
# BOUND_WITH_ID = 'y'
|
57
|
+
end
|
58
|
+
|
59
|
+
# MARC enrichment originating from Alma API
|
60
|
+
# @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/ Alma docs
|
61
|
+
# We cannot modify these subfield settings
|
62
|
+
module Api
|
63
|
+
# Enrichment Tag Names
|
64
|
+
PHYS_INVENTORY_TAG = 'AVA'
|
65
|
+
ELEC_INVENTORY_TAG = 'AVE'
|
66
|
+
|
67
|
+
# Physical Holding (AVA) subfields
|
68
|
+
PHYS_CALL_NUMBER = 'd'
|
69
|
+
PHYS_CALL_NUMBER_TYPE = 'k'
|
70
|
+
PHYS_LIBRARY_CODE = 'b'
|
71
|
+
PHYS_LIBRARY_NAME = 'q'
|
72
|
+
PHYS_LOCATION_CODE = 'j'
|
73
|
+
PHYS_LOCATION_NAME = 'c'
|
74
|
+
PHYS_HOLDING_ID = '8'
|
75
|
+
PHYS_AVAILABILITY = 'e'
|
76
|
+
PHYS_TOTAL_ITEMS = 'f'
|
77
|
+
PHYS_UNAVAILABLE_ITEMS = 'g'
|
78
|
+
PHYS_SUMMARY_INFO = 'v'
|
79
|
+
PHYS_PRIORITY = 'p'
|
80
|
+
|
81
|
+
# Electronic Portfolio (AVE) subfields
|
82
|
+
ELEC_LIBRARY_CODE = 'l'
|
83
|
+
ELEC_COLLECTION_NAME = 'm'
|
84
|
+
ELEC_PUBLIC_NOTE = 'n'
|
85
|
+
ELEC_SERVICE_URL = 'u'
|
86
|
+
ELEC_COVERAGE_STMT = 's'
|
87
|
+
ELEC_INTERFACE_NAME = 't'
|
88
|
+
ELEC_PORTFOLIO_ID = '8'
|
89
|
+
ELEC_COLLECTION_ID = 'c'
|
90
|
+
ELEC_ACTIVATION_STATUS = 'e'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -33,14 +33,14 @@ module PennMARC
|
|
33
33
|
# @param [MARC::Field] field
|
34
34
|
# @return [Boolean]
|
35
35
|
def electronic_holding_tag?(field)
|
36
|
-
field.tag.in? [
|
36
|
+
field.tag.in? [Enriched::Pub::ELEC_INVENTORY_TAG, Enriched::Api::ELEC_INVENTORY_TAG]
|
37
37
|
end
|
38
38
|
|
39
39
|
# Does the record have added physical holding info?
|
40
40
|
# @param [MARC::Field] field
|
41
41
|
# @return [Boolean]
|
42
42
|
def physical_holding_tag?(field)
|
43
|
-
field.tag.in? [
|
43
|
+
field.tag.in? [Enriched::Pub::PHYS_INVENTORY_TAG, Enriched::Api::PHYS_INVENTORY_TAG]
|
44
44
|
end
|
45
45
|
|
46
46
|
# Check if a record contains an 856 entry for an online finding aid, meeting these criteria:
|
@@ -11,9 +11,9 @@ module PennMARC
|
|
11
11
|
# field 520 (Summary, Etc. Note).
|
12
12
|
# https://www.loc.gov/marc/bibliographic/bd510.html
|
13
13
|
# @param [MARC::Record] record
|
14
|
-
# @return [Array] array of citations and any linked alternates
|
14
|
+
# @return [Array<String>] array of citations and any linked alternates
|
15
15
|
def cited_in_show(record)
|
16
|
-
datafield_and_linked_alternate(record, '510')
|
16
|
+
datafield_and_linked_alternate(record, '510').uniq
|
17
17
|
end
|
18
18
|
|
19
19
|
# Field 524 is the Preferred Citation of Described Materials Note. It is the Format for the citation of the
|
@@ -22,9 +22,9 @@ module PennMARC
|
|
22
22
|
# introductory phrase that is generated as a display constant based on the first indicator value.
|
23
23
|
# https://www.loc.gov/marc/bibliographic/bd524.html
|
24
24
|
# @param [MARC::Record] record
|
25
|
-
# @return [Array] array of citation of described materials note and any linked alternates
|
25
|
+
# @return [Array<String>] array of citation of described materials note and any linked alternates
|
26
26
|
def cite_as_show(record)
|
27
|
-
datafield_and_linked_alternate(record, '524')
|
27
|
+
datafield_and_linked_alternate(record, '524').uniq
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -16,19 +16,19 @@ module PennMARC
|
|
16
16
|
}.freeze
|
17
17
|
|
18
18
|
# Enriched MARC tags that hold classification data
|
19
|
-
TAGS = [
|
19
|
+
TAGS = [Enriched::Pub::ITEM_TAG, Enriched::Api::PHYS_INVENTORY_TAG].freeze
|
20
20
|
|
21
21
|
class << self
|
22
22
|
# Parse classification values for faceting. We retrieve classification values from enriched MARC fields 'itm' or
|
23
23
|
# 'AVA' originating respectively from the Alma publishing process or from the Alma Api. We return the
|
24
24
|
# highest level LOC or Dewey classifications from each available call number, joining the class code with
|
25
|
-
# its title in a single string. See {PennMARC::
|
25
|
+
# its title in a single string. See {PennMARC::Enriched} and {PennMARC::Enriched::Api} for more
|
26
26
|
# information on the enriched MARC fields.
|
27
27
|
# @see https://developers.exlibrisgroup.com/alma/apis/docs/bibs/R0VUIC9hbG1hd3MvdjEvYmlicy97bW1zX2lkfQ==/ AVA docs
|
28
28
|
# @param [MARC::Record] record
|
29
29
|
# @return [Array<String>] array of classifications
|
30
30
|
def facet(record)
|
31
|
-
record.fields(TAGS).flat_map
|
31
|
+
record.fields(TAGS).flat_map { |field|
|
32
32
|
call_number_type = subfield_values(field, call_number_type_sf(field))&.first
|
33
33
|
call_numbers = subfield_values(field, call_number_sf(field))
|
34
34
|
|
@@ -39,7 +39,7 @@ module PennMARC
|
|
39
39
|
|
40
40
|
format_facet(class_code, call_number_type, title)
|
41
41
|
end
|
42
|
-
|
42
|
+
}.uniq
|
43
43
|
end
|
44
44
|
|
45
45
|
private
|
@@ -48,18 +48,18 @@ module PennMARC
|
|
48
48
|
# @param [MARC::DataField] field
|
49
49
|
# @return [String]
|
50
50
|
def call_number_sf(field)
|
51
|
-
return
|
51
|
+
return Enriched::Pub::ITEM_CALL_NUMBER if field.tag == Enriched::Pub::ITEM_TAG
|
52
52
|
|
53
|
-
|
53
|
+
Enriched::Api::PHYS_CALL_NUMBER
|
54
54
|
end
|
55
55
|
|
56
56
|
# Retrieve subfield code that stores call number type on enriched marc field
|
57
57
|
# @param [MARC::DataField] field
|
58
58
|
# @return [String]
|
59
59
|
def call_number_type_sf(field)
|
60
|
-
return
|
60
|
+
return Enriched::Pub::ITEM_CALL_NUMBER_TYPE if field.tag == Enriched::Pub::ITEM_TAG
|
61
61
|
|
62
|
-
|
62
|
+
Enriched::Api::PHYS_CALL_NUMBER_TYPE
|
63
63
|
end
|
64
64
|
|
65
65
|
# retrieve title of classification based on single char classification code and call number type
|
@@ -10,82 +10,43 @@ module PennMARC
|
|
10
10
|
class << self
|
11
11
|
# Main tags for Author/Creator information
|
12
12
|
TAGS = %w[100 110].freeze
|
13
|
+
|
13
14
|
# Aux tags for Author/Creator information, for use in search_aux method
|
14
15
|
AUX_TAGS = %w[100 110 111 400 410 411 700 710 711 800 810 811].freeze
|
15
16
|
|
17
|
+
CONFERENCE_SEARCH_TAGS = %w[111 711 811].freeze
|
18
|
+
|
19
|
+
# subfields NOT to join when combining raw subfield values
|
20
|
+
NAME_EXCLUDED_SUBFIELDS = %w[a 1 4 5 6 8 t].freeze
|
21
|
+
|
16
22
|
# Author/Creator search field. Includes all subfield values (even ǂ0 URIs) from
|
17
23
|
# {https://www.oclc.org/bibformats/en/1xx/100.html 100 Main Entry--Personal Name} and
|
18
24
|
# {https://www.oclc.org/bibformats/en/1xx/110.html 110 Main Entry--Corporate Name}. Maps any relator codes found
|
19
25
|
# in ǂ4. To better handle name searches, returns names as both "First Last" and "Last, First" if a comma is found
|
20
|
-
# in ǂa. Also indexes any linked values in the 880.
|
21
|
-
#
|
22
|
-
# @todo
|
23
|
-
# but this should be reexamined in the relevancy-tuning phase. URIs should def be removed. and shouldn't
|
24
|
-
# indicator1 tell us the order of the name?
|
26
|
+
# in ǂa. Also indexes any linked values in the 880.
|
27
|
+
# @todo are we including too many details here and gumming up our index? consider UIRs, relator labels, dates...
|
28
|
+
# @todo shouldn't indicator1 tell us the order of the name? do we not trust the indicator?
|
25
29
|
# @note ported from get_author_creator_1_search_values
|
26
30
|
# @param [MARC::Record] record
|
27
31
|
# @param [Hash] relator_map
|
28
32
|
# @return [Array<String>] array of author/creator values for indexing
|
29
33
|
def search(record, relator_map: Mappers.relator)
|
30
|
-
|
31
|
-
acc = record.fields(TAGS).map do |field|
|
32
|
-
pieces = field.filter_map do |sf|
|
33
|
-
if sf.code == 'a'
|
34
|
-
convert_name_order(sf.value)
|
35
|
-
elsif creator_subfields.exclude?(sf.code)
|
36
|
-
sf.value
|
37
|
-
elsif sf.code == '4'
|
38
|
-
relator = translate_relator(sf.value, relator_map)
|
39
|
-
next if relator.blank?
|
40
|
-
|
41
|
-
relator
|
42
|
-
end
|
43
|
-
end
|
44
|
-
value = join_and_squish(pieces)
|
45
|
-
if value.end_with?('.', '-')
|
46
|
-
value
|
47
|
-
else
|
48
|
-
"#{value}."
|
49
|
-
end
|
50
|
-
end
|
51
|
-
# a second iteration over the same fields produces name entries with the names not reordered
|
52
|
-
secondary_subfields = %w[4 6 8]
|
53
|
-
acc += record.fields(TAGS).map do |field|
|
54
|
-
pieces = field.filter_map do |sf|
|
55
|
-
if secondary_subfields.exclude?(sf.code)
|
56
|
-
sf.value
|
57
|
-
elsif sf.code == '4'
|
58
|
-
relator = translate_relator(sf.value, relator_map)
|
59
|
-
next if relator.blank?
|
60
|
-
|
61
|
-
relator
|
62
|
-
end
|
63
|
-
end
|
64
|
-
value = join_and_squish(pieces)
|
65
|
-
if value.end_with?('.', '-')
|
66
|
-
value
|
67
|
-
else
|
68
|
-
"#{value}."
|
69
|
-
end
|
70
|
-
end
|
71
|
-
acc += record.fields(%w[880]).filter_map do |field|
|
72
|
-
next unless field.any? { |sf| sf.code == '6' && sf.value.in?(%w[100 110]) }
|
73
|
-
|
74
|
-
suba = field.find_all(&subfield_in?(%w[a])).map { |sf|
|
75
|
-
convert_name_order(sf.value)
|
76
|
-
}.first
|
77
|
-
oth = join_and_squish(field.find_all(&subfield_not_in?(%w[6 8 a t])).map(&:value))
|
78
|
-
join_and_squish [suba, oth]
|
79
|
-
end
|
80
|
-
acc.uniq
|
34
|
+
name_search_values record: record, tags: TAGS, relator_map: relator_map
|
81
35
|
end
|
82
36
|
|
83
37
|
# Auxiliary Author/Creator search field
|
38
|
+
# This duplicates the values returned by the search method, but adds in additional MARC tags to include
|
39
|
+
# creator-adjacent entities. The added 4xx tags are mostly obsolete, but the 7xx tags are important. See:
|
40
|
+
# {https://www.loc.gov/marc/bibliographic/bd700.html MARC 700},
|
41
|
+
# {https://www.loc.gov/marc/bibliographic/bd710.html MARC 710},
|
42
|
+
# and {https://www.loc.gov/marc/bibliographic/bd711.html MARC 711}. The 800, 810 and 8111 tags are similar in
|
43
|
+
# theme to the 7xx fields but apply to serial records.
|
84
44
|
# @note ported from get_author_creator_2_search_values
|
85
|
-
# @todo port this later
|
86
45
|
# @param [MARC::Record] record
|
87
46
|
# @return [Array<String>] array of extended author/creator values for indexing
|
88
|
-
def search_aux(record)
|
47
|
+
def search_aux(record, relator_map: Mappers.relator)
|
48
|
+
name_search_values record: record, tags: AUX_TAGS, relator_map: relator_map
|
49
|
+
end
|
89
50
|
|
90
51
|
# All author/creator values for display (like #show, but multivalued?) - no 880 linkage
|
91
52
|
# @note ported from get_author_creator_values (indexed as author_creator_a) - shown on results page
|
@@ -106,9 +67,9 @@ module PennMARC
|
|
106
67
|
def show(record)
|
107
68
|
fields = record.fields(TAGS)
|
108
69
|
fields += record.fields('880').select { |field| subfield_value_in?(field, '6', TAGS) }
|
109
|
-
fields.filter_map
|
70
|
+
fields.filter_map { |field|
|
110
71
|
join_subfields(field, &subfield_not_in?(%w[0 1 4 6 8 e w]))
|
111
|
-
|
72
|
+
}.uniq
|
112
73
|
end
|
113
74
|
|
114
75
|
# Author/Creator sort. Does not map and include any relator codes.
|
@@ -134,11 +95,11 @@ module PennMARC
|
|
134
95
|
700 => 'abcdjq', 710 => 'abcdjq', 711 => 'abcen',
|
135
96
|
800 => 'abcdjq', 810 => 'abcdjq', 811 => 'abcen'
|
136
97
|
}
|
137
|
-
source_map.flat_map
|
98
|
+
source_map.flat_map { |field_num, subfields|
|
138
99
|
record.fields(field_num.to_s).map do |field|
|
139
100
|
trim_punctuation(join_subfields(field, &subfield_in?(subfields.chars)))
|
140
101
|
end
|
141
|
-
|
102
|
+
}.uniq
|
142
103
|
end
|
143
104
|
|
144
105
|
# Conference for display, intended for results display
|
@@ -147,9 +108,9 @@ module PennMARC
|
|
147
108
|
# @param [Hash] relator_map
|
148
109
|
# @return [Array<String>] array of conference values
|
149
110
|
def conference_show(record, relator_map: Mappers.relator)
|
150
|
-
record.fields('111').filter_map
|
111
|
+
record.fields('111').filter_map { |field|
|
151
112
|
name_from_main_entry field, relator_map
|
152
|
-
|
113
|
+
}.uniq
|
153
114
|
end
|
154
115
|
|
155
116
|
# Conference detailed display, intended for record show page.
|
@@ -169,7 +130,7 @@ module PennMARC
|
|
169
130
|
conf_extra = join_subfields field, &subfield_in?(%w[e j w])
|
170
131
|
join_and_squish [conf, conf_extra].compact_blank
|
171
132
|
end
|
172
|
-
values + record.fields('880').filter_map do |field|
|
133
|
+
conferences = values + record.fields('880').filter_map do |field|
|
173
134
|
next unless subfield_value_in? field, '6', %w[111 711]
|
174
135
|
|
175
136
|
next if subfield_defined? field, 'i'
|
@@ -178,11 +139,17 @@ module PennMARC
|
|
178
139
|
conf_extra = join_subfields(field, &subfield_in?(%w[4 e j w]))
|
179
140
|
join_and_squish [conf, conf_extra]
|
180
141
|
end
|
142
|
+
conferences.uniq
|
181
143
|
end
|
182
144
|
|
183
|
-
#
|
184
|
-
# @
|
185
|
-
|
145
|
+
# Conference name values for searching
|
146
|
+
# @param [MARC::Record] record
|
147
|
+
# @return [Array<String>]
|
148
|
+
def conference_search(record)
|
149
|
+
record.fields(CONFERENCE_SEARCH_TAGS).filter_map { |field|
|
150
|
+
join_subfields(field, &subfield_in?(%w[a c d e]))
|
151
|
+
}.uniq
|
152
|
+
end
|
186
153
|
|
187
154
|
# Retrieve contributor values for display from fields {https://www.oclc.org/bibformats/en/7xx/700.html 700}
|
188
155
|
# and {https://www.oclc.org/bibformats/en/7xx/710.html 710} and their linked alternates. Joins subfields
|
@@ -193,7 +160,7 @@ module PennMARC
|
|
193
160
|
# @return [Array<String>]
|
194
161
|
def contributor_show(record, relator_map: Mappers.relator)
|
195
162
|
indicator_2_options = ['', ' ', '0']
|
196
|
-
|
163
|
+
values = record.fields(%w[700 710]).filter_map do |field|
|
197
164
|
next unless indicator_2_options.member?(field.indicator2)
|
198
165
|
next if subfield_defined? field, 'i'
|
199
166
|
|
@@ -210,7 +177,7 @@ module PennMARC
|
|
210
177
|
}.join
|
211
178
|
"#{contributor} #{contributor_append}".squish
|
212
179
|
end
|
213
|
-
contributors + record.fields('880').filter_map do |field|
|
180
|
+
contributors = values + record.fields('880').filter_map do |field|
|
214
181
|
next unless subfield_value_in?(field, '6', %w[700 710])
|
215
182
|
next if subfield_defined?(field, 'i')
|
216
183
|
|
@@ -218,10 +185,37 @@ module PennMARC
|
|
218
185
|
contributor_append = join_subfields(field, &subfield_in?(%w[e u 3]))
|
219
186
|
"#{contributor} #{contributor_append}".squish
|
220
187
|
end
|
188
|
+
contributors.uniq
|
221
189
|
end
|
222
190
|
|
223
191
|
private
|
224
192
|
|
193
|
+
# @param [MARC::Record] record
|
194
|
+
# @param [Array] tags to consider
|
195
|
+
# @param [Hash] relator_map
|
196
|
+
# @return [Array<String>] name values from given tags
|
197
|
+
def name_search_values(record:, tags:, relator_map:)
|
198
|
+
acc = record.fields(tags).filter_map do |field|
|
199
|
+
name_from_main_entry field, relator_map, should_convert_name_order: false
|
200
|
+
end
|
201
|
+
|
202
|
+
acc += record.fields(tags).filter_map do |field|
|
203
|
+
name_from_main_entry field, relator_map, should_convert_name_order: true
|
204
|
+
end
|
205
|
+
|
206
|
+
acc += record.fields(['880']).filter_map do |field|
|
207
|
+
next unless field.any? { |sf| sf.code == '6' && sf.value.in?(tags) }
|
208
|
+
|
209
|
+
suba = field.find_all(&subfield_in?(%w[a])).filter_map { |sf|
|
210
|
+
convert_name_order(sf.value)
|
211
|
+
}.first
|
212
|
+
oth = join_and_squish(field.find_all(&subfield_not_in?(%w[6 8 a t])).map(&:value))
|
213
|
+
join_and_squish [suba, oth]
|
214
|
+
end
|
215
|
+
|
216
|
+
acc.uniq
|
217
|
+
end
|
218
|
+
|
225
219
|
# Trim punctuation method extracted from Traject macro, to ensure consistent output
|
226
220
|
# @todo move to Util?
|
227
221
|
# @param [String] string
|
@@ -244,11 +238,14 @@ module PennMARC
|
|
244
238
|
|
245
239
|
# Extract the information we care about from 1xx fields, map relator codes, and use appropriate punctuation
|
246
240
|
# @param [MARC::Field] field
|
241
|
+
# @param [Hash] mapping
|
242
|
+
# @param [Boolean] should_convert_name_order
|
247
243
|
# @return [String] joined subfield values for value from field
|
248
|
-
def name_from_main_entry(field, mapping)
|
249
|
-
name_subfields = %w[0 1 4 6 8]
|
244
|
+
def name_from_main_entry(field, mapping, should_convert_name_order: false)
|
250
245
|
s = field.filter_map { |sf|
|
251
|
-
if
|
246
|
+
if sf.code == 'a'
|
247
|
+
should_convert_name_order ? convert_name_order(sf.value) : sf.value
|
248
|
+
elsif NAME_EXCLUDED_SUBFIELDS.exclude?(sf.code)
|
252
249
|
" #{sf.value}"
|
253
250
|
elsif sf.code == '4'
|
254
251
|
relator = translate_relator(sf.value, mapping)
|
@@ -19,13 +19,13 @@ module PennMARC
|
|
19
19
|
# @param [Marc::Record]
|
20
20
|
# @return [Array<string>] Array of types
|
21
21
|
def type_facet(record)
|
22
|
-
record.fields('944').filter_map
|
22
|
+
record.fields('944').filter_map { |field|
|
23
23
|
# skip unless specified database format type present
|
24
24
|
next unless subfield_value?(field, 'a', /#{DATABASES_FACET_VALUE}/o)
|
25
25
|
|
26
26
|
type = field.find { |subfield| subfield.code == 'b' }
|
27
27
|
type&.value
|
28
|
-
|
28
|
+
}.uniq
|
29
29
|
end
|
30
30
|
|
31
31
|
# Retrieves database subject category/communities of interest (subfield 'a') from
|
@@ -37,13 +37,13 @@ module PennMARC
|
|
37
37
|
def category_facet(record)
|
38
38
|
return [] unless curated_db?(record)
|
39
39
|
|
40
|
-
record.fields('943').filter_map
|
40
|
+
record.fields('943').filter_map { |field|
|
41
41
|
# skip unless Community of Interest code is in subfield '2'
|
42
42
|
next unless subfield_value?(field, '2', /#{COI_CODE}/o)
|
43
43
|
|
44
44
|
category = field.find { |subfield| subfield.code == 'a' }
|
45
45
|
category&.value
|
46
|
-
|
46
|
+
}.uniq
|
47
47
|
end
|
48
48
|
|
49
49
|
# Concatenates database subject category with database sub subject category in the format "category--subcategory"
|
@@ -58,7 +58,7 @@ module PennMARC
|
|
58
58
|
def subcategory_facet(record)
|
59
59
|
return [] unless curated_db?(record)
|
60
60
|
|
61
|
-
record.fields('943').filter_map
|
61
|
+
record.fields('943').filter_map { |field|
|
62
62
|
# skip unless Community of Interest code is in subfield '2'
|
63
63
|
next unless subfield_value?(field, '2', /#{COI_CODE}/o)
|
64
64
|
|
@@ -73,7 +73,7 @@ module PennMARC
|
|
73
73
|
next if subcategory.blank?
|
74
74
|
|
75
75
|
"#{category.value}--#{subcategory.value}"
|
76
|
-
|
76
|
+
}.uniq
|
77
77
|
end
|
78
78
|
|
79
79
|
private
|
@@ -20,13 +20,13 @@ module PennMARC
|
|
20
20
|
end
|
21
21
|
|
22
22
|
# Retrieve date added (subfield 'q') from enriched marc 'itm' field.
|
23
|
-
# {PennMARC::
|
23
|
+
# {PennMARC::Enriched} maps enriched marc fields and subfields created during Alma publishing. The enriched
|
24
24
|
# metadata provided by the Alma API does not include the date created value, so we can't work with that here.
|
25
25
|
# @param [MARC::Record] record
|
26
26
|
# @return [DateTime, nil] The date added, or nil if date found in record is invalid
|
27
27
|
def added(record)
|
28
|
-
record.fields(
|
29
|
-
subfield_values(field,
|
28
|
+
record.fields(Enriched::Pub::ITEM_TAG).flat_map { |field|
|
29
|
+
subfield_values(field, Enriched::Pub::ITEM_DATE_CREATED).filter_map do |date_added|
|
30
30
|
# On 2022-05-02, this field value (as exported in enriched publishing
|
31
31
|
# job from Alma) began truncating time to day-level granularity. We have
|
32
32
|
# no guarantee that this won't switch back in the future, so for the
|
@@ -14,9 +14,10 @@ module PennMARC
|
|
14
14
|
# @param [MARC::Record] record
|
15
15
|
# @return [Array<String>] array of editions and their alternates
|
16
16
|
def show(record)
|
17
|
-
record.fields('250').map { |field|
|
17
|
+
editions = record.fields('250').map { |field|
|
18
18
|
join_subfields(field, &subfield_not_in?(%w[6 8]))
|
19
19
|
} + linked_alternate_not_6_or_8(record, '250')
|
20
|
+
editions.uniq
|
20
21
|
end
|
21
22
|
|
22
23
|
# Edition values for display in search results. Just grab the first 250 field.
|
@@ -42,12 +43,13 @@ module PennMARC
|
|
42
43
|
|
43
44
|
other_edition_value(field, relator_map)
|
44
45
|
end
|
45
|
-
values + record.fields('880').filter_map do |field|
|
46
|
+
editions = values + record.fields('880').filter_map do |field|
|
46
47
|
next unless field.indicator2.blank? && subfield_value_in?(field, '6', %w[775]) &&
|
47
48
|
subfield_defined?(field, 'i')
|
48
49
|
|
49
50
|
other_edition_value(field, relator_map)
|
50
51
|
end
|
52
|
+
editions.uniq
|
51
53
|
end
|
52
54
|
|
53
55
|
private
|
@@ -48,7 +48,7 @@ module PennMARC
|
|
48
48
|
end
|
49
49
|
join_subfields(f, &subfield_not_in?(subfield_to_ignore))
|
50
50
|
end
|
51
|
-
results.compact_blank
|
51
|
+
results.compact_blank.uniq
|
52
52
|
end
|
53
53
|
|
54
54
|
# Get Format values for faceting. Format values are determined using complex logic for each possible format value.
|
@@ -122,23 +122,24 @@ module PennMARC
|
|
122
122
|
OTHER
|
123
123
|
end
|
124
124
|
end
|
125
|
-
formats.concat(curated_format(record))
|
125
|
+
formats.concat(curated_format(record)).uniq
|
126
126
|
end
|
127
127
|
|
128
128
|
# Show "Other Format" values from {https://www.oclc.org/bibformats/en/7xx/776.html 776} and any 880 linkage.
|
129
129
|
# @todo is 774 an error in the linked field in legacy? i changed to 776 here
|
130
130
|
# @param [MARC::Record] record
|
131
|
-
# @return [Array] other format values for display
|
131
|
+
# @return [Array<String>] other format values for display
|
132
132
|
def other_show(record)
|
133
|
-
|
133
|
+
values = record.fields('776').filter_map do |field|
|
134
134
|
value = join_subfields(field, &subfield_in?(%w[i a s t o]))
|
135
135
|
next if value.blank?
|
136
136
|
|
137
137
|
value
|
138
138
|
end
|
139
|
-
other_formats + linked_alternate(record, '776') do |sf|
|
139
|
+
other_formats = values + linked_alternate(record, '776') do |sf|
|
140
140
|
sf.code.in? %w[i a s t o]
|
141
141
|
end
|
142
|
+
other_formats.uniq
|
142
143
|
end
|
143
144
|
|
144
145
|
# Retrieve cartographic reference data for map/atlas formats for display from
|
@@ -146,9 +147,9 @@ module PennMARC
|
|
146
147
|
# @param [MARC::Record] record
|
147
148
|
# @return [Array<String>]
|
148
149
|
def cartographic_show(record)
|
149
|
-
record.fields(%w[255 342]).map
|
150
|
+
record.fields(%w[255 342]).map { |field|
|
150
151
|
join_subfields(field, &subfield_not_in?(%w[6 8]))
|
151
|
-
|
152
|
+
}.uniq
|
152
153
|
end
|
153
154
|
|
154
155
|
# Check if a set of locations has any locations that include the term 'manuscripts'
|
@@ -165,14 +166,14 @@ module PennMARC
|
|
165
166
|
# @param [MARC::Record] record
|
166
167
|
# @return [Array]
|
167
168
|
def call_nums(record)
|
168
|
-
if field_defined?(record,
|
169
|
-
record.fields(
|
170
|
-
join_subfields(field, &subfield_in?([
|
171
|
-
|
169
|
+
if field_defined?(record, Enriched::Pub::PHYS_INVENTORY_TAG)
|
170
|
+
record.fields(Enriched::Pub::PHYS_INVENTORY_TAG).map do |field|
|
171
|
+
join_subfields(field, &subfield_in?([Enriched::Pub::HOLDING_CLASSIFICATION_PART,
|
172
|
+
Enriched::Pub::HOLDING_ITEM_PART]))
|
172
173
|
end
|
173
|
-
elsif field_defined?(record,
|
174
|
-
record.fields(
|
175
|
-
join_subfields(field, &subfield_in?([
|
174
|
+
elsif field_defined?(record, Enriched::Api::PHYS_INVENTORY_TAG)
|
175
|
+
record.fields(Enriched::Api::PHYS_INVENTORY_TAG).map do |field|
|
176
|
+
join_subfields(field, &subfield_in?([Enriched::Api::PHYS_CALL_NUMBER_TYPE]))
|
176
177
|
end
|
177
178
|
else
|
178
179
|
[]
|