berkeley_library-tind 0.5.1 → 0.6.0

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +15 -3
  3. data/.gitignore +3 -0
  4. data/.idea/inspectionProfiles/Project_Default.xml +10 -0
  5. data/.idea/tind.iml +4 -3
  6. data/CHANGES.md +6 -0
  7. data/README.md +121 -2
  8. data/berkeley_library-tind.gemspec +1 -0
  9. data/bin/alma-multiple-tind +50 -0
  10. data/bin/alma-single-tind +48 -0
  11. data/bin/save_tind_records +80 -0
  12. data/bin/tind-marc +73 -0
  13. data/lib/berkeley_library/tind/mapping/additional_datafield_process.rb +128 -0
  14. data/lib/berkeley_library/tind/mapping/alma.rb +42 -0
  15. data/lib/berkeley_library/tind/mapping/alma_base.rb +101 -0
  16. data/lib/berkeley_library/tind/mapping/alma_multiple_tind.rb +31 -0
  17. data/lib/berkeley_library/tind/mapping/alma_single_tind.rb +28 -0
  18. data/lib/berkeley_library/tind/mapping/config.rb +44 -0
  19. data/lib/berkeley_library/tind/mapping/csv_mapper.rb +35 -0
  20. data/lib/berkeley_library/tind/mapping/csv_multiple_mapper.rb +41 -0
  21. data/lib/berkeley_library/tind/mapping/data/one_to_multiple_mapping.csv +4 -0
  22. data/lib/berkeley_library/tind/mapping/data/one_to_one_mapping.csv +39 -0
  23. data/lib/berkeley_library/tind/mapping/external_tind_field.rb +103 -0
  24. data/lib/berkeley_library/tind/mapping/field_catalog.rb +146 -0
  25. data/lib/berkeley_library/tind/mapping/field_catalog_util.rb +59 -0
  26. data/lib/berkeley_library/tind/mapping/match_tind_field.rb +77 -0
  27. data/lib/berkeley_library/tind/mapping/misc.rb +69 -0
  28. data/lib/berkeley_library/tind/mapping/multiple_rule.rb +36 -0
  29. data/lib/berkeley_library/tind/mapping/single_rule.rb +143 -0
  30. data/lib/berkeley_library/tind/mapping/tind_control_subfield.rb +59 -0
  31. data/lib/berkeley_library/tind/mapping/tind_field.rb +49 -0
  32. data/lib/berkeley_library/tind/mapping/tind_field_from_leader.rb +27 -0
  33. data/lib/berkeley_library/tind/mapping/tind_field_from_multiple_map.rb +59 -0
  34. data/lib/berkeley_library/tind/mapping/tind_field_from_single_map.rb +170 -0
  35. data/lib/berkeley_library/tind/mapping/tind_field_util.rb +112 -0
  36. data/lib/berkeley_library/tind/mapping/tind_marc.rb +134 -0
  37. data/lib/berkeley_library/tind/mapping/tind_subfield_util.rb +154 -0
  38. data/lib/berkeley_library/tind/mapping/util.rb +117 -0
  39. data/lib/berkeley_library/tind/mapping.rb +1 -0
  40. data/lib/berkeley_library/tind/module_info.rb +1 -1
  41. data/lib/berkeley_library/util/files.rb +1 -2
  42. data/spec/berkeley_library/tind/mapping/additional_datafield_process_spec.rb +35 -0
  43. data/spec/berkeley_library/tind/mapping/alma_base_spec.rb +115 -0
  44. data/spec/berkeley_library/tind/mapping/alma_multiple_tind_spec.rb +20 -0
  45. data/spec/berkeley_library/tind/mapping/alma_single_tind_spec.rb +87 -0
  46. data/spec/berkeley_library/tind/mapping/alma_spec.rb +28 -0
  47. data/spec/berkeley_library/tind/mapping/config_spec.rb +19 -0
  48. data/spec/berkeley_library/tind/mapping/csv_mapper_spec.rb +27 -0
  49. data/spec/berkeley_library/tind/mapping/csv_multiple_mapper_spec.rb +27 -0
  50. data/spec/berkeley_library/tind/mapping/external_tind_field_spec.rb +45 -0
  51. data/spec/berkeley_library/tind/mapping/field_catalog_spec.rb +78 -0
  52. data/spec/berkeley_library/tind/mapping/field_catalog_util_spec.rb +57 -0
  53. data/spec/berkeley_library/tind/mapping/match_tind_field_spec.rb +25 -0
  54. data/spec/berkeley_library/tind/mapping/misc_spec.rb +51 -0
  55. data/spec/berkeley_library/tind/mapping/multiple_rule_spec.rb +44 -0
  56. data/spec/berkeley_library/tind/mapping/single_rule_spec.rb +52 -0
  57. data/spec/berkeley_library/tind/mapping/tind_control_subfield_spec.rb +96 -0
  58. data/spec/berkeley_library/tind/mapping/tind_field_from_leader_spec.rb +21 -0
  59. data/spec/berkeley_library/tind/mapping/tind_field_from_multiple_map_spec.rb +31 -0
  60. data/spec/berkeley_library/tind/mapping/tind_field_from_single_map_spec.rb +150 -0
  61. data/spec/berkeley_library/tind/mapping/tind_field_spec.rb +60 -0
  62. data/spec/berkeley_library/tind/mapping/tind_field_util_spec.rb +68 -0
  63. data/spec/berkeley_library/tind/mapping/tind_marc_spec.rb +88 -0
  64. data/spec/berkeley_library/tind/mapping/tind_subfield_util_spec.rb +48 -0
  65. data/spec/berkeley_library/tind/mapping/util_spec.rb +56 -0
  66. data/spec/berkeley_library/tind/marc/xml_writer_spec.rb +24 -0
  67. data/spec/data/mapping/991032333019706532-sru.xml +216 -0
  68. data/spec/data/mapping/one_to_multiple_mapping.csv +4 -0
  69. data/spec/data/mapping/one_to_one_mapping.csv +39 -0
  70. data/spec/data/mapping/record.xml +263 -0
  71. data/spec/data/mapping/record_not_qualified.xml +36 -0
  72. metadata +105 -2
