marcspec 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +59 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/lib/marcspec/customspec.rb +97 -0
  8. data/lib/marcspec/kvmap.rb +79 -0
  9. data/lib/marcspec/map.rb +67 -0
  10. data/lib/marcspec/marcfieldspec.rb +205 -0
  11. data/lib/marcspec/multivaluemap.rb +62 -0
  12. data/lib/marcspec/solrfieldspec.rb +123 -0
  13. data/lib/marcspec/specset.rb +58 -0
  14. data/lib/marcspec.rb +11 -0
  15. data/lib/orig.rb +288 -0
  16. data/spec/data/batch.dat +1 -0
  17. data/spec/data/one.dat +1 -0
  18. data/spec/data/umich/translation_maps/area_map.properties +1039 -0
  19. data/spec/data/umich/translation_maps/availability_map_ht.properties +9 -0
  20. data/spec/data/umich/translation_maps/availability_map_umich.properties +6 -0
  21. data/spec/data/umich/translation_maps/callnumber_map.properties +21 -0
  22. data/spec/data/umich/translation_maps/callnumber_subject_map.properties +214 -0
  23. data/spec/data/umich/translation_maps/country_map.properties +320 -0
  24. data/spec/data/umich/translation_maps/format_map.properties +47 -0
  25. data/spec/data/umich/translation_maps/format_map_umich.properties +35 -0
  26. data/spec/data/umich/translation_maps/ht_namespace_map.properties +10 -0
  27. data/spec/data/umich/translation_maps/institution_map.properties +11 -0
  28. data/spec/data/umich/translation_maps/language_map.properties +489 -0
  29. data/spec/data/umich/translation_maps/library_map.properties +48 -0
  30. data/spec/data/umich/translation_maps/location_map.properties +345 -0
  31. data/spec/data/umich/umich_index.properties +130 -0
  32. data/spec/maps_spec.rb +91 -0
  33. data/spec/marcfieldspecs_spec.rb +109 -0
  34. data/spec/marcspec_spec.rb +10 -0
  35. data/spec/solrfieldspec_spec.rb +177 -0
  36. data/spec/spec_helper.rb +16 -0
  37. metadata +166 -0
