berkeley_library-tind 0.5.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +15 -3
- data/.gitignore +3 -0
- data/.idea/inspectionProfiles/Project_Default.xml +10 -0
- data/.idea/tind.iml +10 -9
- data/.rubocop.yml +1 -1
- data/CHANGES.md +15 -1
- data/README.md +165 -2
- data/berkeley_library-tind.gemspec +2 -2
- data/bin/alma-multiple-tind +50 -0
- data/bin/alma-single-tind +48 -0
- data/bin/save_tind_records +80 -0
- data/bin/tind-marc +73 -0
- data/lib/berkeley_library/tind/export/ods_exporter.rb +4 -6
- data/lib/berkeley_library/tind/mapping/additional_datafield_process.rb +128 -0
- data/lib/berkeley_library/tind/mapping/alma.rb +42 -0
- data/lib/berkeley_library/tind/mapping/alma_base.rb +108 -0
- data/lib/berkeley_library/tind/mapping/alma_multiple_tind.rb +31 -0
- data/lib/berkeley_library/tind/mapping/alma_single_tind.rb +28 -0
- data/lib/berkeley_library/tind/mapping/config.rb +44 -0
- data/lib/berkeley_library/tind/mapping/csv_mapper.rb +35 -0
- data/lib/berkeley_library/tind/mapping/csv_multiple_mapper.rb +41 -0
- data/lib/berkeley_library/tind/mapping/data/one_to_multiple_mapping.csv +4 -0
- data/lib/berkeley_library/tind/mapping/data/one_to_one_mapping.csv +39 -0
- data/lib/berkeley_library/tind/mapping/external_tind_field.rb +103 -0
- data/lib/berkeley_library/tind/mapping/field_catalog.rb +137 -0
- data/lib/berkeley_library/tind/mapping/field_catalog_util.rb +105 -0
- data/lib/berkeley_library/tind/mapping/match_tind_field.rb +77 -0
- data/lib/berkeley_library/tind/mapping/misc.rb +69 -0
- data/lib/berkeley_library/tind/mapping/multiple_rule.rb +36 -0
- data/lib/berkeley_library/tind/mapping/single_rule.rb +149 -0
- data/lib/berkeley_library/tind/mapping/tind_control_subfield.rb +59 -0
- data/lib/berkeley_library/tind/mapping/tind_field.rb +49 -0
- data/lib/berkeley_library/tind/mapping/tind_field_from_leader.rb +27 -0
- data/lib/berkeley_library/tind/mapping/tind_field_from_multiple_map.rb +59 -0
- data/lib/berkeley_library/tind/mapping/tind_field_from_single_map.rb +182 -0
- data/lib/berkeley_library/tind/mapping/tind_field_util.rb +112 -0
- data/lib/berkeley_library/tind/mapping/tind_marc.rb +134 -0
- data/lib/berkeley_library/tind/mapping/tind_record_util.rb +135 -0
- data/lib/berkeley_library/tind/mapping/tind_subfield_util.rb +154 -0
- data/lib/berkeley_library/tind/mapping/util.rb +136 -0
- data/lib/berkeley_library/tind/mapping.rb +1 -0
- data/lib/berkeley_library/tind/module_info.rb +1 -1
- data/spec/berkeley_library/tind/mapping/additional_datafield_process_spec.rb +35 -0
- data/spec/berkeley_library/tind/mapping/alma_base_spec.rb +115 -0
- data/spec/berkeley_library/tind/mapping/alma_multiple_tind_spec.rb +20 -0
- data/spec/berkeley_library/tind/mapping/alma_single_tind_spec.rb +87 -0
- data/spec/berkeley_library/tind/mapping/alma_spec.rb +28 -0
- data/spec/berkeley_library/tind/mapping/config_spec.rb +19 -0
- data/spec/berkeley_library/tind/mapping/csv_mapper_spec.rb +27 -0
- data/spec/berkeley_library/tind/mapping/csv_multiple_mapper_spec.rb +27 -0
- data/spec/berkeley_library/tind/mapping/external_tind_field_spec.rb +45 -0
- data/spec/berkeley_library/tind/mapping/field_catalog_spec.rb +78 -0
- data/spec/berkeley_library/tind/mapping/field_catalog_util_spec.rb +105 -0
- data/spec/berkeley_library/tind/mapping/match_tind_field_spec.rb +24 -0
- data/spec/berkeley_library/tind/mapping/misc_spec.rb +51 -0
- data/spec/berkeley_library/tind/mapping/multiple_rule_spec.rb +44 -0
- data/spec/berkeley_library/tind/mapping/single_rule_spec.rb +52 -0
- data/spec/berkeley_library/tind/mapping/tind_control_subfield_spec.rb +96 -0
- data/spec/berkeley_library/tind/mapping/tind_field_from_leader_spec.rb +21 -0
- data/spec/berkeley_library/tind/mapping/tind_field_from_multiple_map_spec.rb +31 -0
- data/spec/berkeley_library/tind/mapping/tind_field_from_single_map_spec.rb +167 -0
- data/spec/berkeley_library/tind/mapping/tind_field_spec.rb +60 -0
- data/spec/berkeley_library/tind/mapping/tind_field_util_spec.rb +68 -0
- data/spec/berkeley_library/tind/mapping/tind_marc_spec.rb +88 -0
- data/spec/berkeley_library/tind/mapping/tind_record_util_spec.rb +30 -0
- data/spec/berkeley_library/tind/mapping/tind_subfield_util_spec.rb +48 -0
- data/spec/berkeley_library/tind/mapping/util_spec.rb +56 -0
- data/spec/berkeley_library/tind/marc/xml_writer_spec.rb +24 -0
- data/spec/data/api/pre_assigned_response.json +15 -0
- data/spec/data/api/result_file.csv +3 -0
- data/spec/data/api/upload_file.json +1 -0
- data/spec/data/api/upload_response.json +13 -0
- data/spec/data/mapping/991032333019706532-sru.xml +216 -0
- data/spec/data/mapping/one_to_multiple_mapping.csv +4 -0
- data/spec/data/mapping/one_to_one_mapping.csv +39 -0
- data/spec/data/mapping/record.xml +266 -0
- data/spec/data/mapping/record_not_qualified.xml +36 -0
- metadata +89 -54
- data/lib/berkeley_library/util/files.rb +0 -39
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'marc'
|
2
|
+
require 'berkeley_library/tind/mapping/tind_control_subfield'
|
3
|
+
|
4
|
+
module BerkeleyLibrary
|
5
|
+
module TIND
|
6
|
+
require 'marc'
|
7
|
+
module Mapping
|
8
|
+
|
9
|
+
class TindFieldFromLeader
|
10
|
+
include CsvMultipleMapper
|
11
|
+
include Util
|
12
|
+
include TindControlSubfield
|
13
|
+
|
14
|
+
def initialize(record)
|
15
|
+
@leader_value = record.leader
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_datafields
|
19
|
+
leader_rules = rules[Util.tag_symbol('LDR')]
|
20
|
+
return [] unless @leader_value && leader_rules
|
21
|
+
|
22
|
+
extracted_fields_from_leader(leader_rules, @leader_value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'marc'
|
2
|
+
require 'berkeley_library/tind/mapping/tind_control_subfield'
|
3
|
+
|
4
|
+
module BerkeleyLibrary
|
5
|
+
module TIND
|
6
|
+
module Mapping
|
7
|
+
|
8
|
+
class TindFieldFromMultipleMap
|
9
|
+
include CsvMultipleMapper
|
10
|
+
include Util
|
11
|
+
include TindControlSubfield
|
12
|
+
|
13
|
+
def initialize(controlfield, current_datafields)
|
14
|
+
@from_controlfield = controlfield
|
15
|
+
@current_tags = current_datafields.map(&:tag)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_datafields
|
19
|
+
datafields = []
|
20
|
+
control_rules = rules_on_controldatafield
|
21
|
+
|
22
|
+
if control_rules
|
23
|
+
control_rules.each do |rule|
|
24
|
+
df = to_datafield(rule)
|
25
|
+
datafields << df if df
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
datafields
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# one control field may have multiple rules
|
35
|
+
def rules_on_controldatafield
|
36
|
+
tag = @from_controlfield.tag
|
37
|
+
sym = Util.tag_symbol(tag)
|
38
|
+
rules[sym]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Check mapped current datafields has the pre-existed tag defined in the row (rule) of csv file
|
42
|
+
def pre_exsited_tag?(rule)
|
43
|
+
@current_tags.include? rule.pre_existed_tag.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
# get a datafield on a rule (row in csv file)
|
47
|
+
def to_datafield(rule)
|
48
|
+
return nil if pre_exsited_tag?(rule)
|
49
|
+
|
50
|
+
to_value = extract_value(rule, @from_controlfield.value)
|
51
|
+
return nil unless to_value
|
52
|
+
|
53
|
+
extracted_field(rule, to_value)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'marc'
|
2
|
+
require 'berkeley_library/tind/mapping/util'
|
3
|
+
require 'berkeley_library/tind/mapping/tind_subfield_util'
|
4
|
+
|
5
|
+
# 1. datafield could be a regular alma field
|
6
|
+
# 1) data_fields_normal - using @single_rule_hash from SingleRule
|
7
|
+
# 2) data_fields_with_pre_existed_field - using @single_rule_hash from SingleRule
|
8
|
+
# 3) data_fields_with_pre_existed_subfield - using @single_rule_subfield_excluded_hash from SingleRule
|
9
|
+
|
10
|
+
# 2. data field could be an 880 alma field , below types are definded based on the tag from subfield6
|
11
|
+
# 1) data_fields_normal - using @single_rule_hash from SingleRule
|
12
|
+
# 2) data_fields_with_pre_existed_field - using @single_rule_hash from SingleRule
|
13
|
+
# 3) data_fields_with_pre_existed_subfield - using @single_rule_subfield_excluded_hash from SingleRule
|
14
|
+
|
15
|
+
# 3. map_to_tag, indicator are from mapping rule for output tindfield
|
16
|
+
# 4. subfileds are re-mapped, or combined, used as subfields for output tindfield
|
17
|
+
|
18
|
+
module BerkeleyLibrary
|
19
|
+
module TIND
|
20
|
+
module Mapping
|
21
|
+
|
22
|
+
class TindFieldFromSingleMap
|
23
|
+
include CsvMapper
|
24
|
+
include Util
|
25
|
+
include TindSubfieldUtil
|
26
|
+
include Misc
|
27
|
+
|
28
|
+
# excluding_subfield = false: mapping by rule.single_rule_hash
|
29
|
+
# excluding_subfield = true: mapping by rule.single_rule_subfield_excluded_hash
|
30
|
+
def initialize(datafield, excluding_subfield)
|
31
|
+
@from_datafield = datafield
|
32
|
+
@excluding_subfield = excluding_subfield
|
33
|
+
|
34
|
+
@is_880_field = is_880_field?(datafield)
|
35
|
+
|
36
|
+
@mapping_rule = rule
|
37
|
+
@map_to_tag = nil
|
38
|
+
@indicator = nil
|
39
|
+
@single_mapping = nil
|
40
|
+
@ready_to_mapping = ready_to_mapping?
|
41
|
+
|
42
|
+
@codes = subfield_codes(@from_datafield)
|
43
|
+
@to_subfields = all_subfields
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_datafield
|
47
|
+
return nil unless mapped?
|
48
|
+
|
49
|
+
tindfield = Util.datafield(@map_to_tag, @indicator, @to_subfields)
|
50
|
+
@is_880_field ? reversed_880_field(tindfield) : tindfield
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# A referred tag from 880 subfield6 may not have a rule
|
56
|
+
# For example: 880 subfild6 pass in a value in wrong format
|
57
|
+
# In above case, rule is nil
|
58
|
+
# Get mapping parameters from rule when having a rule
|
59
|
+
def ready_to_mapping?
|
60
|
+
return false unless @mapping_rule
|
61
|
+
|
62
|
+
@map_to_tag = @mapping_rule.tag_destination
|
63
|
+
@indicator = @mapping_rule.indicator
|
64
|
+
@single_mapping = @excluding_subfield ? @mapping_rule.single_rule_subfield_excluded_hash : @mapping_rule.single_rule_hash
|
65
|
+
|
66
|
+
return false unless @map_to_tag && @indicator && !@single_mapping.empty?
|
67
|
+
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def mapped?
|
72
|
+
!@to_subfields.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
# tag - regular alma field
|
76
|
+
# referred tag - got tag from subfield6 value of a 880 field
|
77
|
+
# nil rule caused by nil referred tag - eg. 880 subfild6 pass in a value in wrong format
|
78
|
+
def rule
|
79
|
+
tag = origin_mapping_tag(@from_datafield)
|
80
|
+
return nil unless tag
|
81
|
+
|
82
|
+
rules[Util.tag_symbol(tag)]
|
83
|
+
end
|
84
|
+
|
85
|
+
def all_subfields
|
86
|
+
return [] unless @ready_to_mapping
|
87
|
+
|
88
|
+
subfields = subfields_from_single_map + subfields_from_combined_map
|
89
|
+
codes = @mapping_rule.subfields_order || @codes
|
90
|
+
|
91
|
+
return subfields unless subfields.length > 1
|
92
|
+
|
93
|
+
Util.order_subfields(subfields, codes)
|
94
|
+
end
|
95
|
+
|
96
|
+
def subfield_codes(f)
|
97
|
+
f.subfields.map(&:code).uniq
|
98
|
+
end
|
99
|
+
|
100
|
+
# 1.subfields mapped with single rule, mapping one subfield to another subfield
|
101
|
+
# 2. one subfield is mapped to one subfield
|
102
|
+
# 3. When mutiple subfields with the same name found in an orignal field,
|
103
|
+
# they will be mapped one by one
|
104
|
+
def subfields_from_single_map
|
105
|
+
return [] if @single_mapping.empty?
|
106
|
+
|
107
|
+
mapped_subfields = []
|
108
|
+
@single_mapping.each do |from, to|
|
109
|
+
subfields = subfields_from_to(@from_datafield, from, to)
|
110
|
+
mapped_subfields.concat(subfields)
|
111
|
+
end
|
112
|
+
mapped_subfields
|
113
|
+
end
|
114
|
+
|
115
|
+
# return all subfields mapped with diferent combined rules - different destination subfield names
|
116
|
+
# mapped with all combined rules, exmaple: [[["a,b,c,d", "b", "--"],["o,p,q", "b", ""]],[["x,y,z", "a", "--"]]]
|
117
|
+
# mapping using above example rules will return two subfield: $b, $a
|
118
|
+
def subfields_from_combined_map
|
119
|
+
all_rules = @mapping_rule.combined_rules
|
120
|
+
return [] if all_rules.empty?
|
121
|
+
|
122
|
+
mapped_subfields = []
|
123
|
+
all_rules.each do |rules|
|
124
|
+
subfield = subfield_on_same_tosubfieldname(rules)
|
125
|
+
mapped_subfields.push(subfield) if subfield
|
126
|
+
end
|
127
|
+
mapped_subfields
|
128
|
+
end
|
129
|
+
|
130
|
+
# create one subfield with a desintaion subfield name
|
131
|
+
# input array of rules example: [["a,b,c,d", "b", "--"],["o,p,q", "b", ""]] -- all rules with the same destination subfield name "b"
|
132
|
+
# get a subfield$b with a concatenated value
|
133
|
+
def subfield_on_same_tosubfieldname(rules)
|
134
|
+
return nil if rules.empty?
|
135
|
+
|
136
|
+
val = subfield_value_on_rules(rules)
|
137
|
+
return nil if val.strip.empty?
|
138
|
+
|
139
|
+
subfield_name_to = rules[0][1]
|
140
|
+
Util.subfield(subfield_name_to, Util.remove_extra_symbol(rules, val))
|
141
|
+
end
|
142
|
+
|
143
|
+
# input an array of rules, example: [["a,b,c,d", "b", "--"],["o,p,q", "b", ""]]
|
144
|
+
# Theese rules have the same destination subfield name, for example "b" in above example
|
145
|
+
# get a value concatenated with values mapped using different rules
|
146
|
+
def subfield_value_on_rules(rules)
|
147
|
+
val = ''
|
148
|
+
rules.each { |rule| val << subfield_value_on_rule(rule) }
|
149
|
+
val
|
150
|
+
end
|
151
|
+
|
152
|
+
# input a rule (for example ["a,b,c,d", "b", "--"]),
|
153
|
+
# get a combined value of subfield a,b,c,d concatenated by " -- " as above example
|
154
|
+
# One subfield names may occurs mutiple times in a an orignal field
|
155
|
+
def subfield_value_on_rule(rule)
|
156
|
+
subfield_names_from = rule[0].strip.split(',')
|
157
|
+
symbol = Util.concatenation_symbol(rule[2])
|
158
|
+
val = ''
|
159
|
+
subfield_names_from.each do |subfield_name|
|
160
|
+
sub_val = combined_subfield_value(@from_datafield, subfield_name, symbol)
|
161
|
+
val << sub_val
|
162
|
+
end
|
163
|
+
val
|
164
|
+
end
|
165
|
+
|
166
|
+
# 880 datafield: reverse tag from 'to_tag' defined mapping rule to '880'
|
167
|
+
def reversed_880_field(f)
|
168
|
+
update_datafield6(f)
|
169
|
+
f.tag = '880'
|
170
|
+
f
|
171
|
+
end
|
172
|
+
|
173
|
+
# update subfield6 tag with destination tag from the rule
|
174
|
+
# since an origin tag may have been mapped a different tag - destination tag
|
175
|
+
def update_datafield6(f) # need test
|
176
|
+
f['6'].sub!(@mapping_rule.tag_origin, @mapping_rule.tag_destination)
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'marc'
|
2
|
+
|
3
|
+
module BerkeleyLibrary
|
4
|
+
module TIND
|
5
|
+
module Mapping
|
6
|
+
module TindFieldUtil
|
7
|
+
|
8
|
+
# tag - regular alma field
|
9
|
+
# referred tag - got tag from subfield6 value of a 880 field
|
10
|
+
# nil rule caused by nil referred tag - eg. 880 subfild6 has a value in wrong format
|
11
|
+
def rule(field)
|
12
|
+
tag = origin_mapping_tag(field)
|
13
|
+
return nil unless tag
|
14
|
+
|
15
|
+
rules[Util.tag_symbol(tag)]
|
16
|
+
end
|
17
|
+
|
18
|
+
def tindfield_existed?(field, fields)
|
19
|
+
return false unless field_has_rule?(field)
|
20
|
+
|
21
|
+
field_rule = rule(field)
|
22
|
+
mapping_to_tag = field_rule.pre_existed_tag
|
23
|
+
return false unless mapping_to_tag
|
24
|
+
|
25
|
+
map_to_tag_existed_in_fields?(field, fields, mapping_to_tag)
|
26
|
+
end
|
27
|
+
|
28
|
+
# To check TIND datafield and the specific subfield from rule existed
|
29
|
+
def tindfield_subfield_existed?(field, fields)
|
30
|
+
return false unless field_has_rule?(field)
|
31
|
+
|
32
|
+
field_rule = rule(field)
|
33
|
+
return false unless pre_existed_tag_subfield_in_rule?(field_rule)
|
34
|
+
|
35
|
+
tag_subfield = field_rule.pre_existed_tag_subfield
|
36
|
+
mapping_to_tag = tag_subfield[0]
|
37
|
+
return false unless map_to_tag_existed_in_fields?(field, fields, mapping_to_tag)
|
38
|
+
|
39
|
+
existed_datafield = field_pre_existed(mapping_to_tag, field, fields)
|
40
|
+
return false unless existed_datafield
|
41
|
+
|
42
|
+
subfield_name = tag_subfield[1]
|
43
|
+
existed_datafield[subfield_name] ? true : false
|
44
|
+
end
|
45
|
+
|
46
|
+
def field_880_on_subfield6_tag(tag, fields)
|
47
|
+
datafield_on_tag(tag, fields) { |f| referred_tag(f) == tag }
|
48
|
+
end
|
49
|
+
|
50
|
+
def field_on_tag(tag, fields)
|
51
|
+
datafield_on_tag(tag, fields) { |f| f.tag == tag }
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def field_has_rule?(field)
|
57
|
+
field_rule = rule(field)
|
58
|
+
return false unless field_rule
|
59
|
+
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def pre_existed_tag_subfield_in_rule?(rule)
|
64
|
+
tag_subfield = rule.pre_existed_tag_subfield
|
65
|
+
return false unless tag_subfield
|
66
|
+
|
67
|
+
return false unless tag_subfield.length == 2
|
68
|
+
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def map_to_tag_existed_in_fields?(field, fields, mapping_to_tag)
|
73
|
+
existed_tags = if is_880_field?(field)
|
74
|
+
tags_from_fields(fields) { |f| tag_from_880_subfield6(f) }
|
75
|
+
else
|
76
|
+
tags_from_fields(fields, &:tag)
|
77
|
+
end
|
78
|
+
|
79
|
+
existed_tags.include? mapping_to_tag
|
80
|
+
end
|
81
|
+
|
82
|
+
# field, fields be both regular fields
|
83
|
+
# or field, fields be both 880 fields
|
84
|
+
# since a field may mapped to another one in TIND, mapping_to_tag is not always the same as field.tag
|
85
|
+
def field_pre_existed(mapping_to_tag, field, fields)
|
86
|
+
if is_880_field?(field)
|
87
|
+
field_880_on_subfield6_tag(mapping_to_tag,
|
88
|
+
fields)
|
89
|
+
else
|
90
|
+
field_on_tag(mapping_to_tag, fields)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def datafield_on_tag(tag, fields)
|
95
|
+
fields.find do |f|
|
96
|
+
yield(f, tag)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def tags_from_fields(fields, &block)
|
101
|
+
fields.map(&block)
|
102
|
+
end
|
103
|
+
|
104
|
+
def tag_from_880_subfield6(field)
|
105
|
+
field['6'].split('-')[0]
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'marc'
|
2
|
+
|
3
|
+
module BerkeleyLibrary
|
4
|
+
module TIND
|
5
|
+
module Mapping
|
6
|
+
class TindMarc
|
7
|
+
include CsvMapper
|
8
|
+
include Util
|
9
|
+
include TindSubfieldUtil
|
10
|
+
include Misc
|
11
|
+
include TindFieldUtil
|
12
|
+
include AdditionalDatafieldProcess
|
13
|
+
include BerkeleyLibrary::Logging
|
14
|
+
include MatchTindField
|
15
|
+
|
16
|
+
attr_accessor :source_marc_record
|
17
|
+
attr_writer :tind_external_datafields
|
18
|
+
attr_reader :field_catalog
|
19
|
+
attr_reader :mms_id
|
20
|
+
|
21
|
+
# input an alma record
|
22
|
+
def initialize(record)
|
23
|
+
@source_marc_record = record
|
24
|
+
@field_catalog = DataFieldsCatalog.new(@source_marc_record)
|
25
|
+
@tind_external_datafields = []
|
26
|
+
end
|
27
|
+
|
28
|
+
# return mapped tind datafields
|
29
|
+
# keep the order of different mapping
|
30
|
+
def tindfields
|
31
|
+
fields = []
|
32
|
+
fields.concat tindfields_group
|
33
|
+
fields.concat tindfields_group_880
|
34
|
+
fields
|
35
|
+
end
|
36
|
+
|
37
|
+
# return a TIND Marc record
|
38
|
+
# add external datafields
|
39
|
+
# flag to do additional TIND datafield process before generating a TIND Marc record
|
40
|
+
def tind_record
|
41
|
+
fields = tindfields
|
42
|
+
fields.concat @tind_external_datafields
|
43
|
+
more_process(fields)
|
44
|
+
record = ::MARC::Record.new
|
45
|
+
fields.each { |f| record.append(f) }
|
46
|
+
record
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# return mapped tind datafields
|
52
|
+
# keep the order of different mapping
|
53
|
+
def tindfields_group
|
54
|
+
fields_from_normal = tindfields_from_normal(@field_catalog.data_fields_group[:normal])
|
55
|
+
fields_from_leader = TindFieldFromLeader.new(@source_marc_record).to_datafields
|
56
|
+
|
57
|
+
temp_fields = fields_from_normal.concat fields_from_leader
|
58
|
+
temp_fields.concat tindfields_from_control(temp_fields)
|
59
|
+
temp_fields.concat tindfields_with_pre_existed_field(@field_catalog.data_fields_group[:pre_tag], temp_fields)
|
60
|
+
temp_fields.concat tindfields_with_pre_existed_subfield(@field_catalog.data_fields_group[:pre_tag_subfield], temp_fields)
|
61
|
+
|
62
|
+
temp_fields
|
63
|
+
end
|
64
|
+
|
65
|
+
# return mapped tind 880 datafields
|
66
|
+
# keep the order of different mapping
|
67
|
+
def tindfields_group_880
|
68
|
+
fields = []
|
69
|
+
fields.concat tindfields_from_normal(@field_catalog.data_fields_880_group[:normal])
|
70
|
+
fields.concat tindfields_with_pre_existed_field(@field_catalog.data_fields_880_group[:pre_tag], fields)
|
71
|
+
fields.concat tindfields_with_pre_existed_subfield(@field_catalog.data_fields_880_group[:pre_tag_subfield], fields)
|
72
|
+
fields.concat @field_catalog.data_fields_880_00
|
73
|
+
fields
|
74
|
+
end
|
75
|
+
|
76
|
+
# # Return TIND datafields mapped in a normal way
|
77
|
+
# # Normal way mapping: one regular datafield is mapped one TIND datafield
|
78
|
+
def tindfields_from_normal(alma_fields)
|
79
|
+
new_fls = []
|
80
|
+
alma_fields.each do |f|
|
81
|
+
add_tindfield(new_fls, f, excluding_subfield: false)
|
82
|
+
end
|
83
|
+
new_fls
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return TIND datafield mapped from a control datafield
|
87
|
+
# One control datafield could be mapped to multiple TIND datafields
|
88
|
+
def tindfields_from_control(currentfields)
|
89
|
+
new_fls = []
|
90
|
+
@field_catalog.control_fields.each do |f|
|
91
|
+
add_tindcontrolfield(new_fls, f, currentfields)
|
92
|
+
end
|
93
|
+
new_fls
|
94
|
+
end
|
95
|
+
|
96
|
+
# Return TIND datafields if no pre_existed field
|
97
|
+
def tindfields_with_pre_existed_field(alma_fields, currentfields)
|
98
|
+
new_fls = []
|
99
|
+
alma_fields.each do |f|
|
100
|
+
add_tindfield(new_fls, f, excluding_subfield: false) unless tindfield_existed?(f, currentfields)
|
101
|
+
end
|
102
|
+
new_fls
|
103
|
+
end
|
104
|
+
|
105
|
+
# Return TIND datafields considering excluding pre_existing subfields
|
106
|
+
def tindfields_with_pre_existed_subfield(alma_fields, currentfields)
|
107
|
+
new_fls = []
|
108
|
+
alma_fields.each do |f|
|
109
|
+
excluding_subfield = tindfield_subfield_existed?(f, currentfields)
|
110
|
+
add_tindfield(new_fls, f, excluding_subfield: excluding_subfield)
|
111
|
+
end
|
112
|
+
new_fls
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_tindfield(fls, f, excluding_subfield: false)
|
116
|
+
tindfield = TindFieldFromSingleMap.new(f, excluding_subfield).to_datafield
|
117
|
+
fls << tindfield if tindfield
|
118
|
+
end
|
119
|
+
|
120
|
+
def add_tindcontrolfield(fls, f, currentfields)
|
121
|
+
fls.concat TindFieldFromMultipleMap.new(f, currentfields).to_datafields
|
122
|
+
end
|
123
|
+
|
124
|
+
# Additional processes - run in a sequence
|
125
|
+
def more_process(fields)
|
126
|
+
remove_repeats(fields)
|
127
|
+
clean_subfields(fields)
|
128
|
+
un_matched_fields_880(fields, @field_catalog.mms_id)
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'marc'
|
2
|
+
|
3
|
+
module BerkeleyLibrary
|
4
|
+
module TIND
|
5
|
+
module Mapping
|
6
|
+
module TindRecordUtil
|
7
|
+
|
8
|
+
class << self
|
9
|
+
include TindRecordUtil
|
10
|
+
end
|
11
|
+
|
12
|
+
# 1) tag_subfield_hash: a hash to add a new, or update an existing subfield to a field in a TIND Marc record
|
13
|
+
# If a subfield existed, it will replace the subfield, otherwise, add a new subfield
|
14
|
+
# when 'a' => nil, it will skip add/update to subfield,
|
15
|
+
# It can be useed in a case: only need to add/update subfields of specific records in a collection
|
16
|
+
# This is an example: tag_subfield_hash = { '245' => { 'b' => 'subtitle', 'a' => 'title' }, '336' => { 'a' => nil } }
|
17
|
+
# 2) fields_removal_list: an array including fields' informat: tag, indicators - "[tag, inicator1, indicator2]".
|
18
|
+
# This list is to remove fields in a record.
|
19
|
+
# An example fields_removal_list = [%w[856 4 1], %w[041 _ _]].
|
20
|
+
# '_' means an empty indicator ''
|
21
|
+
# 3) How to use it:
|
22
|
+
# a. add/update subfields of existed fields in record: TindRecordUtil.update_record(record, tag_subfield_hash)
|
23
|
+
# b. remove a list of fields in the record: TindRecordUtil.update_record(record, nil, fields_removal_list)
|
24
|
+
# c. both a. and b. : TindRecordUtil.update_record(record, tag_subfield_hash, fields_removal_list)
|
25
|
+
def update_record(record, tag_subfield_hash, fields_removal_list = nil)
|
26
|
+
return record unless valid_hash?(tag_subfield_hash) || valid_hash?(fields_removal_list)
|
27
|
+
|
28
|
+
fields = record.fields
|
29
|
+
final_fields = tag_subfield_hash ? subfields_to_existed_fields(fields, tag_subfield_hash) : fields
|
30
|
+
remove_fields(final_fields, fields_removal_list) if fields_removal_list
|
31
|
+
new_record(final_fields)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def subfields_to_existed_fields(fields, tag_subfield_hash)
|
37
|
+
updated_fields = []
|
38
|
+
tags = tag_subfield_hash.keys
|
39
|
+
fields.each do |field|
|
40
|
+
tag = field.tag
|
41
|
+
need_change_subfield = tags.include? tag
|
42
|
+
updated_fields << (need_change_subfield ? field_changed_subfields(field, tag_subfield_hash[tag]) : field)
|
43
|
+
end
|
44
|
+
fields
|
45
|
+
end
|
46
|
+
|
47
|
+
# example: fields_removal_list = [%w[856 4 1]]
|
48
|
+
def remove_fields(fields, fields_removal_list)
|
49
|
+
fields.reject! { |f| field_in_removal_list?(f, fields_removal_list) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def new_record(fields)
|
53
|
+
record = ::MARC::Record.new
|
54
|
+
fields.each { |f| record.append(f) }
|
55
|
+
record
|
56
|
+
end
|
57
|
+
|
58
|
+
# subfield_hash example { 'b' => 'subtitle', 'a' => 'title' }
|
59
|
+
def field_changed_subfields(field, subfield_hash)
|
60
|
+
tag = field.tag
|
61
|
+
indicators = [field.indicator1, field.indicator2]
|
62
|
+
subfields = changed_subfields(field, subfield_hash)
|
63
|
+
new_datafield(tag, indicators, subfields)
|
64
|
+
end
|
65
|
+
|
66
|
+
# example subfield_hash = { 'p' => 'subtitle'}
|
67
|
+
def changed_subfields(field, subfield_hash)
|
68
|
+
subfields = field.subfields
|
69
|
+
codes = subfields.map(&:code)
|
70
|
+
warning_duplicated_subfield_code(codes)
|
71
|
+
|
72
|
+
keys = subfield_hash.keys
|
73
|
+
|
74
|
+
keys_no_related_codes = keys - codes
|
75
|
+
keys_with_related_codes = keys - keys_no_related_codes
|
76
|
+
|
77
|
+
updated_subfields(subfields, keys_with_related_codes, subfield_hash)
|
78
|
+
|
79
|
+
subfields.concat new_subfields(field, keys_no_related_codes, subfield_hash)
|
80
|
+
subfields
|
81
|
+
end
|
82
|
+
|
83
|
+
# example subfield_hash = { 'p' => 'subtitle'}
|
84
|
+
def updated_subfields(subfields, existed_codes, subfield_hash)
|
85
|
+
subfields.each do |subfield|
|
86
|
+
code = subfield.code
|
87
|
+
next unless existed_codes.include? code
|
88
|
+
|
89
|
+
val = subfield_hash[code]
|
90
|
+
next unless val
|
91
|
+
|
92
|
+
subfield.value = val
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def new_subfields(_field, new_codes, subfield_hash)
|
97
|
+
subfields = []
|
98
|
+
subfield_hash.each do |key, val|
|
99
|
+
next unless val
|
100
|
+
|
101
|
+
subfields << ::MARC::Subfield.new(key, val) if new_codes.include? key
|
102
|
+
end
|
103
|
+
subfields
|
104
|
+
end
|
105
|
+
|
106
|
+
def field_in_removal_list?(f, fields_removal_list)
|
107
|
+
ls = [f.tag, clr(f.indicator1), clr(f.indicator2)]
|
108
|
+
fields_removal_list.include? ls
|
109
|
+
end
|
110
|
+
|
111
|
+
def clr(str)
|
112
|
+
str.strip.empty? ? '_' : str.strip
|
113
|
+
end
|
114
|
+
|
115
|
+
def new_datafield(tag, indicator, subfields)
|
116
|
+
datafield = ::MARC::DataField.new(tag, indicator[0], indicator[1])
|
117
|
+
subfields.each { |sf| datafield.append(sf) }
|
118
|
+
datafield
|
119
|
+
end
|
120
|
+
|
121
|
+
# suppose there are no duplicated subfield codes in a field
|
122
|
+
# giving warning for investigation if finding any dublicated subfields in a field
|
123
|
+
def warning_duplicated_subfield_code(codes)
|
124
|
+
duplicated_codes = codes.select { |code| codes.count(code) > 1 }.uniq
|
125
|
+
puts "Warning: duplicated subfields #{duplicated_codes.join(' ; ')}" unless duplicated_codes.empty?
|
126
|
+
end
|
127
|
+
|
128
|
+
def valid_hash?(hash)
|
129
|
+
hash && !hash.empty?
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|