@@ -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,154 @@
1
+ require 'marc'
2
+
3
+ module BerkeleyLibrary
4
+ module TIND
5
+ module Mapping
6
+ module TindSubfieldUtil
7
+
8
+ def clean_subfield(subfield)
9
+ new_value = clr_value(subfield.value)
10
+ subfield.value = new_value
11
+ end
12
+
13
+ def fields_880_subfield6(datafields_880)
14
+ formated_subfield6_value_arr(fields_by(datafields_880) { |f| is_880_field?(f) })
15
+ end
16
+
17
+ # From all subfield 6 gotten for repeated fields - with the same tag
18
+ # return the first subfield 6
19
+ def the_first_subfield6(fields)
20
+ values = subfield_6_values(fields)
21
+ return nil if values.empty?
22
+
23
+ # keep it here, in case needed in future: this can make sure
24
+ # 880 and regular fields having matching sequence number
25
+ # subfield6_with_small_no(values)
26
+
27
+ # new implementation: keep the first subfield 6 value
28
+ logger.warn("#{f[0].tag} have multiple datafields with multiple subfield 6, the first subfield 6 is kept") if values.length > 1
29
+ Util.subfield('6', values[0])
30
+ end
31
+
32
+ private
33
+
34
+ def subfield6?(f)
35
+ !f['6'].nil?
36
+ end
37
+
38
+ # f.tag can be a string or integer
39
+ def is_880_field?(f)
40
+ f.tag.to_s == '880'
41
+ end
42
+
43
+ # return subfield6 value formated the same way for both 880 and regular datafields
44
+ def formated_subfield6_value(f)
45
+ is_880_field?(f) ? formated_subfield6_from_880(f) : formated_subfield6_from_regular_field(f)
46
+ end
47
+
48
+ def fields_by(fields, &block)
49
+ fields.select(&block)
50
+ end
51
+
52
+ # return array of formated subfield6 values on inputted datafields
53
+ def formated_subfield6_value_arr(fields_source)
54
+ # fields_source = is_880_field ? fields_880(datafields) : fields_regular(datafields)
55
+ ls = []
56
+ fields_source.map do |f|
57
+ next unless subfield6?(f)
58
+
59
+ ls << formated_subfield6_value(f)
60
+ end
61
+ ls.compact
62
+ end
63
+
64
+ # return formated field6 for a regular datafield
65
+ # tag:246, subfields: 880-02 => 880-246-02
66
+ def formated_subfield6_from_regular_field(f)
67
+ return nil unless f['6']
68
+
69
+ tag = f.tag
70
+ arr = f['6'].strip.split('-')
71
+ "#{arr[0]}-#{tag}-#{arr[1]}"
72
+ end
73
+
74
+ # return formated subfield6 for a 880 datafield
75
+ # tag: 880, subfield6: 245-01/$1 => 880-245-01
76
+ def formated_subfield6_from_880(f)
77
+ return nil unless f['6']
78
+
79
+ "#{f.tag}-#{f['6'].strip[0, 6].strip}"
80
+ end
81
+
82
+ # return subfields of specific code
83
+ # Datafield returned from Alma may have mutiple subfields with the same code
84
+ def subfields(field, code)
85
+ sf = []
86
+ field.each do |s|
87
+ next unless s.code == code
88
+
89
+ captilize_subfield(s) if code == '3'
90
+ sf << s
91
+ end
92
+ sf
93
+ end
94
+
95
+ def captilize_subfield(subfield)
96
+ new_value = subfield.value.capitalize
97
+ subfield.value = new_value
98
+ end
99
+
100
+ # Combine multiple subfield values based on definition from csv file
101
+ def combined_subfield_value(field, code, symbol)
102
+ sf_arr = subfields(field, code)
103
+ return '' if sf_arr.empty?
104
+
105
+ sf_arr.map(&:value).join(symbol) << symbol # symbol exmaple ' -- '
106
+ end
107
+
108
+ # Mutilpe subfields in one field may have the same name
109
+ # Get all subfields on 'from subfield name'
110
+ # Change subfield names with 'to subfield name'
111
+ def subfields_from_to(field, from, to)
112
+ subfield_arr = subfields(field, from)
113
+ subfield_arr.each { |sf| sf.code = to } if !subfield_arr.empty? && (from != to)
114
+ subfield_arr
115
+ end
116
+
117
+ def subfield6_endwith_00?(f)
118
+ return false unless is_880_field?(f)
119
+
120
+ two_digits = f['6'].strip.split('-')[1][0..1]
121
+ two_digits.to_s == '00'
122
+ end
123
+
124
+ def fields_with_subfield6(fields)
125
+ fields.select { |f| subfield6?(f) }
126
+ end
127
+
128
+ # return subfield 6 with the smallest sequence number
129
+ def subfield_6_values(fields)
130
+ fields_with_subfield6(fields).map(&:value)
131
+ end
132
+
133
+ def subfield6_value_with_lowest_seq_no(values)
134
+ seq = 9999
135
+ txt = nil
136
+ values.each do |val|
137
+ num = seq_no(val)
138
+ if (num > 0) && (num < seq)
139
+ seq = num
140
+ txt = val
141
+ end
142
+ end
143
+ txt
144
+ end
145
+
146
+ # return all subfields except subfield6
147
+ def subfields_without_subfield6(field)
148
+ field.subfields.reject { |sf| sf.code == '6' }
149
+ end
150
+
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,117 @@
1
+ require 'csv'
2
+ require 'marc'
3
+
4
+ module BerkeleyLibrary
5
+ module TIND
6
+ module Mapping
7
+ module Util
8
+
9
+ class << self
10
+
11
+ def csv_rows(file)
12
+ rows = []
13
+ CSV.foreach(file, headers: true, header_converters: :symbol, encoding: 'bom|utf-8') do |row|
14
+ # puts row.headers
15
+ rows << clr_row(row)
16
+ end
17
+ rows
18
+ end
19
+
20
+ def concatenation_symbol(str)
21
+ str ? str.gsub('a', ' ') : ' '
22
+ end
23
+
24
+ def indicator(str)
25
+ return [] unless str
26
+
27
+ str.strip.gsub('_', ' ').split(',')
28
+ end
29
+
30
+ def tag_symbol(tag)
31
+ "tag_#{tag}".to_sym
32
+ end
33
+
34
+ def datafield(tag, indicator, subfields)
35
+ datafield = ::MARC::DataField.new(tag, indicator[0], indicator[1])
36
+ subfields.each { |sf| datafield.append(sf) }
37
+ datafield
38
+ end
39
+
40
+ def subfield(code, value)
41
+ ::MARC::Subfield.new(code, value)
42
+ end
43
+
44
+ def subfield_hash(field)
45
+ code_value_arr = field.to_hash[field.tag]['subfields']
46
+ {}.tap { |i| code_value_arr.each(&i.method(:update)) }
47
+ end
48
+
49
+ # input an array of rules, example: [["a,b,c,d", "b", "--"],["o,p,q", "b", ""]]
50
+ def symbols(rules)
51
+ rules.map { |rule| concatenation_symbol(rule[2]).strip }
52
+ end
53
+
54
+ def remove_extra_symbol(rules, val)
55
+ symbols = symbols(rules)
56
+ symbols.each { |s| val = val.strip.delete_suffix(s) }
57
+ val
58
+ end
59
+
60
+ def alma_datafield(tag, record)
61
+ record.fields.each { |f| return f if f.tag.to_s == tag }
62
+ nil
63
+ end
64
+
65
+ def qualified_alma_record?(alma_record)
66
+ f_245 = alma_datafield('245', alma_record)
67
+ f_245_a = f_245['a'].downcase
68
+
69
+ val = 'Host bibliographic record'.downcase
70
+ !f_245_a.start_with? val
71
+ end
72
+
73
+ # From DM - get testing MARC from xml file
74
+ # @param xml [String] the XML to parse
75
+ # @return [MARC::Record, nil] the MARC record from the specified XML
76
+ def from_xml(xml)
77
+ # noinspection RubyYardReturnMatch,RubyMismatchedReturnType
78
+ all_from_xml(xml).first
79
+ end
80
+
81
+ private
82
+
83
+ def clr_row(row)
84
+ row[:tag_origin] = format_tag(row[:tag_origin])
85
+ row[:tag_destination] = format_tag(row[:tag_destination])
86
+ row[:map_if_no_this_tag_existed] = format_tag(row[:map_if_no_this_tag_existed])
87
+ row
88
+ end
89
+
90
+ # To ensure tag from csv file in three digits
91
+ def format_tag(tag)
92
+ # LDR is not a numeric tag
93
+ return tag unless numeric? tag
94
+
95
+ format('%03d', tag)
96
+ end
97
+
98
+ def numeric?(str)
99
+ return false if str.nil?
100
+
101
+ str.scan(/\D/).empty?
102
+ end
103
+
104
+ # Parses MARCXML.
105
+ #
106
+ # @param xml [String] the XML to parse
107
+ # @return [MARC::XMLReader] the MARC records
108
+ def all_from_xml(xml)
109
+ input = StringIO.new(xml.scrub)
110
+ MARC::XMLReader.new(input)
111
+ end
112
+
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1 @@
1
+ Dir.glob(File.expand_path('mapping/*.rb', __dir__)).sort.each(&method(:require))
@@ -7,7 +7,7 @@ module BerkeleyLibrary
7
7
  SUMMARY = 'TIND DA utilities for the UC Berkeley Library'.freeze
