pennmarc 1.4.9 → 1.4.12
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 +4 -4
- data/.gitlab/ci-requirements.yml +17 -0
- data/.gitlab-ci.yml +90 -22
- data/.rubocop_todo.yml +12 -13
- data/README.md +13 -1
- data/lib/pennmarc/heading_control.rb +20 -12
- data/lib/pennmarc/helpers/creator.rb +18 -5
- data/lib/pennmarc/helpers/edition.rb +2 -2
- data/lib/pennmarc/helpers/format.rb +1 -1
- data/lib/pennmarc/helpers/language.rb +1 -1
- data/lib/pennmarc/helpers/subject.rb +47 -51
- data/lib/pennmarc/helpers/title.rb +14 -14
- data/lib/pennmarc/mappings/headings_override.yml +13 -2
- data/lib/pennmarc/mappings/locations.yml +4 -0
- data/lib/pennmarc/version.rb +1 -1
- data/spec/lib/pennmarc/heading_control_spec.rb +9 -19
- data/spec/lib/pennmarc/helpers/creator_spec.rb +32 -0
- data/spec/lib/pennmarc/helpers/subject_spec.rb +14 -0
- data/spec/lib/pennmarc/helpers/title_spec.rb +1 -1
- metadata +3 -3
- data/.pipeline.yml +0 -73
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed8428b56d9ed1e81912885aef968ba0f104f647491afd2498e4e3893748b9c4
|
|
4
|
+
data.tar.gz: fead988cff9b7cb56201510a7eba63152dd16f8e691ece753d6b306158afc53e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 21e44d07cd4bde553e717f84a944dd297055f94518a27a14f9d9fb3d64c3f1f9172746af0ba0d9e1549fb69198a7ef5f5c23b5323189bba500d04cc5ace40a32
|
|
7
|
+
data.tar.gz: caaf7925c9d91833c92ba25524dc70f4bad6f4b31c7640ff35823f20477d5d9c7b7449770bd8ffdcd66fde1a39460ddbad58666d71eeeb22ca3b225f5da72d9a
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
collections:
|
|
3
|
+
- name: "https://gitlab.library.upenn.edu/api/v4/projects/devops%2Fansible%2Fcollection\
|
|
4
|
+
/packages/generic/collections/stack-v2.2.0/library-stack-2.2.0.tar.gz"
|
|
5
|
+
type: url
|
|
6
|
+
- name: "https://gitlab.library.upenn.edu/api/v4/projects/devops%2Fansible%2Fcollection\
|
|
7
|
+
/packages/generic/collections/proxmox-v1.6.0/library-proxmox-1.6.0.tar.gz"
|
|
8
|
+
type: url
|
|
9
|
+
- name: "https://gitlab.library.upenn.edu/api/v4/projects/devops%2Fansible%2Fcollection\
|
|
10
|
+
/packages/generic/collections/services-v1.11.0/library-services-1.11.0.tar.gz"
|
|
11
|
+
type: url
|
|
12
|
+
- name: "https://gitlab.library.upenn.edu/api/v4/projects/devops%2Fansible%2Fcollection\
|
|
13
|
+
/packages/generic/collections/infra-v4.7.0/library-infra-4.7.0.tar.gz"
|
|
14
|
+
type: url
|
|
15
|
+
- name: "https://gitlab.library.upenn.edu/api/v4/projects/devops%2Fansible%2Fcollection\
|
|
16
|
+
/packages/generic/collections/ci-v0.7.0/library-ci-0.7.0.tar.gz"
|
|
17
|
+
type: url
|
data/.gitlab-ci.yml
CHANGED
|
@@ -1,34 +1,102 @@
|
|
|
1
1
|
---
|
|
2
|
+
variables:
|
|
3
|
+
FF_SCRIPT_SECTIONS: "true"
|
|
4
|
+
|
|
2
5
|
include:
|
|
3
|
-
-
|
|
6
|
+
- component: gitlab.library.upenn.edu/devops/gitlab/components/sast/secret_scan@~latest
|
|
7
|
+
- component: gitlab.library.upenn.edu/devops/gitlab/components/general/install_ansible@~latest
|
|
8
|
+
- component: gitlab.library.upenn.edu/devops/gitlab/components/general/ci_requirements@~latest
|
|
9
|
+
- component: gitlab.library.upenn.edu/devops/gitlab/components/general/install_hashicorp_vault@~latest
|
|
10
|
+
- component: gitlab.library.upenn.edu/devops/gitlab/components/general/vault_jwt_auth@~latest
|
|
11
|
+
inputs:
|
|
12
|
+
ci_requirements_file: ".gitlab/ci-requirements.yml"
|
|
13
|
+
vault_addr: ${VAULT_ADDR}
|
|
14
|
+
vault_auth_path: ${VAULT_AUTH_PATH}
|
|
15
|
+
vault_auth_role: ${VAULT_AUTH_ROLE}
|
|
16
|
+
- component: gitlab.library.upenn.edu/devops/gitlab/components/tests/rubocop@~latest
|
|
17
|
+
inputs:
|
|
18
|
+
app_dir: "."
|
|
19
|
+
job_image_name: "ruby"
|
|
20
|
+
job_image_tag: "3.2.2"
|
|
21
|
+
needs:
|
|
22
|
+
- secret_detection
|
|
4
23
|
|
|
5
24
|
stages:
|
|
6
25
|
- secret_check
|
|
7
|
-
-
|
|
26
|
+
- test
|
|
27
|
+
- deploy
|
|
28
|
+
|
|
29
|
+
rubocop:
|
|
30
|
+
before_script:
|
|
31
|
+
- bundle install -j $(nproc)
|
|
32
|
+
rules:
|
|
33
|
+
- exists:
|
|
34
|
+
- .rubocop.yml
|
|
8
35
|
|
|
9
|
-
|
|
10
|
-
stage:
|
|
11
|
-
|
|
36
|
+
rspec:
|
|
37
|
+
stage: test
|
|
38
|
+
image: ruby:3.2.2
|
|
39
|
+
needs:
|
|
40
|
+
- secret_detection
|
|
41
|
+
variables:
|
|
42
|
+
ANSIBLE_COLLECTIONS_CACHE_DIR: .cache/ansible/collections
|
|
43
|
+
GALAXY_REQUIREMENTS_FILE: ".gitlab/ci-requirements.yml"
|
|
44
|
+
cache:
|
|
45
|
+
- key:
|
|
46
|
+
files:
|
|
47
|
+
- .gitlab/ci-requirements.yml
|
|
48
|
+
prefix: ansible-collections
|
|
49
|
+
paths:
|
|
50
|
+
- $ANSIBLE_COLLECTIONS_CACHE_DIR
|
|
51
|
+
before_script:
|
|
52
|
+
- !reference [.install_ansible, before_script]
|
|
53
|
+
- !reference [.ci_requirements, before_script]
|
|
54
|
+
- bundle install -j $(nproc)
|
|
55
|
+
script:
|
|
56
|
+
- tail -F --pid=$$ rspec.log 2>/dev/null &
|
|
57
|
+
- ansible-playbook library.ci.rails_test -e rails_test_app_dir="."
|
|
12
58
|
artifacts:
|
|
13
59
|
paths:
|
|
14
|
-
- .
|
|
15
|
-
expire_in: 1
|
|
60
|
+
- rspec.xml
|
|
61
|
+
expire_in: 1 week
|
|
62
|
+
reports:
|
|
63
|
+
junit: rspec.xml
|
|
64
|
+
rules:
|
|
65
|
+
- exists:
|
|
66
|
+
- spec/**.rb
|
|
67
|
+
- spec/**/**.rb
|
|
16
68
|
tags:
|
|
17
69
|
- build
|
|
18
|
-
rules:
|
|
19
|
-
- when: always
|
|
20
70
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
71
|
+
# This step was inspired from Gitlab's example of gem publication:
|
|
72
|
+
# https://gitlab.com/gitlab-org/quality/pipeline-common/-/blob/master/ci/gem-release.yml
|
|
73
|
+
gem_publication:
|
|
74
|
+
stage: deploy
|
|
75
|
+
image: ruby:3.2.2
|
|
76
|
+
extends:
|
|
77
|
+
- .vault_jwt_auth
|
|
78
|
+
variables:
|
|
79
|
+
GEMSPEC_FILE: "${CI_PROJECT_NAME}.gemspec"
|
|
80
|
+
before_script:
|
|
81
|
+
- !reference [.install_ansible, before_script]
|
|
82
|
+
- !reference [.install_hashicorp_vault, before_script]
|
|
83
|
+
- !reference [.vault_jwt_auth, before_script]
|
|
84
|
+
script:
|
|
85
|
+
- export GEM_HOST_API_KEY="$(vault kv get -field=rubygems_api_key ${VAULT_KV_ENDPOINT}${ENVIRONMENT})"
|
|
86
|
+
- |
|
|
87
|
+
gem -v
|
|
88
|
+
rm -f ./*.gem
|
|
89
|
+
[ -f "${GEMSPEC_FILE}" ] || (echo "No ${GEMSPEC_FILE} file found!" && exit 1)
|
|
90
|
+
- '[ -n "${GEM_HOST_API_KEY}" ] || (echo "GEM_HOST_API_KEY is not set!" && exit 1)'
|
|
91
|
+
- GEM_FILE=$(echo "${CI_PROJECT_NAME}-${CI_COMMIT_TAG:1}.gem")
|
|
92
|
+
- |
|
|
93
|
+
gem build "${GEMSPEC_FILE}"
|
|
94
|
+
[ -f "${GEM_FILE}" ] || (echo "No ${GEM_FILE} file found!" && exit 1)
|
|
95
|
+
- '[ "${DISABLE_GEM_PUSH}" == "true" ] || gem push "${GEM_FILE}"'
|
|
31
96
|
rules:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
97
|
+
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+(\.[a-zA-Z0-9]+)?$/
|
|
98
|
+
artifacts:
|
|
99
|
+
paths:
|
|
100
|
+
- "*.gem"
|
|
101
|
+
tags:
|
|
102
|
+
- deploy
|
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
|
|
3
|
+
# on 2026-09-28 22:29:36 UTC using RuboCop version 1.84.1.
|
|
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
|
|
@@ -33,7 +33,7 @@ Metrics/ClassLength:
|
|
|
33
33
|
- 'lib/pennmarc/helpers/subject.rb'
|
|
34
34
|
- 'lib/pennmarc/helpers/title.rb'
|
|
35
35
|
|
|
36
|
-
# Offense count:
|
|
36
|
+
# Offense count: 23
|
|
37
37
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
38
38
|
Metrics/CyclomaticComplexity:
|
|
39
39
|
Exclude:
|
|
@@ -70,7 +70,7 @@ Metrics/ModuleLength:
|
|
|
70
70
|
Exclude:
|
|
71
71
|
- 'lib/pennmarc/util.rb'
|
|
72
72
|
|
|
73
|
-
# Offense count:
|
|
73
|
+
# Offense count: 18
|
|
74
74
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
75
75
|
Metrics/PerceivedComplexity:
|
|
76
76
|
Exclude:
|
|
@@ -82,6 +82,7 @@ Metrics/PerceivedComplexity:
|
|
|
82
82
|
- 'lib/pennmarc/helpers/note.rb'
|
|
83
83
|
- 'lib/pennmarc/helpers/production.rb'
|
|
84
84
|
- 'lib/pennmarc/helpers/series.rb'
|
|
85
|
+
- 'lib/pennmarc/helpers/subject.rb'
|
|
85
86
|
- 'lib/pennmarc/helpers/title.rb'
|
|
86
87
|
- 'lib/pennmarc/util.rb'
|
|
87
88
|
|
|
@@ -109,18 +110,16 @@ RSpec/ExampleLength:
|
|
|
109
110
|
Exclude:
|
|
110
111
|
- 'spec/lib/pennmarc/marc_util_spec.rb'
|
|
111
112
|
|
|
113
|
+
# Offense count: 1
|
|
114
|
+
# Configuration parameters: AllowSubject, Max.
|
|
115
|
+
RSpec/MultipleMemoizedHelpers:
|
|
116
|
+
Exclude:
|
|
117
|
+
- 'spec/lib/pennmarc/helpers/subject_spec.rb'
|
|
118
|
+
|
|
112
119
|
# Offense count: 2
|
|
113
|
-
# Configuration parameters:
|
|
114
|
-
#
|
|
120
|
+
# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
|
|
121
|
+
# SupportedInflectors: default, active_support
|
|
115
122
|
RSpec/SpecFilePathFormat:
|
|
116
123
|
Exclude:
|
|
117
124
|
- 'spec/lib/pennmarc/parser_spec.rb'
|
|
118
125
|
- 'spec/lib/pennmarc/services/title_suggestion_weight_service_spec.rb'
|
|
119
|
-
|
|
120
|
-
# Offense count: 1
|
|
121
|
-
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
122
|
-
# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
|
|
123
|
-
# SupportedStyles: predicate, comparison
|
|
124
|
-
Style/NumericPredicate:
|
|
125
|
-
Exclude:
|
|
126
|
-
- 'lib/pennmarc/services/title_suggestion_weight_service.rb'
|
data/README.md
CHANGED
|
@@ -93,7 +93,19 @@ rspec
|
|
|
93
93
|
2. Create a new tag that matches the version set in step 1 (ex: v1.1.0).
|
|
94
94
|
3. Add a release title that is the same as the tag name.
|
|
95
95
|
4. Submit by clicking "Create Release".
|
|
96
|
-
4. Once the release is created a pipeline will run to publish the gem to RubyGems.
|
|
96
|
+
4. Once the release is created a pipeline will run to publish the gem to RubyGems.
|
|
97
|
+
|
|
98
|
+
### Publishing Manually
|
|
99
|
+
|
|
100
|
+
You can also publish the gem using `gem` commands. After updating the `VERSION` value on `main`, you can run the
|
|
101
|
+
following commands:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
gem build pennmarc.gemspec
|
|
105
|
+
gem push pennmarc-<VERSION>.gem
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
You may need an OTP code for use in the push to bundler.
|
|
97
109
|
|
|
98
110
|
### Versioning Guidelines
|
|
99
111
|
|
|
@@ -5,28 +5,36 @@ module PennMARC
|
|
|
5
5
|
class HeadingControl
|
|
6
6
|
# These codes are expected to be found in sf2 of a subject/genre field when the indicator2 value is 7, indicating
|
|
7
7
|
# "source specified". There are some sources whose headings we don't want to display.
|
|
8
|
-
ALLOWED_SOURCE_CODES = %w[aat cct fast ftamc gmgpc gsafd homoit jlabsh lcgft lcsh lcstt lctgm
|
|
8
|
+
ALLOWED_SOURCE_CODES = %w[aat cct fast ftamc gmgpc gsafd homoit homoit/eng jlabsh lcgft lcsh lcstt lctgm local/upenn
|
|
9
9
|
local/osu mesh ndlsh nli nlksh rbbin rbgenr rbmscv rbpap rbpri rbprov rbpub rbtyp].freeze
|
|
10
10
|
|
|
11
11
|
REMOVE_TERM_REGEX = /#{Mappers.headings_to_remove&.join('|')}/i
|
|
12
12
|
REPLACE_TERM_REGEX = /(#{Mappers.heading_overrides.keys.join('|')})/i
|
|
13
13
|
|
|
14
|
+
# Don't perform any term overriding on these tags
|
|
15
|
+
NO_OVERRIDE_TAGS = %w[600 610 611 630].freeze
|
|
16
|
+
|
|
14
17
|
class << self
|
|
15
18
|
# Replace or remove any terms in provided values pursuant to the configuration in remove and override mappers.
|
|
16
19
|
# Used to remove or replace offensive or otherwise undesirable subject headings.
|
|
17
|
-
# @param
|
|
18
|
-
# @return [
|
|
19
|
-
def term_override(
|
|
20
|
-
values
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
# @param [String] value
|
|
21
|
+
# @return [String, nil]
|
|
22
|
+
def term_override(value)
|
|
23
|
+
# Remove values if they contain a remove term
|
|
24
|
+
return if value.match?(REMOVE_TERM_REGEX)
|
|
25
|
+
|
|
26
|
+
# return early if there are no terms to replace
|
|
27
|
+
return value if value.match(REPLACE_TERM_REGEX).nil?
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
# lookup and perform replacement
|
|
30
|
+
value.sub(::Regexp.last_match.to_s, Mappers.heading_overrides[::Regexp.last_match.to_s.downcase])
|
|
31
|
+
end
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
# Is the given field overrideable?
|
|
34
|
+
# @param field [MARC::Field]
|
|
35
|
+
# @return [Boolean]
|
|
36
|
+
def overrideable_field?(field)
|
|
37
|
+
!field.tag.in?(NO_OVERRIDE_TAGS)
|
|
30
38
|
end
|
|
31
39
|
end
|
|
32
40
|
end
|
|
@@ -9,10 +9,10 @@ module PennMARC
|
|
|
9
9
|
TAGS = %w[100 110].freeze
|
|
10
10
|
|
|
11
11
|
# For creator fields intended for display, these subfields are ignored
|
|
12
|
-
DISPLAY_EXCLUDED_SUBFIELDS = %w[a 0 1 4 5 6 8 t].freeze
|
|
12
|
+
DISPLAY_EXCLUDED_SUBFIELDS = %w[a 0 1 4 5 6 7 8 t].freeze
|
|
13
13
|
|
|
14
14
|
# For creator fields intended for searching, these subfields are ignored
|
|
15
|
-
SEARCH_EXCLUDED_SUBFIELDS = %w[a 1 4 5 6 8 t].freeze
|
|
15
|
+
SEARCH_EXCLUDED_SUBFIELDS = %w[a 1 4 5 6 7 8 t].freeze
|
|
16
16
|
|
|
17
17
|
# Aux tags for Author/Creator information, for use in search_aux method
|
|
18
18
|
AUX_TAGS = %w[100 110 111 400 410 411 700 710 711 800 810 811].freeze
|
|
@@ -23,6 +23,8 @@ module PennMARC
|
|
|
23
23
|
CONTRIBUTOR_TAGS = %w[700 710].freeze
|
|
24
24
|
CONTRIBUTOR_DISPLAY_SUBFIELDS = %w[a b c d j q u 3].freeze
|
|
25
25
|
|
|
26
|
+
UNIVERSITY_IDENTIFIER = 'PU'
|
|
27
|
+
|
|
26
28
|
FACET_SOURCE_MAP = {
|
|
27
29
|
100 => 'abcdjq', 110 => 'abcdjq', 111 => 'abcen',
|
|
28
30
|
700 => 'abcdjq', 710 => 'abcdjq', 711 => 'abcen',
|
|
@@ -33,7 +35,7 @@ module PennMARC
|
|
|
33
35
|
# Author/Creator search field, from tags
|
|
34
36
|
# {https://www.oclc.org/bibformats/en/1xx/100.html 100 Main Entry--Personal Name} and
|
|
35
37
|
# {https://www.oclc.org/bibformats/en/1xx/110.html 110 Main Entry--Corporate Name}. Includes subfield values
|
|
36
|
-
# except for those in the {
|
|
38
|
+
# except for those in the {SEARCH_EXCLUDED_SUBFIELDS} constant. Maps any relator codes found in ǂ4. To better
|
|
37
39
|
# handle name searches, returns names as both "First Last" and "Last, First" if a comma is found in ǂa. Also
|
|
38
40
|
# indexes any linked values in the 880.
|
|
39
41
|
# @param record [MARC::Record]
|
|
@@ -127,6 +129,8 @@ module PennMARC
|
|
|
127
129
|
def authors_list(record, main_tags_only: false, first_initial_only: false)
|
|
128
130
|
fields = record.fields(main_tags_only ? TAGS : TAGS + CONTRIBUTOR_TAGS)
|
|
129
131
|
fields.filter_map { |field|
|
|
132
|
+
next if university_provenance? field
|
|
133
|
+
|
|
130
134
|
if field['a'].present?
|
|
131
135
|
name = trim_trailing(:comma, field['a'])
|
|
132
136
|
first_initial_only ? abbreviate_name(name) : name
|
|
@@ -153,6 +157,7 @@ module PennMARC
|
|
|
153
157
|
fields.each do |field|
|
|
154
158
|
next if indicator_2_options.exclude?(field.indicator2) && field.tag.in?(CONTRIBUTOR_TAGS)
|
|
155
159
|
next if subfield_defined? field, 'i'
|
|
160
|
+
next if university_provenance? field
|
|
156
161
|
|
|
157
162
|
relator = relator(field: field, relator_term_sf: 'e', relator_map: relator_map)
|
|
158
163
|
relator = 'Contributor' if relator.blank?
|
|
@@ -458,7 +463,7 @@ module PennMARC
|
|
|
458
463
|
# @param relator_map [Hash]
|
|
459
464
|
# @return [String]
|
|
460
465
|
def parse_show_value(field, relator_map: Mappers.relator)
|
|
461
|
-
creator = join_subfields(field, &subfield_not_in?(%w[0 1 4 6 8 e w]))
|
|
466
|
+
creator = join_subfields(field, &subfield_not_in?(%w[0 1 4 6 7 8 e w]))
|
|
462
467
|
append_relator(field: field, joined_subfields: creator, relator_term_sf: 'e', relator_map: relator_map)
|
|
463
468
|
end
|
|
464
469
|
|
|
@@ -470,7 +475,7 @@ module PennMARC
|
|
|
470
475
|
# @return [String]
|
|
471
476
|
def parse_conference_detail_show_value(field, relator_map: Mappers.relator)
|
|
472
477
|
conf = if subfield_undefined? field, 'i'
|
|
473
|
-
join_subfields field, &subfield_not_in?(%w[0 4 5 6 8 e j w])
|
|
478
|
+
join_subfields field, &subfield_not_in?(%w[0 4 5 6 7 8 e j w])
|
|
474
479
|
else
|
|
475
480
|
''
|
|
476
481
|
end
|
|
@@ -486,6 +491,14 @@ module PennMARC
|
|
|
486
491
|
def describes_author?(field)
|
|
487
492
|
field['4']&.downcase == 'aut' || field['e']&.downcase&.start_with?('author')
|
|
488
493
|
end
|
|
494
|
+
|
|
495
|
+
# Does the given field describe a contributor representing university provenance. A contributor tag with a '$5'
|
|
496
|
+
# value equal to the university identifier represents university provenance.
|
|
497
|
+
# @param field [MARC::Field]
|
|
498
|
+
# @return [Boolean]
|
|
499
|
+
def university_provenance?(field)
|
|
500
|
+
field.tag.in?(CONTRIBUTOR_TAGS) && subfield_values(field, '5').include?(UNIVERSITY_IDENTIFIER)
|
|
501
|
+
end
|
|
489
502
|
end
|
|
490
503
|
end
|
|
491
504
|
end
|
|
@@ -16,7 +16,7 @@ module PennMARC
|
|
|
16
16
|
# @return [Array<String>] array of editions and their alternates
|
|
17
17
|
def show(record, with_alternate: true)
|
|
18
18
|
editions = record.fields('250').map do |field|
|
|
19
|
-
join_subfields(field, &subfield_not_in?(%w[6 8]))
|
|
19
|
+
join_subfields(field, &subfield_not_in?(%w[6 7 8]))
|
|
20
20
|
end
|
|
21
21
|
editions += linked_alternate_not_6_or_8(record, '250') if with_alternate
|
|
22
22
|
|
|
@@ -30,7 +30,7 @@ module PennMARC
|
|
|
30
30
|
edition = record.fields('250').first
|
|
31
31
|
return if edition.blank?
|
|
32
32
|
|
|
33
|
-
join_subfields(edition, &subfield_not_in?(%w[6 8]))
|
|
33
|
+
join_subfields(edition, &subfield_not_in?(%w[6 7 8]))
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Entry for another available edition of the target item (horizontal relationship). When a note is generated
|
|
@@ -38,7 +38,7 @@ module PennMARC
|
|
|
38
38
|
def show(record)
|
|
39
39
|
results = record.fields('300').map { |f| join_subfields(f, &subfield_not_in?(%w[3 6 8])) }
|
|
40
40
|
results += record.fields(%w[254 255 310 342 352 362]).map do |f|
|
|
41
|
-
join_subfields(f, &subfield_not_in?(%w[6 8]))
|
|
41
|
+
join_subfields(f, &subfield_not_in?(%w[6 7 8]))
|
|
42
42
|
end
|
|
43
43
|
results += record.fields('340').map { |f| join_subfields(f, &subfield_not_in?(%w[0 2 6 8])) }
|
|
44
44
|
results += record.fields('880').map do |f|
|
|
@@ -16,7 +16,7 @@ module PennMARC
|
|
|
16
16
|
# @return [Array<String>] language values and notes
|
|
17
17
|
def show(record)
|
|
18
18
|
values = record.fields('546').map do |field|
|
|
19
|
-
join_subfields field, &subfield_not_in?(%w[6 8])
|
|
19
|
+
join_subfields field, &subfield_not_in?(%w[6 7 8])
|
|
20
20
|
end
|
|
21
21
|
language_values = values + linked_alternate(record, '546', &subfield_not_in?(%w[6 8]))
|
|
22
22
|
language_values.uniq
|
|
@@ -7,25 +7,25 @@ module PennMARC
|
|
|
7
7
|
# tightly to be preserved. As a result fo this, display methods and facet methods below are ported from their state
|
|
8
8
|
# prior to Michael's 2/2021 subject parsing changes.
|
|
9
9
|
class Subject < Helper
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
SEARCH_TAGS = %w[541 561 600 610 611 630 650 651 653].freeze
|
|
10
|
+
# Tags that serve as sources for Subject search values
|
|
11
|
+
# @todo why are 541 and 561 included here? these fields include info about source of acquisition
|
|
12
|
+
SEARCH_TAGS = %w[541 561 600 610 611 630 650 651 653].freeze
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
# Valid indicator 2 values indicating the source thesaurus for subject terms. These are:
|
|
15
|
+
# - 0: LCSH
|
|
16
|
+
# - 1: LC Children's
|
|
17
|
+
# - 2: MeSH
|
|
18
|
+
# - 4: Source not specified (local?)
|
|
19
|
+
# - 7: Source specified in ǂ2
|
|
20
|
+
VALID_SOURCE_INDICATORS = %w[0 1 2 4 7].freeze
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
# Tags that serve as sources for Subject facet values
|
|
23
|
+
DISPLAY_TAGS = %w[600 610 611 630 650 651].freeze
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
# Local subject heading tags
|
|
26
|
+
LOCAL_TAGS = %w[690 691 697].freeze
|
|
28
27
|
|
|
28
|
+
class << self
|
|
29
29
|
# All Subjects for searching. This includes most subfield content from any field contained in {SEARCH_TAGS} or
|
|
30
30
|
# 69X, including any linked 880 fields. Fields must have an indicator2 value in {VALID_SOURCE_INDICATORS}.
|
|
31
31
|
# @todo this includes subfields that may not be desired like 1 (uri) and 2 (source code) but this might be OK for
|
|
@@ -63,11 +63,11 @@ module PennMARC
|
|
|
63
63
|
#
|
|
64
64
|
# @note this is ported mostly form MG's new-style Subject parsing
|
|
65
65
|
# @param record [MARC::Record]
|
|
66
|
-
# @param override [Boolean] remove undesirable terms or not
|
|
66
|
+
# @param override [Boolean] whether to remove undesirable terms in eligible fields or not
|
|
67
67
|
# @return [Array<String>] array of all subject values for faceting
|
|
68
68
|
def facet(record, override: true)
|
|
69
|
-
|
|
70
|
-
term_hash = build_subject_hash(field)
|
|
69
|
+
subject_fields(record, type: :facet).filter_map { |field|
|
|
70
|
+
term_hash = build_subject_hash(field, override)
|
|
71
71
|
next if term_hash.blank? || term_hash[:count]&.zero?
|
|
72
72
|
|
|
73
73
|
heading = format_term type: :facet, term: term_hash
|
|
@@ -75,8 +75,6 @@ module PennMARC
|
|
|
75
75
|
|
|
76
76
|
[heading, main_term].compact_blank
|
|
77
77
|
}.flatten.uniq
|
|
78
|
-
|
|
79
|
-
override ? HeadingControl.term_override(values) : values
|
|
80
78
|
end
|
|
81
79
|
|
|
82
80
|
# All Subjects for display. This includes all {DISPLAY_TAGS} and {LOCAL_TAGS}. For tags that specify a source,
|
|
@@ -87,8 +85,8 @@ module PennMARC
|
|
|
87
85
|
# @param override [Boolean] to remove undesirable terms or not
|
|
88
86
|
# @return [Array] array of all subject values for display
|
|
89
87
|
def show(record, override: true)
|
|
90
|
-
|
|
91
|
-
term_hash = build_subject_hash(field)
|
|
88
|
+
subject_fields(record, type: :all).filter_map { |field|
|
|
89
|
+
term_hash = build_subject_hash(field, override)
|
|
92
90
|
next if term_hash.blank? || term_hash[:count]&.zero?
|
|
93
91
|
|
|
94
92
|
heading = format_term type: :display, term: term_hash
|
|
@@ -96,7 +94,6 @@ module PennMARC
|
|
|
96
94
|
|
|
97
95
|
[heading, main_term].compact_blank
|
|
98
96
|
}.flatten.uniq
|
|
99
|
-
override ? HeadingControl.term_override(values) : values
|
|
100
97
|
end
|
|
101
98
|
|
|
102
99
|
# Get Subjects from "Children" ontology
|
|
@@ -105,14 +102,13 @@ module PennMARC
|
|
|
105
102
|
# @param override [Boolean] remove undesirable terms or not
|
|
106
103
|
# @return [Array] array of children's subject values for display
|
|
107
104
|
def childrens_show(record, override: true)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
subject_fields(record, type: :display, options: { tags: DISPLAY_TAGS, indicator2: '1' })
|
|
106
|
+
.filter_map { |field|
|
|
107
|
+
term_hash = build_subject_hash(field, override)
|
|
108
|
+
next if term_hash.blank? || term_hash[:count]&.zero?
|
|
112
109
|
|
|
113
|
-
|
|
110
|
+
format_term type: :display, term: term_hash
|
|
114
111
|
}.uniq
|
|
115
|
-
override ? HeadingControl.term_override(values) : values
|
|
116
112
|
end
|
|
117
113
|
|
|
118
114
|
# Get Subjects from "MeSH" ontology
|
|
@@ -121,14 +117,13 @@ module PennMARC
|
|
|
121
117
|
# @param override [Boolean] remove undesirable terms or not
|
|
122
118
|
# @return [Array] array of MeSH subject values for display
|
|
123
119
|
def medical_show(record, override: true)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
subject_fields(record, type: :display, options: { tags: DISPLAY_TAGS, indicator2: '2' })
|
|
121
|
+
.filter_map { |field|
|
|
122
|
+
term_hash = build_subject_hash(field, override)
|
|
123
|
+
next if term_hash.blank? || term_hash[:count]&.zero?
|
|
128
124
|
|
|
129
|
-
|
|
125
|
+
format_term type: :display, term: term_hash
|
|
130
126
|
}.uniq
|
|
131
|
-
override ? HeadingControl.term_override(values) : values
|
|
132
127
|
end
|
|
133
128
|
|
|
134
129
|
# Get Subject values from {DISPLAY_TAGS} where indicator2 is 4 and {LOCAL_TAGS}. Do not include any values where
|
|
@@ -140,15 +135,14 @@ module PennMARC
|
|
|
140
135
|
def local_show(record, override: true)
|
|
141
136
|
local_fields = subject_fields(record, type: :display, options: { tags: DISPLAY_TAGS, indicator2: '4' }) +
|
|
142
137
|
subject_fields(record, type: :local)
|
|
143
|
-
|
|
138
|
+
local_fields.filter_map { |field|
|
|
144
139
|
next if subfield_value?(field, '2', /penncoi/)
|
|
145
140
|
|
|
146
|
-
term_hash = build_subject_hash(field)
|
|
141
|
+
term_hash = build_subject_hash(field, override)
|
|
147
142
|
next if term_hash.blank? || term_hash[:count]&.zero?
|
|
148
143
|
|
|
149
144
|
format_term type: :display, term: term_hash
|
|
150
145
|
}.uniq
|
|
151
|
-
override ? HeadingControl.term_override(values) : values
|
|
152
146
|
end
|
|
153
147
|
|
|
154
148
|
private
|
|
@@ -177,25 +171,26 @@ module PennMARC
|
|
|
177
171
|
end
|
|
178
172
|
end
|
|
179
173
|
|
|
180
|
-
# Format a term hash as a string for display
|
|
174
|
+
# Format a term hash as a string for display, performing overrides if needed
|
|
181
175
|
#
|
|
182
|
-
# @todo support search field formatting?
|
|
183
176
|
# @param type [Symbol]
|
|
184
177
|
# @param term [Hash] components and information as a hash
|
|
185
|
-
# @return [String]
|
|
178
|
+
# @return [String, nil]
|
|
186
179
|
def format_term(type:, term:)
|
|
187
180
|
return unless type.in? %i[facet display]
|
|
188
181
|
|
|
189
182
|
# attempt to handle poorly coded record
|
|
190
183
|
normalize_single_subfield(term[:parts].first) if term[:count] == 1 && term[:parts].first.present?
|
|
191
184
|
|
|
192
|
-
case type
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
185
|
+
value = case type
|
|
186
|
+
when :facet
|
|
187
|
+
trim_trailing(:period, term[:parts].join('--').strip)
|
|
188
|
+
when :display
|
|
189
|
+
display_value = "#{term[:parts].join('--')} #{term[:append].join(' ')}".strip
|
|
190
|
+
display_value.ends_with?('.') ? display_value : "#{display_value}."
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
term[:override] ? HeadingControl.term_override(value) : value
|
|
199
194
|
end
|
|
200
195
|
|
|
201
196
|
# Is a field intended for display in a general subject field? To be included, the field tag is in either
|
|
@@ -251,13 +246,14 @@ module PennMARC
|
|
|
251
246
|
# heading. - MG
|
|
252
247
|
# @todo do i need all this?
|
|
253
248
|
# @param field [MARC::DataField]
|
|
249
|
+
# @param override [Boolean]
|
|
254
250
|
# @return [Hash{Symbol => Integer, Array, Boolean, String}, Nil]
|
|
255
|
-
def build_subject_hash(field)
|
|
251
|
+
def build_subject_hash(field, override)
|
|
256
252
|
term_info = { count: 0, parts: [], append: [], uri: nil,
|
|
257
253
|
local: field.indicator2 == '4' || field.tag.starts_with?('69'), # local subject heading
|
|
258
254
|
vernacular: field.tag == '880',
|
|
259
|
-
|
|
260
|
-
lcsh: lcsh?(field) }
|
|
255
|
+
override: override && HeadingControl.overrideable_field?(field),
|
|
256
|
+
subfield_a: nil, lcsh: lcsh?(field) }
|
|
261
257
|
field.each do |subfield|
|
|
262
258
|
case subfield.code
|
|
263
259
|
when '0', '6', '8', '5', '7'
|
|
@@ -68,7 +68,7 @@ module PennMARC
|
|
|
68
68
|
record.fields(%w[245 880]).filter_map { |field|
|
|
69
69
|
next if field.tag == '880' && no_subfield_value_matches?(field, '6', /^245/)
|
|
70
70
|
|
|
71
|
-
join_subfields(field, &subfield_not_in?(%w[c 6 8 h]))
|
|
71
|
+
join_subfields(field, &subfield_not_in?(%w[c 6 7 8 h]))
|
|
72
72
|
}.uniq
|
|
73
73
|
end
|
|
74
74
|
|
|
@@ -77,7 +77,7 @@ module PennMARC
|
|
|
77
77
|
# @param record [MARC::Record]
|
|
78
78
|
# @return [Array<String>] array of auxiliary title values for search
|
|
79
79
|
def search_aux(record)
|
|
80
|
-
values = search_aux_values(record: record, title_type: :main, &subfield_not_in?(%w[c 6 8])) +
|
|
80
|
+
values = search_aux_values(record: record, title_type: :main, &subfield_not_in?(%w[c 6 7 8])) +
|
|
81
81
|
search_aux_values(record: record, title_type: :related, &subfield_in?(%w[s t])) +
|
|
82
82
|
search_aux_values(record: record, title_type: :entity, &subfield_in?(%w[t])) +
|
|
83
83
|
search_aux_values(record: record, title_type: :note, &subfield_in?(%w[t]))
|
|
@@ -95,7 +95,7 @@ module PennMARC
|
|
|
95
95
|
record.fields(%w[245 880]).filter_map { |field|
|
|
96
96
|
next if field.tag == '880' && no_subfield_value_matches?(field, '6', /^245/)
|
|
97
97
|
|
|
98
|
-
join_subfields(field, &subfield_not_in?(%w[c 6 8 h]))
|
|
98
|
+
join_subfields(field, &subfield_not_in?(%w[c 6 7 8 h]))
|
|
99
99
|
}.uniq
|
|
100
100
|
end
|
|
101
101
|
|
|
@@ -105,7 +105,7 @@ module PennMARC
|
|
|
105
105
|
# @param record [MARC::Record]
|
|
106
106
|
# @return [Array<String>] auxiliary journal title information for search
|
|
107
107
|
def journal_search_aux(record)
|
|
108
|
-
values = search_aux_values(record: record, title_type: :main, journal: true, &subfield_not_in?(%w[c 6 8])) +
|
|
108
|
+
values = search_aux_values(record: record, title_type: :main, journal: true, &subfield_not_in?(%w[c 6 7 8])) +
|
|
109
109
|
search_aux_values(record: record, title_type: :related, journal: true, &subfield_in?(%w[s t])) +
|
|
110
110
|
search_aux_values(record: record, title_type: :entity, journal: true, &subfield_in?(%w[t])) +
|
|
111
111
|
search_aux_values(record: record, title_type: :note, journal: true, &subfield_in?(%w[t]))
|
|
@@ -120,20 +120,20 @@ module PennMARC
|
|
|
120
120
|
# @return [String] single title for display
|
|
121
121
|
def show(record)
|
|
122
122
|
field = record.fields('245')&.first
|
|
123
|
-
return
|
|
123
|
+
return NO_TITLE_PROVIDED unless field.present?
|
|
124
124
|
|
|
125
125
|
values = title_values(field)
|
|
126
126
|
[format_title(values[:title_or_form]), values[:punctuation], values[:other_info]].compact_blank.join(' ')
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
-
# Same as show, but with
|
|
129
|
+
# Same as show, but with most subfields included as found.
|
|
130
130
|
# @param record [MARC::Record]
|
|
131
131
|
# @return [String] detailed title for display
|
|
132
132
|
def detailed_show(record)
|
|
133
133
|
field = record.fields('245')&.first
|
|
134
134
|
return unless field
|
|
135
135
|
|
|
136
|
-
join_subfields(field, &subfield_not_in?(%w[6 8]))
|
|
136
|
+
join_subfields(field, &subfield_not_in?(%w[6 7 8]))
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
# Same structure as show, but linked alternate title.
|
|
@@ -198,7 +198,7 @@ module PennMARC
|
|
|
198
198
|
# @return [Array<String>] Array of standardized titles as strings
|
|
199
199
|
def standardized_show(record)
|
|
200
200
|
standardized_titles = record.fields(%w[130 240]).map do |field|
|
|
201
|
-
join_subfields(field, &subfield_not_in?(%w[0 6 8 e w]))
|
|
201
|
+
join_subfields(field, &subfield_not_in?(%w[0 6 7 8 e w]))
|
|
202
202
|
end
|
|
203
203
|
standardized_titles += record.fields('730').filter_map do |field|
|
|
204
204
|
# skip unless one of the indicators is blank
|
|
@@ -207,13 +207,13 @@ module PennMARC
|
|
|
207
207
|
# skip if a subfield i is present
|
|
208
208
|
next if subfield_defined?(field, 'i')
|
|
209
209
|
|
|
210
|
-
join_subfields(field, &subfield_not_in?(%w[5 6 8 e w]))
|
|
210
|
+
join_subfields(field, &subfield_not_in?(%w[5 6 7 8 e w]))
|
|
211
211
|
end
|
|
212
212
|
titles = standardized_titles + record.fields('880').filter_map do |field|
|
|
213
213
|
next unless subfield_undefined?(field, 'i') &&
|
|
214
214
|
subfield_value?(field, '6', /^(130|240|730)/)
|
|
215
215
|
|
|
216
|
-
join_subfields field, &subfield_not_in?(%w[5 6 8 e w])
|
|
216
|
+
join_subfields field, &subfield_not_in?(%w[5 6 7 8 e w])
|
|
217
217
|
end
|
|
218
218
|
titles.uniq
|
|
219
219
|
end
|
|
@@ -227,18 +227,18 @@ module PennMARC
|
|
|
227
227
|
# @return [Array<String>] Array of other titles as strings
|
|
228
228
|
def other_show(record)
|
|
229
229
|
other_titles = record.fields('246').map do |field|
|
|
230
|
-
join_subfields(field, &subfield_not_in?(%w[6 8]))
|
|
230
|
+
join_subfields(field, &subfield_not_in?(%w[6 7 8]))
|
|
231
231
|
end
|
|
232
232
|
other_titles += record.fields('740')
|
|
233
233
|
.filter_map do |field|
|
|
234
234
|
next unless field.indicator2.in? ['', ' ', '0', '1', '3']
|
|
235
235
|
|
|
236
|
-
join_subfields(field, &subfield_not_in?(%w[5 6 8]))
|
|
236
|
+
join_subfields(field, &subfield_not_in?(%w[5 6 7 8]))
|
|
237
237
|
end
|
|
238
238
|
titles = other_titles + record.fields('880').filter_map do |field|
|
|
239
239
|
next unless subfield_value? field, '6', /^(246|740)/
|
|
240
240
|
|
|
241
|
-
join_subfields(field, &subfield_not_in?(%w[5 6 8]))
|
|
241
|
+
join_subfields(field, &subfield_not_in?(%w[5 6 7 8]))
|
|
242
242
|
end
|
|
243
243
|
titles.uniq
|
|
244
244
|
end
|
|
@@ -257,7 +257,7 @@ module PennMARC
|
|
|
257
257
|
next unless field.tag == '247' || (field.tag == '880' && subfield_value?(field, '6', /^247/))
|
|
258
258
|
|
|
259
259
|
# 6 and 8 are not meaningful for display
|
|
260
|
-
former_title = join_subfields field, &subfield_not_in?(%w[6 8 e w])
|
|
260
|
+
former_title = join_subfields field, &subfield_not_in?(%w[6 7 8 e w])
|
|
261
261
|
former_title_append = join_subfields field, &subfield_in?(%w[e w])
|
|
262
262
|
"#{former_title} #{former_title_append}".strip
|
|
263
263
|
}.uniq
|
|
@@ -3,10 +3,21 @@
|
|
|
3
3
|
# read "Illegal Noncitizens".
|
|
4
4
|
# Also, use lower case only in the key to support efficient case-insensitive matching.
|
|
5
5
|
"illegal alien children": Undocumented immigrant children
|
|
6
|
-
"alien detention centers":
|
|
6
|
+
"alien detention centers": Noncitizen detention centers
|
|
7
7
|
"alien criminals": Noncitizen criminals
|
|
8
|
-
"alien property": Foreign
|
|
8
|
+
"alien property": Foreign property
|
|
9
9
|
"illegal aliens": Undocumented immigrants
|
|
10
10
|
"alien labor": Foreign workers
|
|
11
11
|
gypsies: Romanies
|
|
12
12
|
aliens: Noncitizens
|
|
13
|
+
"east indian americans": Indian Americans
|
|
14
|
+
"east indian american": Indian American
|
|
15
|
+
"east indians": Indians (India)
|
|
16
|
+
"east indian": Indian (India)
|
|
17
|
+
"indians of north america": Indigenous peoples of North America
|
|
18
|
+
"indians of south america": Indigenous peoples of South America
|
|
19
|
+
"indians of central america": Indigenous peoples of Central America
|
|
20
|
+
"indians of mexico": Indigenous peoples of Mexico
|
|
21
|
+
"indians of the west indies": Indigenous peoples of the West Indies
|
|
22
|
+
"memphis race riot, memphis, tenn., 1866": Memphis Race Massacre, Memphis, Tenn., 1866
|
|
23
|
+
"east saint louis race riot, east saint louis, ill., 1917": East Saint Louis Race Massacre, East Saint Louis, Ill., 1917
|
|
@@ -856,6 +856,10 @@ muselock:
|
|
|
856
856
|
- Penn Museum Library
|
|
857
857
|
display: Penn Museum Library - Locked Cases (ask staff)
|
|
858
858
|
aeon: true
|
|
859
|
+
musemap:
|
|
860
|
+
specific_location: Penn Museum Library - Maps
|
|
861
|
+
library: Penn Museum Library
|
|
862
|
+
display: Penn Museum Library - Maps
|
|
859
863
|
musemedia:
|
|
860
864
|
specific_location: Penn Museum Library - Media
|
|
861
865
|
library: Penn Museum Library
|
data/lib/pennmarc/version.rb
CHANGED
|
@@ -8,45 +8,35 @@ describe 'PennMARC::HeadingControl' do
|
|
|
8
8
|
describe '.process' do
|
|
9
9
|
context 'with a term for removal' do
|
|
10
10
|
it 'removes the term if found in isolation' do
|
|
11
|
-
|
|
12
|
-
expect(PennMARC::HeadingControl.term_override(values)).to eq []
|
|
11
|
+
expect(PennMARC::HeadingControl.term_override(remove_term)).to be_nil
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
it 'removes the term regardless of case' do
|
|
16
|
-
|
|
17
|
-
expect(PennMARC::HeadingControl.term_override(values)).to eq []
|
|
15
|
+
expect(PennMARC::HeadingControl.term_override(remove_term.downcase)).to be_nil
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
it 'removes the term if it is included as a substring' do
|
|
21
|
-
|
|
22
|
-
expect(PennMARC::HeadingControl.term_override(
|
|
19
|
+
value = "#{remove_term}--History"
|
|
20
|
+
expect(PennMARC::HeadingControl.term_override(value)).to be_nil
|
|
23
21
|
end
|
|
24
22
|
end
|
|
25
23
|
|
|
26
24
|
PennMARC::Mappers.heading_overrides.each do |target, replacement|
|
|
27
25
|
context "with the \"#{target}\" term" do
|
|
28
26
|
it 'replaces the term in isolation' do
|
|
29
|
-
|
|
30
|
-
expect(PennMARC::HeadingControl.term_override(values)).to eq [replacement]
|
|
27
|
+
expect(PennMARC::HeadingControl.term_override(target)).to eq replacement
|
|
31
28
|
end
|
|
32
29
|
|
|
33
30
|
it 'replaces the term when used with other headings' do
|
|
34
|
-
|
|
35
|
-
expect(PennMARC::HeadingControl.term_override(
|
|
31
|
+
value = "#{target}--History"
|
|
32
|
+
expect(PennMARC::HeadingControl.term_override(value)).to eq "#{replacement}--History"
|
|
36
33
|
end
|
|
37
34
|
|
|
38
35
|
it 'replaces the term regardless of case' do
|
|
39
|
-
|
|
40
|
-
expect(PennMARC::HeadingControl.term_override(
|
|
36
|
+
value = "#{target.titleize}--History"
|
|
37
|
+
expect(PennMARC::HeadingControl.term_override(value)).to eq "#{replacement}--History"
|
|
41
38
|
end
|
|
42
39
|
end
|
|
43
40
|
end
|
|
44
|
-
|
|
45
|
-
context 'with a variety of terms' do
|
|
46
|
-
it 'removes and replaces terms as needed' do
|
|
47
|
-
values = [remove_term, replace_term, 'History']
|
|
48
|
-
expect(PennMARC::HeadingControl.term_override(values)).to contain_exactly 'History', replaced_term
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
41
|
end
|
|
52
42
|
end
|
|
@@ -192,6 +192,23 @@ describe 'PennMARC::Creator' do
|
|
|
192
192
|
'Name ends with comma', 'Name with no comma'
|
|
193
193
|
end
|
|
194
194
|
end
|
|
195
|
+
|
|
196
|
+
context 'with university donor/fund provenance' do
|
|
197
|
+
let(:fields) do
|
|
198
|
+
[marc_field(tag: '100', subfields: { a: 'Surname, Name', '0': 'http://cool.uri/12345', d: '1900-2000',
|
|
199
|
+
e: 'author.', '4': 'http://cool.uri/vocabulary/relators/aut' }),
|
|
200
|
+
marc_field(tag: '700', subfields: { a: 'A contributor', e: 'contributor',
|
|
201
|
+
'5': "Does-not-start-with-#{helper::UNIVERSITY_IDENTIFIER}" }),
|
|
202
|
+
marc_field(tag: '700', subfields: { a: 'Important Person Collection', '6': '100', d: '1970-',
|
|
203
|
+
'5': helper::UNIVERSITY_IDENTIFIER }),
|
|
204
|
+
marc_field(tag: '710', subfields: { a: 'Important Person Collection Library', '6': '100', d: '1970-',
|
|
205
|
+
'5': helper::UNIVERSITY_IDENTIFIER })]
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it 'omits any university donor/fund provenance' do
|
|
209
|
+
expect(helper.authors_list(record)).to contain_exactly 'Surname, Name', 'A contributor'
|
|
210
|
+
end
|
|
211
|
+
end
|
|
195
212
|
end
|
|
196
213
|
|
|
197
214
|
describe '.contributors_list' do
|
|
@@ -245,6 +262,21 @@ describe 'PennMARC::Creator' do
|
|
|
245
262
|
['Translator', ['Einstein, Albert 1970-, Translator']]
|
|
246
263
|
end
|
|
247
264
|
end
|
|
265
|
+
|
|
266
|
+
context 'with university donor/fund provenance' do
|
|
267
|
+
let(:fields) do
|
|
268
|
+
[marc_field(tag: '700', subfields: { a: 'Surname, Name', '0': 'http://cool.uri/12345', d: '1900-2000',
|
|
269
|
+
e: 'author.', '4': 'http://cool.uri/vocabulary/relators/aut' }),
|
|
270
|
+
marc_field(tag: '700', subfields: { a: 'Important Person Collection', '6': '100', d: '1970-',
|
|
271
|
+
'5': helper::UNIVERSITY_IDENTIFIER }),
|
|
272
|
+
marc_field(tag: '710', subfields: { a: 'Important Person Collection Library', '6': '100', d: '1970-',
|
|
273
|
+
'5': helper::UNIVERSITY_IDENTIFIER })]
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it 'omits any university donor/fund provenance' do
|
|
277
|
+
expect(helper.contributors_list(record)).to contain_exactly ['Author', ['Surname, Name']]
|
|
278
|
+
end
|
|
279
|
+
end
|
|
248
280
|
end
|
|
249
281
|
|
|
250
282
|
describe '.show_facet_map' do
|
|
@@ -199,6 +199,20 @@ describe 'PennMARC::Subject' do
|
|
|
199
199
|
'Franklin, Benjamin', 'Philadelphia (Pa.)')
|
|
200
200
|
end
|
|
201
201
|
end
|
|
202
|
+
|
|
203
|
+
context 'with a record with fields not subject to overriding' do
|
|
204
|
+
let(:replace_term) { PennMARC::Mappers.heading_overrides.keys[2] }
|
|
205
|
+
let(:fields) do
|
|
206
|
+
[marc_field(tag: '650', indicator2: '0', subfields: { a: replace_term }),
|
|
207
|
+
marc_field(tag: PennMARC::HeadingControl::NO_OVERRIDE_TAGS.sample, indicator2: '0',
|
|
208
|
+
subfields: { a: replace_term })]
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it 'only overrides in the allowed field' do
|
|
212
|
+
replaced_term = PennMARC::HeadingControl.term_override(replace_term)
|
|
213
|
+
expect(values).to contain_exactly(replace_term, replaced_term)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
202
216
|
end
|
|
203
217
|
|
|
204
218
|
describe '.show' do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pennmarc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.4.
|
|
4
|
+
version: 1.4.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Kanning
|
|
@@ -12,7 +12,7 @@ authors:
|
|
|
12
12
|
autorequire:
|
|
13
13
|
bindir: bin
|
|
14
14
|
cert_chain: []
|
|
15
|
-
date: 2026-
|
|
15
|
+
date: 2026-11-11 00:00:00.000000000 Z
|
|
16
16
|
dependencies:
|
|
17
17
|
- !ruby/object:Gem::Dependency
|
|
18
18
|
name: activesupport
|
|
@@ -94,8 +94,8 @@ extra_rdoc_files: []
|
|
|
94
94
|
files:
|
|
95
95
|
- ".gitignore"
|
|
96
96
|
- ".gitlab-ci.yml"
|
|
97
|
+
- ".gitlab/ci-requirements.yml"
|
|
97
98
|
- ".gitleaks.toml"
|
|
98
|
-
- ".pipeline.yml"
|
|
99
99
|
- ".rspec"
|
|
100
100
|
- ".rubocop.yml"
|
|
101
101
|
- ".rubocop_todo.yml"
|
data/.pipeline.yml
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
variables:
|
|
2
|
-
FF_SCRIPT_SECTIONS: "true"
|
|
3
|
-
GIT_CLONE_PATH: "${CI_BUILDS_DIR}/${CI_PROJECT_NAME}/${CI_JOB_ID}"
|
|
4
|
-
|
|
5
|
-
include:
|
|
6
|
-
- component: gitlab.library.upenn.edu/devops/gitlab/components/general/install_hashicorp_vault@~latest
|
|
7
|
-
- component: gitlab.library.upenn.edu/devops/gitlab/components/general/vault_jwt_auth@~latest
|
|
8
|
-
inputs:
|
|
9
|
-
vault_addr: ${VAULT_ADDR}
|
|
10
|
-
vault_auth_path: ${VAULT_AUTH_PATH}
|
|
11
|
-
vault_auth_role: ${VAULT_AUTH_ROLE}
|
|
12
|
-
|
|
13
|
-
- project: "devops/gitlab/ci-templates/ruby"
|
|
14
|
-
ref: "sans-dind"
|
|
15
|
-
file:
|
|
16
|
-
- ".rspec.yml"
|
|
17
|
-
- ".rubocop.yml"
|
|
18
|
-
|
|
19
|
-
stages:
|
|
20
|
-
- test
|
|
21
|
-
- deploy
|
|
22
|
-
|
|
23
|
-
rspec_app_test:
|
|
24
|
-
stage: test
|
|
25
|
-
image: ruby:3.2.2
|
|
26
|
-
before_script:
|
|
27
|
-
- bundle install -j $(nproc)
|
|
28
|
-
extends:
|
|
29
|
-
- .rspec
|
|
30
|
-
tags:
|
|
31
|
-
- build
|
|
32
|
-
|
|
33
|
-
rubocop_app_test:
|
|
34
|
-
stage: test
|
|
35
|
-
image: ruby:3.2.2
|
|
36
|
-
before_script:
|
|
37
|
-
- bundle install -j $(nproc)
|
|
38
|
-
extends:
|
|
39
|
-
- .rubocop
|
|
40
|
-
tags:
|
|
41
|
-
- build
|
|
42
|
-
|
|
43
|
-
# This step was inspired from Gitlab's example of gem publication:
|
|
44
|
-
# https://gitlab.com/gitlab-org/quality/pipeline-common/-/blob/master/ci/gem-release.yml
|
|
45
|
-
gem_publication:
|
|
46
|
-
stage: deploy
|
|
47
|
-
image: ruby:3.2.2
|
|
48
|
-
extends:
|
|
49
|
-
- .vault_jwt_auth
|
|
50
|
-
variables:
|
|
51
|
-
GEMSPEC_FILE: "${CI_PROJECT_NAME}.gemspec"
|
|
52
|
-
before_script:
|
|
53
|
-
- !reference [.install_hashicorp_vault, before_script]
|
|
54
|
-
- !reference [.vault_jwt_auth, before_script]
|
|
55
|
-
script:
|
|
56
|
-
- export GEM_HOST_API_KEY="$(vault kv get -field=rubygems_api_key ${VAULT_KV_ENDPOINT}${ENVIRONMENT})"
|
|
57
|
-
- |
|
|
58
|
-
gem -v
|
|
59
|
-
rm -f ./*.gem
|
|
60
|
-
[ -f "${GEMSPEC_FILE}" ] || (echo "No ${GEMSPEC_FILE} file found!" && exit 1)
|
|
61
|
-
- '[ -n "${GEM_HOST_API_KEY}" ] || (echo "GEM_HOST_API_KEY is not set!" && exit 1)'
|
|
62
|
-
- GEM_FILE=$(echo "${CI_PROJECT_NAME}-${CI_COMMIT_TAG:1}.gem")
|
|
63
|
-
- |
|
|
64
|
-
gem build "${GEMSPEC_FILE}"
|
|
65
|
-
[ -f "${GEM_FILE}" ] || (echo "No ${GEM_FILE} file found!" && exit 1)
|
|
66
|
-
- '[ "${DISABLE_GEM_PUSH}" == "true" ] || gem push "${GEM_FILE}"'
|
|
67
|
-
rules:
|
|
68
|
-
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+(\.[a-zA-Z0-9]+)?$/
|
|
69
|
-
artifacts:
|
|
70
|
-
paths:
|
|
71
|
-
- "*.gem"
|
|
72
|
-
tags:
|
|
73
|
-
- deploy
|