@@ -0,0 +1,123 @@
1
+ require 'stringio'
2
+ module MARCSpec
3
+ class SolrFieldSpec
4
+ attr_accessor :solrField, :first, :map, :noMapKeyDefault, :marcfieldspecs, :default
5
+
6
+ def initialize(opts)
7
+ @solrField = opts[:solrField]
8
+ @first = opts[:firstOnly] || false
9
+ @default = opts[:default] || nil
10
+ @map = opts[:map] || nil
11
+ @noMapKeyDefault = opts[:noMapKeyDefault] || nil
12
+ @marcfieldspecs = []
13
+ end
14
+
15
+ def << tagspec
16
+ @marcfieldspecs << tagspec
17
+ end
18
+
19
+
20
+ def raw_marc_values r
21
+ vals = []
22
+ @marcfieldspecs.each do |ts|
23
+ vals.concat ts.marc_values(r)
24
+ end
25
+ return vals
26
+ end
27
+
28
+ def marc_values r
29
+ vals = raw_marc_values r
30
+ unless vals.is_a? Array
31
+ vals = [vals]
32
+ end
33
+
34
+ if @first
35
+ vals = [vals.compact.first].compact
36
+ end
37
+
38
+ # If we got nothing, just return either nothing or the defualt,
39
+ # if there is one. Don't screw around with mapping.
40
+ if vals.size == 0
41
+ if @default.nil? # unless there's a default value, just return nothing
42
+ return []
43
+ else
44
+ return [@default]
45
+ end
46
+ end
47
+
48
+ # If we've got a map, map it.
49
+
50
+ if (@map)
51
+ vals.map! {|v| @map[v, @noMapKeyDefault]}
52
+ end
53
+
54
+ # Flatten it all out
55
+
56
+ vals.flatten!
57
+ vals.uniq!
58
+ vals.compact!
59
+ return vals
60
+ end
61
+
62
+
63
+ def == other
64
+ return ((other.solrField == self.solrField) and
65
+ (other.first == self.first) and
66
+ (other.map == self.map) and
67
+ (other.noMapKeyDefault == self.noMapKeyDefault) and
68
+ (other.marcfieldspecs == self.marcfieldspecs))
69
+ end
70
+
71
+ def self.fromPPString str
72
+ return self.fromHash eval(str)
73
+ end
74
+
75
+ def self.fromHash h
76
+ sfs = self.new(h)
77
+ h[:specs].each do |s|
78
+ if s.size < 3
79
+ sfs << MARCSpec::ControlFieldSpec.new(*s)
80
+ else
81
+ sfs << MARCSpec::VariableFieldSpec.new(s[0], s[3], s[4])
82
+ end
83
+ end
84
+ return sfs
85
+ end
86
+
87
+ def pretty_print pp
88
+ pp.pp eval(self.asPPString)
89
+ end
90
+
91
+ def asPPString
92
+ s = StringIO.new
93
+ s.print "{\n :solrField=> "
94
+ PP.singleline_pp(@solrField, s)
95
+ s.print(",\n ")
96
+ s.print ":firstOnly => true,\n " if @first
97
+ if @default
98
+ s.print(":default => ")
99
+ PP.singleline_pp(@default, s)
100
+ s.print(",\n ")
101
+ end
102
+ if @map
103
+ s.print(":mapname => ")
104
+ PP.singleline_pp(@map.mapname, s)
105
+ s.print(",\n ")
106
+ end
107
+ if @noMapKeyDefault
108
+ s.print(":noMapKeyDefault => ")
109
+ PP.singleline_pp(@noMapKeyDefault, s)
110
+ s.print(",\n ")
111
+ end
112
+ s.print(":specs => [\n")
113
+ @marcfieldspecs.each do |ts|
114
+ s.print ' '
115
+ PP.singleline_pp(ts, s)
116
+ s.print(",\n")
117
+ end
118
+ s.print " ]\n}"
119
+ return s.string
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,58 @@
1
+ require 'jruby_streaming_update_solr_server'
2
+ module MARCSpec
3
+ class SpecSet
4
+ attr_accessor :tmaps, :solrfieldspecs
5
+
6
+ def initialize
7
+ @tmaps = {}
8
+ @solrfieldspecs = []
9
+ end
10
+
11
+ def map name
12
+ return self.tmaps[name]
13
+ end
14
+
15
+ def loadMapsFromDir dir
16
+ unless File.exist? dir
17
+ raise ArgumentError, "Cannot load maps from #{dir}: does not exist"
18
+ end
19
+ Dir.glob("#{dir}/*.rb").each do |tmapfile|
20
+ self.add_map(MARCSpec::Map.fromFile(tmapfile))
21
+ end
22
+ end
23
+
24
+ def add_map map
25
+ self.tmaps[map.mapname] = map
26
+ end
27
+
28
+ def add_spec solrfieldspec
29
+ self.solrfieldspecs << solrfieldspec
30
+ end
31
+
32
+ alias_method :<<, :add_spec
33
+
34
+ def each
35
+ @solrfieldspecs.each do |fs|
36
+ yield fs
37
+ end
38
+ end
39
+
40
+ def fill_hashlike_from_marc r, hashlike
41
+ @solrfieldspecs.each do |sfs|
42
+ hashlike[sfs.solrField] = sfs.marc_values(r)
43
+ end
44
+ end
45
+
46
+ def doc_from_marc r
47
+ doc = SolrInputDocument.new
48
+ fill_hashlike_from_marc r, doc
49
+ return doc
50
+ end
51
+
52
+ def hash_from_marc r
53
+ h = {}
54
+ fill_hashlike_from_marc r, h
55
+ return h
56
+ end
57
+ end
58
+ end
data/lib/marcspec.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'logger'
2
+
3
+ $LOG = Logger.new(STDOUT)
4
+ $LOG.level = Logger::WARN
5
+
6
+ require "marcspec/customspec"
7
+ require "marcspec/solrfieldspec"
8
+ require "marcspec/kvmap"
9
+ require "marcspec/multivaluemap"
10
+ require "marcspec/specset"
11
+ require "marcspec/marcfieldspec"
data/lib/orig.rb ADDED
@@ -0,0 +1,288 @@
1
+ require 'set'
2
+ require 'pp'
3
+ require 'logger'
4
+
5
+ $LOG ||= Logger.new(STDERR)
6
+
7
+ module MARCSpec
8
+
9
+ class MapSpec
10
+ attr_accessor :map, :type, :default
11
+
12
+ def initialize(type, map, default=nil)
13
+ @type = type
14
+ @default = default
15
+ @map = map
16
+ end
17
+
18
+ def [] key
19
+ if (@type == :kv)
20
+ if @map.has_key? key
21
+ return @map[key]
22
+ else
23
+ return @default
24
+ end
25
+ end
26
+
27
+ # For a pattern, we find all that match.
28
+
29
+ if (@type == :pattern)
30
+ rv = []
31
+ @map.each do |pv|
32
+ pat = pv[0]
33
+ val = pv[1]
34
+ # puts "Trying pattern #{pat} against #{key}"
35
+ if pat.match(key)
36
+ rv << val
37
+ # puts "Matched: adding #{val}"
38
+ end
39
+ end
40
+ rv.uniq!
41
+ if rv.size > 0
42
+ return rv
43
+ else
44
+ return @default
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ class CustomSpec
51
+ def initialize(proc, args)
52
+ @proc = proc
53
+ @args = args
54
+ end
55
+
56
+ def marc_values_hash fieldnames, r
57
+ a = @proc(r, args)
58
+ rv = {}
59
+ fieldnames.each_with_index do |fn, i|
60
+ rv[fn] = a[i]
61
+ end
62
+ return rv
63
+ end
64
+ end
65
+
66
+
67
+ class TagSpec
68
+ attr_accessor :tag, :codes, :joiner, :parent, :ind1, :ind2, :range, :is_control
69
+
70
+ def initialize(tag, codes=nil)
71
+ @codes = Set.new
72
+ @tag = tag
73
+ @joiner = ' '
74
+ @substr = nil
75
+ tagint = tag.to_i
76
+ @is_control = (tagint != 0 and tagint < 10)
77
+ if (codes)
78
+ self.codes = codes
79
+ end
80
+ end
81
+
82
+ def range= newrange
83
+ if newrange =~ /\s*(\d+)-(\d+)/
84
+ start = $1.to_i
85
+ last = $2.to_i
86
+ @range = start..last
87
+ else
88
+ se = newrange.to_i
89
+ @range = se..se
90
+ end
91
+ end
92
+
93
+ def codes= newcodes
94
+ if newcodes.is_a? Range
95
+ @codes = newcodes.to_a
96
+ elsif newcodes !~ /\S/
97
+ @codes = nil
98
+ # Otherwise, just split into individual characters
99
+ else
100
+ @codes = newcodes.split(//)
101
+ end
102
+ end
103
+
104
+ def marc_values r
105
+ if @is_control
106
+ vals = r.find_by_tag(@tag).map {|f| f.value}
107
+ # puts "Start with #{vals.join(', ')}"
108
+ if @range
109
+ vals.map! {|v| v[@range]}
110
+ end
111
+ # puts "End with #{vals.join(', ')}"
112
+
113
+ else
114
+ fields = r.find_by_tag(@tag)
115
+ vals = []
116
+ fields.each do |f|
117
+ subvals = f.sub_values(@codes)
118
+ vals << subvals.join(@joiner) if subvals.size > 0
119
+ end
120
+ end
121
+ # puts vals.join(', ')
122
+ return vals
123
+ end
124
+
125
+ end
126
+
127
+ class FieldSpec
128
+ attr_accessor :field, :first, :map, :tagspecs
129
+
130
+ def initialize(opts)
131
+ @field = opts[:field]
132
+ @first = opts[:first] || false
133
+ @map = opts[:map] || nil
134
+ @tagspecs = []
135
+ end
136
+
137
+ def << tagspec
138
+ tagspec.parent = self
139
+ @tagspecs << tagspec
140
+ end
141
+
142
+ def marc_values r
143
+ vals = []
144
+ # puts "Tagspecs has #{@tagspecs.size} items"
145
+ @tagspecs.each do |ts|
146
+ vals.concat ts.marc_values(r)
147
+ # puts vals.join(', ')
148
+ break if @first and vals.size > 0
149
+ end
150
+
151
+ if (@map)
152
+ vals.map! {|v| @map[v]}
153
+ # vals.each do |v|
154
+ # puts "Map: #{v} => #{@map[v].to_s}"
155
+ # end
156
+ end
157
+ vals.flatten!
158
+ vals.uniq!
159
+ vals.compact!
160
+ return vals
161
+ end
162
+
163
+ end
164
+
165
+
166
+ class SpecSet
167
+ attr_accessor :tmaps, :fieldspecs
168
+ def initialize(*args)
169
+ tmapdir = args.pop!
170
+ unless File.directory? tmapdir
171
+ $LOG.error "Directory #{tmapdir} not found"
172
+ raise LoadError, "Directory #{tmapdir} not found"
173
+ end
174
+
175
+ @tmaps = {}
176
+ Dir.glob(tmapdir + '/*.rb') do |fn|
177
+ basename = File.basename(fn).sub(/\.rb$/, '')
178
+ $LOG.info "Loading translation map #{basename}"
179
+
180
+ begin
181
+ rawmap = eval(File.open(fn).read)
182
+ @tmaps[basename] = MapSpec.new(rawmap[:type], rawmap[:map], rawmap[:default])
183
+ rescue SyntaxError
184
+ $LOG.error "Error processing translation map file #{fn}: #{$!}"
185
+ raise SyntaxError, $!
186
+ end
187
+
188
+ end
189
+
190
+ @fieldspecs = []
191
+
192
+ # Get the index files
193
+ args.each do |indexfile|
194
+ begin
195
+ unless File.exists? indexfile
196
+ $LOG.error "File #{indexfile} does not exist"
197
+ raise LoadError, "File #{indexfile} does not exist"
198
+ end
199
+ $LOG.info "Loading index file #{indexfile}"
200
+ rawindex = eval(File.open(indexfile).read)
201
+ rawindex.each do |entry|
202
+ fs = FieldSpec.new(:field => entry[:solrField], :first=>entry[:firstOnly])
203
+ mapname = entry[:map]
204
+ if mapname
205
+ if @tmaps.has_key? mapname
206
+ fs.map = @tmaps[mapname]
207
+ else
208
+ $LOG.error "Can't find map #{mapname}"
209
+ end
210
+ end
211
+ entry[:specs].each do |entryts|
212
+
213
+ # A one- or two-element entry is a control field
214
+ # A three element entry (tag, ind1, ind2) is all subs of a field (need to implement)
215
+ # A four element field is tag, ind1, ind2, subs
216
+ # A five element field is tag, ind1, ind2, subs, joiner
217
+
218
+
219
+ tag = entryts[0]
220
+
221
+ # Is tag the symbol :custom? Then make it a custom item
222
+
223
+ if tag == :custom
224
+ ts = CustomSpec.new(entryts[1], entryts[2..-1])
225
+ fs << ts
226
+ next
227
+ end
228
+
229
+ # If it's not custom, the solrField better be a scale
230
+ if entry[:solrField].is_a? Array
231
+ # log an error and bail out
232
+ end
233
+
234
+ # Otherwise, it's a tag spec
235
+ if tag.is_a? Fixnum
236
+ tag = '%03d' % tag
237
+ end
238
+
239
+
240
+ ts = TagSpec.new(tag)
241
+ if entryts.size < 3
242
+ ts.is_control = true
243
+ ts.range = entryts[1] if entryts[1]
244
+ else
245
+ ts.ind1 = entryts[1]
246
+ ts.ind2 = entryts[2]
247
+ ts.codes = entryts[3]
248
+ ts.joiner = entryts[4] if entryts[4]
249
+ end
250
+ fs << ts
251
+ end
252
+ self << fs
253
+ end
254
+ rescue SyntaxError
255
+ $LOG.error "Error processing index file #{indexfile}: #{$!}"
256
+ raise SyntaxError
257
+ end
258
+ end
259
+ end
260
+
261
+ def each
262
+ @fieldspecs.each do |fs|
263
+ yield fs
264
+ end
265
+ end
266
+
267
+ def << fieldspec
268
+ @fieldspecs << fieldspec
269
+ end
270
+
271
+ def doc_from_marc r
272
+ doc = SolrInputDocument.new
273
+ @fieldspecs.each do |fs|
274
+ doc[fs.field] = fs.marc_values(r)
275
+ end
276
+ return doc
277
+ end
278
+
279
+ def hash_from_marc r
280
+ h = {}
281
+ @fieldspecs.each do |fs|
282
+ h[fs.field] = fs.marc_values(r)
283
+ end
284
+ return h
285
+ end
286
+
287
+ end
288
+ end
@@ -0,0 +1 @@
1
+ 00755cam 22002414a 4500001001300000003000600013005001700019008004100036010001700077020004300094040001800137042000800155050002600163082001700189100003100206245005400237260004200291300007200333500003300405650003700438630002500475630001300500fol05731351 IMchF20000613133448.0000107s2000 nyua 001 0 eng  a 00020737  a0471383147 (paper/cd-rom : alk. paper) aDLCcDLCdDLC apcc00aQA76.73.P22bM33 200000a005.13/32211 aMartinsson, Tobias,d1976-10aActivePerl with ASP and ADO /cTobias Martinsson. aNew York :bJohn Wiley & Sons,c2000. axxi, 289 p. :bill. ;c23 cm. +e1 computer laser disc (4 3/4 in.) a"Wiley Computer Publishing." 0aPerl (Computer program language)00aActive server pages.00aActiveX.00647pam 2200241 a 4500001001300000003000600013005001700019008004100036010001700077020001500094040001800109042000800127050002600135082001500161100002600176245006700202260003800269263000900307300001100316650003700327650002500364700001600389fol05754809 IMchF20000601115601.0000203s2000 mau 001 0 eng  a 00022023  a1565926994 aDLCcDLCdDLC apcc00aQA76.73.P22bD47 200000a005.742211 aDescartes, Alligator.10aProgramming the Perl DBI /cAlligator Descartes and Tim Bunce. aCmabridge, MA :bO'Reilly,c2000. a1111 ap. cm. 0aPerl (Computer program language) 0aDatabase management.1 aBunce, Tim.00605cam 22002054a 4500001001300000003000600013005001700019008004100036010001700077040001800094042000800112050002700120082001700147100002100164245005500185260004500240300002600285504005100311650003700362fol05843555 IMchF20000525142739.0000318s1999 cau b 001 0 eng  a 00501349  aDLCcDLCdDLC apcc00aQA76.73.P22bB763 199900a005.13/32211 aBrown, Martin C.10aPerl :bprogrammer's reference /cMartin C. Brown. aBerkeley :bOsborne/McGraw-Hill,cc1999. axix, 380 p. ;c22 cm. aIncludes bibliographical references and index. 0aPerl (Computer program language)00579cam 22002054a 4500001001300000003000600013005001700019008004100036010001700077020001500094040001800109042000800127050002700135082001700162100002100179245005500200260004500255300003600300650003700336fol05843579 IMchF20000525142716.0000318s1999 caua 001 0 eng  a 00502116  a0072120002 aDLCcDLCdDLC apcc00aQA76.73.P22bB762 199900a005.13/32211 aBrown, Martin C.10aPerl :bthe complete reference /cMartin C. Brown. aBerkeley :bOsborne/McGraw-Hill,cc1999. axxxv, 1179 p. :bill. ;c24 cm. 0aPerl (Computer program language)00801nam 22002778a 4500001001300000003000600013005001700019008004100036010001700077020001500094040001300109042000800122050002600130082001800156100002000174245008800194250003200282260004100314263000900355300001100364650003700375650003600412650002600448700002500474700002400499fol05848297 IMchF20000524125727.0000518s2000 mau 001 0 eng  a 00041664  a1565924193 aDLCcDLC apcc00aQA76.73.P22bG84 200000a005.2/7622211 aGuelich, Scott.10aCGI programming with Perl /cScott Guelich, Shishir Gundavaram & Gunther Birznieks. a2nd ed., expanded & updated aCambridge, Mass. :bO'Reilly,c2000. a0006 ap. cm. 0aPerl (Computer program language) 0aCGI (Computer network protocol) 0aInternet programming.1 aGundavaram, Shishir.1 aBirznieks, Gunther.00665nam 22002298a 4500001001300000003000600013005001700019008004100036010001700077020001500094040001300109042000800122050002700130082001700157111005200174245008600226250001200312260004100324263000900365300001100374650005000385fol05865950 IMchF20000615103017.0000612s2000 mau 100 0 eng  a 00055759  a0596000138 aDLCcDLC apcc00aQA76.73.P22bP475 200000a005.13/32212 aPerl Conference 4.0d(2000 :cMonterey, Calif.)10aProceedings of the Perl Conference 4.0 :bJuly 17-20, 2000, Monterey, California. a1st ed. aCambridge, Mass. :bO'Reilly,c2000. a0006 ap. cm. 0aPerl (Computer program language)vCongresses.00579nam 22002178a 4500001001300000003000600013005001700019008004100036010001700077020001500094040001300109042000800122050002600130082001700156100002800173245006200201260004100263263000900304300001100313650003700324fol05865956 IMchF20000615102948.0000612s2000 mau 000 0 eng  a 00055770  a1565926099 aDLCcDLC apcc00aQA76.73.P22bB43 200000a005.13/32211 aBlank-Edelman, David N.10aPerl for system administration /cDavid N. Blank-Edelman. aCambridge, Mass. :bO'Reilly,c2000. a0006 ap. cm. 0aPerl (Computer program language)00661nam 22002538a 4500001001300000003000600013005001700019008004100036010001700077020001500094040001300109042000800122050002600130082001700156100001700173245006700190250001200257260004100269263000900310300001100319650003700330700002300367700001700390fol05865967 IMchF20000615102611.0000614s2000 mau 000 0 eng  a 00055799  a0596000278 aDLCcDLC apcc00aQA76.73.P22bW35 200000a005.13/32211 aWall, Larry.10aProgramming Perl /cLarry Wall, Tom Christiansen & Jon Orwant. a3rd ed. aCambridge, Mass. :bO'Reilly,c2000. a0007 ap. cm. 0aPerl (Computer program language)1 aChristiansen, Tom.1 aOrwant, Jon.00603cam 22002054a 4500001001300000003000600013005001700019008004100036010001700077020001500094040001800109042000800127050002600135082001700161100003200178245006000210260005700270300003300327650003700360fol05872355 IMchF20000706095105.0000315s1999 njua 001 0 eng  a 00500678  a013020868X aDLCcDLCdDLC apcc00aQA76.73.P22bL69 199900a005.13/32211 aLowe, Vincentq(Vincent D.)10aPerl programmer's interactive workbook /cVincent Lowe. aUpper Saddle River, NJ :bPrentice Hall PTP,cc1999. axx, 633 p. :bill. ;c23 cm. 0aPerl (Computer program language)00696nam 22002538a 4500001001300000003000600013005001700019008004100036010001700077020002800094040001300122042000800135050002600143082001700169100002600186245004400212260005100256263000900307300001100316500002000327650003700347650001700384650004100401fol05882032 IMchF20000707091904.0000630s2000 cau 001 0 eng  a 00058174  a0764547291 (alk. paper) aDLCcDLC apcc00aQA76.73.P22bF64 200000a005.13/32212 aFoster-Johnson, Eric.10aCross-platform Perl /cEric F. Johnson. aFoster City, CA :bIDG Books Worldwide,c2000. a0009 ap. cm. aIncludes index. 0aPerl (Computer program language) 0aWeb servers. 0aCross-platform software development.
data/spec/data/one.dat ADDED
@@ -0,0 +1 @@
1
+ 00734njm a2200217uu 4500001001500000003000400015005001700019007001500036008004100051010001900092040001300111245006800124260002600192300005600218651004500274700005400319700005500373700003000428852005000458852000800508afc99990058366DLC20071104155141.9sd ummunniauub071103s1939 xxufmnne||||||||| u eng|| aafc99990058366 aDLCcDLC04aThe Texas rangerh[sound recording] /cSung by Beale D. Taylor. aMedina, Texas,c1939. a1 sound disc :banalog, 33 1/3 rpm, mono. ;c12 in. 0aMedinazTexaszUnited States of America.1 aLomax, John Avery, 1867-1948eRecording engineer.1 aLomax, Ruby T. (Ruby Terrill)eRecording engineer.1 aTaylor, Beale D.eSinger. aAmerican Folklife Center, Library of Congress aDLC