8
8
  DESCRIPTION = 'UC Berkeley Library utility gem for working with the TIND DA digital archive.'.freeze
9
9
  LICENSE = 'MIT'.freeze
10
- VERSION = '0.5.1'.freeze
10
+ VERSION = '0.6.0'.freeze
11
11
  HOMEPAGE = 'https://github.com/BerkeleyLibrary/tind'.freeze
12
12
  end
13
13
  end
@@ -30,8 +30,7 @@ module BerkeleyLibrary
30
30
  #
31
31
  # @param obj [Object] the object that might be an IO
32
32
  def writer_like?(obj)
33
- # TODO: is it possible/desirable to loosen this? how strict is libxml2?
34
- obj.is_a?(IO) || obj.is_a?(StringIO)
33
+ obj.respond_to?(:write) && obj.respond_to?(:close)
35
34
  end
36
35
 
37
36
  end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'marc'
3
+
4
+ module BerkeleyLibrary
5
+ module TIND
6
+ module Mapping
7
+ describe AdditionalDatafieldProcess do
8
+
9
+ let(:qualified_alma_obj) { Alma.new('spec/data/mapping/record.xml') }
10
+ let(:qualified_alm_record) { qualified_alma_obj.record }
11
+
12
+ let(:tind_marc) { TindMarc.new(qualified_alm_record) }
13
+ let(:tindfields) { tind_marc.tindfields }
14
+ let(:additona_245_field) { [Util.datafield('245', [' ', ' '], [Util.subfield('a', 'fake 245 a')])] }
15
+ let(:with_repeated_fields) { tindfields.concat additona_245_field }
16
+
17
+ context '# Process 1: Remove duplicated fields' do
18
+ # two 245 fields inputed, one is removed
19
+ it 'Input two 245 fields, return only one 245 field' do
20
+ expect(tind_marc.remove_repeats(with_repeated_fields).map(&:tag).count('245')).to eq 1
21
+ end
22
+
23
+ # one subfield 'a' is kept, subfield 'a' with a value of 'fake 245 a' is ignored
24
+ it 'Input two subfield $a, keeping the first one' do
25
+ without_repeated_fields = tind_marc.remove_repeats(with_repeated_fields)
26
+ single_field_245 = tind_marc.field_on_tag('245', without_repeated_fields)
27
+ expect(single_field_245['a']).to eq 'Cang jie pian'
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ module BerkeleyLibrary
5
+ module TIND
6
+ module Mapping
7
+ describe AlmaBase do
8
+ extend BerkeleyLibrary::Logging
9
+ let(:dummy_obj) { Class.new { extend AlmaBase } }
10
+ let(:alma_record) { ::MARC::Record.new }
11
+ let(:save_to_file) { Tempfile.new('xml') }
12
+ let(:record_id) { Class.new { extend BerkeleyLibrary::Alma::RecordId } }
13
+ let(:hash) { { '980' => ['pre_1912'] } }
14
+
15
+ let(:qualified_alma_obj) { Alma.new('spec/data/mapping/record.xml') }
16
+ let(:qualified_alm_record) { qualified_alma_obj.record }
17
+
18
+ let(:un_qualified_alma_obj) { Alma.new('spec/data/mapping/record_not_qualified.xml') }
19
+ let(:un_qualified_alm_record) { un_qualified_alma_obj.record }
20
+
21
+ describe '# base_tind_record' do
22
+ it 'input a qualified Alma record - return a tind record' do
23
+ allow(dummy_obj).to receive(:tind_record).with('C084093187', qualified_alm_record, []).and_return(::MARC::Record.new)
24
+ expect(dummy_obj.base_tind_record('C084093187', [], qualified_alm_record)).to be_a ::MARC::Record
25
+ end
26
+
27
+ it 'input an unqualified Alma record - return nil' do
28
+ expect { dummy_obj.base_tind_record('C084093187', [], un_qualified_alm_record) }.to raise_error(ArgumentError)
29
+ end
30
+
31
+ it 'no input Alma record with a nil (record) from id - return nil' do
32
+ allow(dummy_obj).to receive(:alma_record_from).with('C084093187').and_return(nil)
33
+ expect { dummy_obj.base_tind_record('C084093187', []) }.to raise_error(ArgumentError)
34
+ end
35
+ end
36
+
37
+ describe '# base_save' do
38
+ it 'save tind record' do
39
+ dummy_obj.base_save('C084093187', qualified_alm_record, save_to_file)
40
+ expect(File.open(save_to_file.path).readlines[0]).to eq "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
41
+ end
42
+ end
43
+
44
+ describe '# alma_record' do
45
+ it 'get Alma record' do
46
+ allow(record_id).to receive(:get_marc_record).and_return(::MARC::Record.new)
47
+ allow(dummy_obj).to receive(:get_record_id).with('C084093187').and_return(record_id)
48
+ expect(dummy_obj.send(:alma_record_from, 'C084093187')).to be_instance_of ::MARC::Record
49
+ end
50
+ end
51
+
52
+ describe '# get_record_id' do
53
+ it 'return BerkeleyLibrary::Alma::BarCode' do
54
+ BerkeleyLibrary::TIND::Mapping::AlmaBase.is_barcode = true
55
+ expect(dummy_obj.send(:get_record_id, 'C084093187')).to be_instance_of BerkeleyLibrary::Alma::BarCode
56
+ end
57
+
58
+ it 'return BerkeleyLibrary::Alma::MMSID' do
59
+ BerkeleyLibrary::TIND::Mapping::AlmaBase.is_barcode = false
60
+ expect(dummy_obj.send(:get_record_id, '991085821143406532')).to be_instance_of BerkeleyLibrary::Alma::MMSID
61
+
62
+ end
63
+
64
+ it 'return BerkeleyLibrary::Alma::BibNumber' do
65
+ BerkeleyLibrary::TIND::Mapping::AlmaBase.is_barcode = false
66
+ expect(dummy_obj.send(:get_record_id, 'b11082434')).to be_instance_of BerkeleyLibrary::Alma::BibNumber
67
+
68
+ end
69
+ end
70
+
71
+ describe '# add_f_035' do
72
+ it 'Add 035 from mms_id' do
73
+ BerkeleyLibrary::TIND::Mapping::AlmaBase.is_035_from_mms_id = true
74
+ expect(dummy_obj.send(:add_f_035, '991085821143406532', hash).tag).to eq '035'
75
+ end
76
+
77
+ it 'Not to add 035 from mms_id' do
78
+ BerkeleyLibrary::TIND::Mapping::AlmaBase.is_035_from_mms_id = false
79
+ expect(dummy_obj.send(:add_f_035, '991085821143406532', hash)).to eq nil
80
+ end
81
+ end
82
+
83
+ describe '# tind_record, # derived_tind_fields' do
84
+ BerkeleyLibrary::TIND::Mapping::AlmaBase.collection_parameter_hash = {
85
+ '336' => ['Image'],
86
+ '852' => ['East Asian Library'],
87
+ '980' => ['pre_1912'],
88
+ '982' => ['Pre 1912 Chinese Materials - short name', 'Pre 1912 Chinese Materials - long name'],
89
+ '991' => []
90
+ }
91
+ let(:id) { '991085821143406532' }
92
+ let(:tags) { %w[902 336 852 980 982 901 856] }
93
+ let(:tags_with_035) { %w[902 336 852 980 982 901 856 035] }
94
+
95
+ it ' get tind_record' do
96
+ datafields = []
97
+ marc_record = qualified_alm_record
98
+ expect(dummy_obj.send(:tind_record, id, marc_record, datafields)).to be_instance_of ::MARC::Record
99
+ end
100
+
101
+ it 'get derived fields without 035' do
102
+ expect(dummy_obj.send(:derived_tind_fields, id).map(&:tag)).to eq tags
103
+ end
104
+
105
+ it 'get derived fields with 035' do
106
+ BerkeleyLibrary::TIND::Mapping::AlmaBase.is_035_from_mms_id = true
107
+ expect(dummy_obj.send(:derived_tind_fields, id).map(&:tag)).to eq tags_with_035
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+ end
114
+ end
115
+ end