viral_seq 1.5.0 → 1.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/README.md +17 -1
- data/bin/tcs +13 -4
- data/bin/tcs_log +614 -0
- data/bin/tcs_sdrm +2 -1
- data/docs/variants_structure.pdf +0 -0
- data/lib/viral_seq/constant.rb +3 -1
- data/lib/viral_seq/muscle.rb +8 -2
- data/lib/viral_seq/seq_hash.rb +8 -3
- data/lib/viral_seq/sequence.rb +12 -13
- data/lib/viral_seq/tcs_core.rb +10 -2
- data/lib/viral_seq/tcs_dr.rb +2 -2
- data/lib/viral_seq/version.rb +2 -2
- data/viral_seq.gemspec +1 -1
- metadata +5 -4
data/bin/tcs_log
CHANGED
@@ -16,6 +16,7 @@ require 'viral_seq'
|
|
16
16
|
require 'pathname'
|
17
17
|
require 'json'
|
18
18
|
require 'fileutils'
|
19
|
+
require 'csv'
|
19
20
|
|
20
21
|
indir = ARGV[0].chomp
|
21
22
|
indir_basename = File.basename(indir)
|
@@ -26,6 +27,7 @@ Dir.mkdir(tcs_dir) unless File.directory?(tcs_dir)
|
|
26
27
|
|
27
28
|
libs = []
|
28
29
|
Dir.chdir(indir) {libs = Dir.glob("*")}
|
30
|
+
libs.sort_by! {|lib| lib.split("-")[1].to_i}
|
29
31
|
|
30
32
|
outdir2 = File.join(tcs_dir, "combined_TCS_per_lib")
|
31
33
|
outdir3 = File.join(tcs_dir, "TCS_per_region")
|
@@ -53,18 +55,26 @@ header = %w{
|
|
53
55
|
Resampling_index
|
54
56
|
Combined_TCS
|
55
57
|
Combined_TCS_after_QC
|
58
|
+
Detection_Sensitivity
|
56
59
|
WARNINGS
|
57
60
|
}
|
58
61
|
|
62
|
+
pid_dist_data = {}
|
63
|
+
|
59
64
|
log.puts header.join(',')
|
60
65
|
libs.each do |lib|
|
61
66
|
Dir.mkdir(File.join(outdir2, lib)) unless File.directory?(File.join(outdir2, lib))
|
62
67
|
fasta_files = []
|
63
68
|
json_files = []
|
69
|
+
pid_json_files = []
|
70
|
+
pid_dist_data[lib] = {}
|
71
|
+
|
64
72
|
Dir.chdir(File.join(indir, lib)) do
|
65
73
|
fasta_files = Dir.glob("**/*.fasta")
|
66
74
|
json_files = Dir.glob("**/log.json")
|
75
|
+
pid_json_files = Dir.glob("**/primer_id.json")
|
67
76
|
end
|
77
|
+
|
68
78
|
fasta_files.each do |f|
|
69
79
|
path_array = Pathname(f).each_filename.to_a
|
70
80
|
region = path_array[0]
|
@@ -81,6 +91,14 @@ libs.each do |lib|
|
|
81
91
|
|
82
92
|
json_files.each do |f|
|
83
93
|
json_log = JSON.parse(File.read(File.join(indir, lib, f)), symbolize_names: true)
|
94
|
+
tcs_number = json_log[:total_tcs]
|
95
|
+
if json_log[:combined_tcs]
|
96
|
+
tcs_number = json_log[:combined_tcs]
|
97
|
+
if json_log[:combined_tcs_after_qc]
|
98
|
+
tcs_number = json_log[:combined_tcs_after_qc]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
84
102
|
log.print [lib,
|
85
103
|
json_log[:primer_set_name],
|
86
104
|
json_log[:total_raw_sequence],
|
@@ -95,8 +113,604 @@ libs.each do |lib|
|
|
95
113
|
json_log[:resampling_param],
|
96
114
|
json_log[:combined_tcs],
|
97
115
|
json_log[:combined_tcs_after_qc],
|
116
|
+
ViralSeq::TcsCore::detection_limit(tcs_number.to_i),
|
98
117
|
json_log[:warnings],
|
99
118
|
].join(',') + "\n"
|
100
119
|
end
|
120
|
+
|
121
|
+
pid_json_files.each do |f|
|
122
|
+
pid_json = JSON.parse(File.read(File.join(indir, lib, f)), symbolize_names: true)
|
123
|
+
region = Pathname(f).each_filename.to_a[-2]
|
124
|
+
pid_dist = {}
|
125
|
+
pid_json[:primer_id_distribution].each {|k,v| pid_dist[k.to_s.to_i] = v}
|
126
|
+
pid_dist_data[lib][region] = pid_dist
|
127
|
+
end
|
101
128
|
end
|
102
129
|
log.close
|
130
|
+
|
131
|
+
# Create HTML page with charts from log.csv above
|
132
|
+
|
133
|
+
class String
|
134
|
+
def var_safe
|
135
|
+
gsub '-',''
|
136
|
+
end
|
137
|
+
def shorten_html
|
138
|
+
gsub /\n/, ''
|
139
|
+
gsub /\t/, ''
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
colors = ["#332288", "#117733", "#44AA99", "#88CCEE", "#DDCC77", "#CC6677", "#AA4499", "#882255"]
|
144
|
+
bool_colors = { true => colors[3], false => colors[5] }
|
145
|
+
|
146
|
+
#hold vars for csv input
|
147
|
+
raw_sequence_data = ["['Library Name', 'Raw Sequences', { role: 'annotation' }]"]
|
148
|
+
lib_names = []
|
149
|
+
total_reads = 0
|
150
|
+
max_region_char_length = 5
|
151
|
+
lib_data = {}
|
152
|
+
batch_name = ""
|
153
|
+
region_colors = {"Other" => "#808080"}
|
154
|
+
|
155
|
+
CSV.foreach(log_file).each_with_index do |row, i|
|
156
|
+
next if i == 0 || row[0] == nil
|
157
|
+
|
158
|
+
lib_name = row[0]
|
159
|
+
region = row[1]
|
160
|
+
raw_sequences_per_barcode = row[2].to_i
|
161
|
+
|
162
|
+
if not region_colors.key?(region)
|
163
|
+
region_colors[region] = colors[region_colors.length % (colors.length - 1)]
|
164
|
+
end
|
165
|
+
|
166
|
+
if region.length > max_region_char_length + 2
|
167
|
+
max_region_char_length = region.length + 2
|
168
|
+
end
|
169
|
+
|
170
|
+
if batch_name == ""
|
171
|
+
batch_name = lib_name.split('-')[0]
|
172
|
+
end
|
173
|
+
|
174
|
+
if not lib_names.include? lib_name
|
175
|
+
lib_names.push(lib_name)
|
176
|
+
total_reads += raw_sequences_per_barcode
|
177
|
+
raw_sequence_data.push("['#{lib_name}', #{raw_sequences_per_barcode}, '#{raw_sequences_per_barcode}']")
|
178
|
+
lib_data[lib_name] = {}
|
179
|
+
end
|
180
|
+
|
181
|
+
lib_data[lib_name][region] = {
|
182
|
+
'lib_name' => lib_name,
|
183
|
+
'region' => region,
|
184
|
+
'raw_sequences_per_barcode' => raw_sequences_per_barcode,
|
185
|
+
'r1_raw' => row[3].to_i,
|
186
|
+
'r2_raw' => row[4].to_i,
|
187
|
+
'paired_raw' => row[5].to_i,
|
188
|
+
'cutoff' => row[6].to_i,
|
189
|
+
'consensus2' => row[9].to_i,
|
190
|
+
'distinct_to_raw' => row[10].to_f,
|
191
|
+
'resampling_index' => row[11].to_f,
|
192
|
+
'combined_TCS' => row[12].to_i,
|
193
|
+
'combined_TCS_after_QC' => row[13].to_i,
|
194
|
+
'detection_sensitivity' => row[14].to_f,
|
195
|
+
'warnings' => row[15].to_s || ""
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
#format output
|
200
|
+
total_reads = total_reads.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
201
|
+
raw_sequence_data = "[" + raw_sequence_data.join(',') + "]"
|
202
|
+
lib_names = lib_names.sort_by { |s| [s.split("-")[0], s.split("-")[1].to_i] }
|
203
|
+
|
204
|
+
#calculate 'other'
|
205
|
+
lib_data.each do |lib, data|
|
206
|
+
sum = data.values.reduce(0) { |sum, obj| sum + obj['paired_raw'] }
|
207
|
+
raw_sequences_per_barcode = data[data.keys.first]['raw_sequences_per_barcode']
|
208
|
+
other = raw_sequences_per_barcode - sum
|
209
|
+
data['other'] = {
|
210
|
+
"region" => "Other",
|
211
|
+
"paired_raw" => other,
|
212
|
+
"raw_sequences_per_barcode" => raw_sequences_per_barcode
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
#process each library's html
|
217
|
+
library_links = "
|
218
|
+
<ul>
|
219
|
+
<li id='link_basic-statistics' class='pointer current_page' onclick='showPage(\"basic-statistics\")'>Basic Statistics</li>
|
220
|
+
<li>Libraries
|
221
|
+
<ul style='margin-left: 16px;'>" + lib_names.map { |lib_name| "
|
222
|
+
<li id='link_#{lib_name}' class='pointer' onclick='showPage(\""+lib_name+"\")'>"+lib_name+"</li>"
|
223
|
+
}.join + "
|
224
|
+
</ul>
|
225
|
+
</li>
|
226
|
+
</ul>
|
227
|
+
"
|
228
|
+
|
229
|
+
library_pages = lib_names.map { |lib_name|
|
230
|
+
"<div id='"+lib_name+"' class='page hidden'>
|
231
|
+
<div class='error-message'></div>
|
232
|
+
<div style='display: flex; gap: 16px; min-height: 45vh;'>
|
233
|
+
<div class='card' style='flex: 1; display: flex; flex-direction: column;'>
|
234
|
+
<h2>Distribution of Raw Sequencing Reads</h2>
|
235
|
+
<div class='pie_chart' style='flex: 1;'></div>
|
236
|
+
</div>
|
237
|
+
<div class='card' style='display: flex; align-items: center; justify-content: center; flex-direction: column; flex: 1;'>
|
238
|
+
" + lib_data[lib_name].map{ |lib, data| data['region'] == "Other" ? "" : "
|
239
|
+
<div class='#{data['region']}' style='display: flex; align-items: center;'>
|
240
|
+
<div style='width: #{max_region_char_length.to_s}ch;'>#{data['region']}</div>
|
241
|
+
<div class='treemap' style='flex: 1; height: #{90 / lib_data[lib_name].length}vh;'></div>
|
242
|
+
</div>
|
243
|
+
" }.join + "
|
244
|
+
</div>
|
245
|
+
</div>
|
246
|
+
<div class='card'>
|
247
|
+
<h2 style='margin: 32px;'>Number of TCS at Regions</h2>
|
248
|
+
<div class='tcs_bar_chart'></div>
|
249
|
+
<div class='tcs_warnings' style='text-align: center;color: red;font-weight: bold;margin-left: 24px;'><strong></strong></div>
|
250
|
+
</div>
|
251
|
+
<div class='card'>
|
252
|
+
<h2>Detection Sensitivity</h2>
|
253
|
+
<p><i>The lowest abundance of minority mutations that can be detected with 95% confidence</i></p>
|
254
|
+
<div class='detection_sensitivity_chart'></div>
|
255
|
+
</div>
|
256
|
+
<div class='card'>
|
257
|
+
<h2 style='text-align: center;'>Distinct to Raw</h2>
|
258
|
+
<p><i>Distinct to Raw greater than 0.1 suggests more raw sequences are required to fully recover TCS</i></p>
|
259
|
+
<div class='raw_bar_chart'></div>
|
260
|
+
</div>
|
261
|
+
<div class='card'>
|
262
|
+
<h2>Resampling Index</h2>
|
263
|
+
<p><i>Resampling index less than 0.9 suggests Primer ID resampling</i></p>
|
264
|
+
<div class='resampling_bar_chart'></div>
|
265
|
+
</div>
|
266
|
+
<div class='card'>
|
267
|
+
<h2>Primer ID bin size distribution</h2>
|
268
|
+
<p><i>Red vertical line shows the consensus cut-off value</i></p>
|
269
|
+
" + lib_data[lib_name].map{ |lib, data| data['region'] == "Other" ? "" : "
|
270
|
+
<div class='#{data['region']} scatter' style='height: #{90 / 3}vh;'></div>
|
271
|
+
" }.join + "
|
272
|
+
</div>
|
273
|
+
</div>"
|
274
|
+
}.join
|
275
|
+
|
276
|
+
#format lib_data into charts
|
277
|
+
paired_raw = {}
|
278
|
+
tree_charts = {}
|
279
|
+
tcs_bar_chart = {}
|
280
|
+
distinct_bar_chart = {}
|
281
|
+
resampling_bar_chart = {}
|
282
|
+
detection_sensitivity_chart = {}
|
283
|
+
|
284
|
+
lib_data.each do |lib, data|
|
285
|
+
paired_raw[lib] = {}
|
286
|
+
|
287
|
+
paired_raw[lib]['data'] = "
|
288
|
+
[
|
289
|
+
['Region', 'Paired Raw'], " +
|
290
|
+
data.map { |lib, d| "
|
291
|
+
['#{d['region']},#{d['paired_raw']}', #{d['paired_raw']}],
|
292
|
+
" }.join + "
|
293
|
+
]
|
294
|
+
"
|
295
|
+
|
296
|
+
paired_raw[lib]['slices'] = "[#{
|
297
|
+
data.map{ |lib, d| "{color: '#{region_colors[d['region']]}'}" }.join(',')
|
298
|
+
}]"
|
299
|
+
|
300
|
+
paired_raw[lib]['residuel_text'] = data.map { |lib, d|
|
301
|
+
(d['paired_raw'].to_f / d['raw_sequences_per_barcode'].to_f) < (0.5/360) ?
|
302
|
+
"#{d['region']},#{d['paired_raw']}" :
|
303
|
+
nil
|
304
|
+
}.compact.join(" - ")
|
305
|
+
|
306
|
+
tree_charts[lib] = {}
|
307
|
+
data.each do |region, d|
|
308
|
+
if region != "other"
|
309
|
+
tree_charts[lib][region] = "
|
310
|
+
[
|
311
|
+
['Location', 'Parent', 'r'],
|
312
|
+
['Global', null,0],
|
313
|
+
['R1_Raw', 'Global', #{d['r1_raw'].to_s}],
|
314
|
+
['R2_Raw', 'Global', #{d['r2_raw'].to_s}],
|
315
|
+
['Paired_Raw', 'Global', #{d['paired_raw'].to_s}],
|
316
|
+
]
|
317
|
+
"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
has_c = data.values.inject(0) {|sum, h| sum + (h['combined_TCS'] == nil ? 0 : h['combined_TCS']) } > 0
|
322
|
+
has_qc = data.values.inject(0) {|sum, h| sum + (h['combined_TCS_after_QC'] == nil ? 0 : h['combined_TCS_after_QC']) } > 0
|
323
|
+
|
324
|
+
tcs_bar_chart[lib] = {
|
325
|
+
'data' => "
|
326
|
+
[
|
327
|
+
[
|
328
|
+
'Region',
|
329
|
+
'TCS',
|
330
|
+
#{has_c ? "'Combined TCS'," : ''}
|
331
|
+
#{has_qc ? "'TCS After QC'," : ''}
|
332
|
+
],
|
333
|
+
" + data.map{ |region, d| d['region'] === "Other" ? "" : "
|
334
|
+
[
|
335
|
+
'#{d['region']}#{d['warnings'].length > 0 ? '*' : ''}',
|
336
|
+
#{d['consensus2']} ,
|
337
|
+
#{has_c ? "#{d['combined_TCS']}," : ''}
|
338
|
+
#{has_qc ? "#{d['combined_TCS_after_QC']}," : ''}
|
339
|
+
],
|
340
|
+
" }.join + "
|
341
|
+
]
|
342
|
+
",
|
343
|
+
'warnings' => data.map{ |region, d|
|
344
|
+
d['region'] != "Other" && d['warnings'].length > 0 ? "#{region} - #{d["warnings"]}<br/>" : ''
|
345
|
+
}.join
|
346
|
+
}
|
347
|
+
|
348
|
+
detection_sensitivity_chart[lib] = "[
|
349
|
+
['Region', 'Detection Sensitivity'],
|
350
|
+
#{ data.map{ |region, d|
|
351
|
+
d['region'] == 'Other' ? "" : "['#{d['region']}', #{d['detection_sensitivity']}]"
|
352
|
+
}.join(',') }
|
353
|
+
]"
|
354
|
+
|
355
|
+
distinct_bar_chart[lib] = "
|
356
|
+
[
|
357
|
+
['Region','Distinct to Raw', {role: 'style'}],
|
358
|
+
"+ data.map{ |region, d| d['region'] == 'Other' ? "" : "
|
359
|
+
[
|
360
|
+
'#{d['region']}', #{d['distinct_to_raw']}, '#{bool_colors[d['distinct_to_raw'].to_f < 0.1]}'
|
361
|
+
],"}.join + "
|
362
|
+
]
|
363
|
+
"
|
364
|
+
|
365
|
+
resampling_bar_chart[lib] = "
|
366
|
+
[
|
367
|
+
['Region','Resampling Index', {role: 'style'}],
|
368
|
+
"+ data.map{ |region, d| d['region'] == 'Other' ? "" : "
|
369
|
+
[
|
370
|
+
'#{d['region']}', #{d['resampling_index']}, '#{bool_colors[d['resampling_index'].to_f > 0.9]}'
|
371
|
+
]," }.join + "
|
372
|
+
]
|
373
|
+
"
|
374
|
+
end
|
375
|
+
|
376
|
+
scatter_charts = {}
|
377
|
+
|
378
|
+
pid_dist_data.each do |lib, data|
|
379
|
+
scatter_charts[lib] = {}
|
380
|
+
data.each do |region, d|
|
381
|
+
max_x = d.max_by{|index, distribution| distribution }[1]
|
382
|
+
max_x = "1#{ max_x.to_s.chars.map{ |c| "0"}.join }"
|
383
|
+
scatter_charts[lib][region] = "[
|
384
|
+
['Index', 'Distribution', 'Cutoff'],
|
385
|
+
[#{lib_data[lib][region]['cutoff'].to_s}, null, 0],
|
386
|
+
[#{lib_data[lib][region]['cutoff'].to_s}, null, #{max_x}],
|
387
|
+
#{d.map{ |index, distribution| "[#{index}, #{distribution}, null]" }.join(',') },
|
388
|
+
]"
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
#create JS that initializes charts
|
393
|
+
paired_raw_js = paired_raw.map { |lib, data| '
|
394
|
+
var element_pie_'+lib.var_safe+' = document.querySelector("#'+lib+' .pie_chart")
|
395
|
+
if(isVisible(element_pie_'+lib.var_safe+')){
|
396
|
+
var chart_pie_'+lib.var_safe+' = new google.visualization.PieChart(element_pie_'+lib.var_safe+');
|
397
|
+
chart_pie_'+lib.var_safe+'.draw(
|
398
|
+
google.visualization.arrayToDataTable('+data['data']+'),
|
399
|
+
{
|
400
|
+
title: "Paired Raw",
|
401
|
+
titleTextStyle: {
|
402
|
+
fontSize: 18
|
403
|
+
},
|
404
|
+
pieSliceText: "label",
|
405
|
+
slices: ' + data['slices'] + ',
|
406
|
+
legend: {
|
407
|
+
position: "left",
|
408
|
+
alignment: "center"
|
409
|
+
},
|
410
|
+
chartArea: {
|
411
|
+
width: "100%",
|
412
|
+
height: "100%"
|
413
|
+
},
|
414
|
+
' + (data['residuel_text'].length > 0 ? "pieResidueSliceLabel: '#{data['residuel_text']}'" : "") + '
|
415
|
+
}
|
416
|
+
);
|
417
|
+
}
|
418
|
+
' }.join
|
419
|
+
|
420
|
+
tree_charts_js = tree_charts.map { |lib, d|
|
421
|
+
d.map { |region, data| '
|
422
|
+
var element_chart_tree_'+lib.var_safe+'_'+region.var_safe+' = document.querySelector("#'+lib+' .'+region+' .treemap")
|
423
|
+
if(isVisible(element_chart_tree_'+lib.var_safe+'_'+region.var_safe+')){
|
424
|
+
var chart_tree_'+lib.var_safe+'_'+region.var_safe+' = new google.visualization.TreeMap(element_chart_tree_'+lib.var_safe+'_'+region.var_safe+');
|
425
|
+
chart_tree_'+lib.var_safe+'_'+region.var_safe+'.draw(
|
426
|
+
google.visualization.arrayToDataTable('+data+'),
|
427
|
+
{
|
428
|
+
headerHeight: 0,
|
429
|
+
minColor: "' + colors[1] + '",
|
430
|
+
maxColor: "' + colors[5] + '",
|
431
|
+
fontColor: "#fff",
|
432
|
+
}
|
433
|
+
);
|
434
|
+
google.visualization.events.addListener(chart_tree_'+lib.var_safe+'_'+region.var_safe+', "select", function () {
|
435
|
+
chart_tree_'+lib.var_safe+'_'+region.var_safe+'.setSelection([]);
|
436
|
+
});
|
437
|
+
}
|
438
|
+
'}
|
439
|
+
}.join
|
440
|
+
|
441
|
+
tcs_bar_chart_js = tcs_bar_chart.map { |lib, data| '
|
442
|
+
var element_tcs_bar_chart_'+lib.var_safe+' = document.querySelector("#'+lib+' .tcs_bar_chart")
|
443
|
+
if(isVisible(element_tcs_bar_chart_'+lib.var_safe+')){
|
444
|
+
var tcs_bar_chart_'+lib.var_safe+' = new google.charts.Bar(element_tcs_bar_chart_'+lib.var_safe+');
|
445
|
+
tcs_bar_chart_'+lib.var_safe+'.draw(
|
446
|
+
google.visualization.arrayToDataTable('+data["data"]+'),
|
447
|
+
google.charts.Bar.convertOptions({
|
448
|
+
colors: ["' + colors[0] + '", "' + colors[1] + '", "' + colors[2] + '"],
|
449
|
+
legend: { position: "bottom" },
|
450
|
+
height: Math.round(window.innerHeight * .5),
|
451
|
+
chartArea: {
|
452
|
+
width: "100%",
|
453
|
+
height: "100%"
|
454
|
+
}
|
455
|
+
})
|
456
|
+
);
|
457
|
+
document.querySelector("#'+lib+' .tcs_warnings").innerHTML = "'+data['warnings']+'";
|
458
|
+
}
|
459
|
+
' }.join
|
460
|
+
|
461
|
+
detection_sensitivity_js = detection_sensitivity_chart.map { |lib, data| '
|
462
|
+
var element_detection_'+lib.var_safe+' = document.querySelector("#'+lib+' .detection_sensitivity_chart")
|
463
|
+
if(isVisible(element_detection_'+lib.var_safe+')){
|
464
|
+
var detection_'+lib.var_safe+' = new google.visualization.ColumnChart(element_detection_'+lib.var_safe+');
|
465
|
+
detection_'+lib.var_safe+'.draw(
|
466
|
+
google.visualization.arrayToDataTable('+data+'),
|
467
|
+
{
|
468
|
+
legend: {
|
469
|
+
position: "none"
|
470
|
+
},
|
471
|
+
height: Math.round(window.innerHeight * .5),
|
472
|
+
colors: ["' + colors[3] + '"],
|
473
|
+
vAxis: {
|
474
|
+
scaleType: "log",
|
475
|
+
viewWindow: {
|
476
|
+
min: 0.00001
|
477
|
+
},
|
478
|
+
ticks: [0.00001, 0.0001, 0.001, 0.01, 0.1, 1]
|
479
|
+
}
|
480
|
+
}
|
481
|
+
);
|
482
|
+
}
|
483
|
+
'}.join
|
484
|
+
|
485
|
+
distinct_bar_chart_js = distinct_bar_chart.map { |lib, data| '
|
486
|
+
var element_distinct_'+lib.var_safe+' = document.querySelector("#'+lib+' .raw_bar_chart")
|
487
|
+
if(isVisible(element_distinct_'+lib.var_safe+')){
|
488
|
+
var distinct_'+lib.var_safe+' = new google.visualization.ColumnChart(element_distinct_'+lib.var_safe+');
|
489
|
+
distinct_'+lib.var_safe+'.draw(
|
490
|
+
google.visualization.arrayToDataTable('+data+'),
|
491
|
+
{
|
492
|
+
legend: {
|
493
|
+
position: "none"
|
494
|
+
},
|
495
|
+
height: Math.round(window.innerHeight * .5),
|
496
|
+
}
|
497
|
+
);
|
498
|
+
}
|
499
|
+
' }.join
|
500
|
+
|
501
|
+
resampling_bar_chart_js = resampling_bar_chart.map { |lib, data| '
|
502
|
+
var element_resampling_'+lib.var_safe+' = document.querySelector("#'+lib+' .resampling_bar_chart")
|
503
|
+
if(isVisible(element_resampling_'+lib.var_safe+')){
|
504
|
+
var resampling_'+lib.var_safe+' = new google.visualization.ColumnChart(element_resampling_'+lib.var_safe+');
|
505
|
+
resampling_'+lib.var_safe+'.draw(
|
506
|
+
google.visualization.arrayToDataTable('+data+'),
|
507
|
+
{
|
508
|
+
legend: {
|
509
|
+
position: "none"
|
510
|
+
},
|
511
|
+
height: Math.round(window.innerHeight * .5),
|
512
|
+
}
|
513
|
+
);
|
514
|
+
}
|
515
|
+
' }.join
|
516
|
+
|
517
|
+
scatter_charts_js = scatter_charts.map { |lib, d|
|
518
|
+
d.map { |region, data| '
|
519
|
+
var element_chart_scatter_'+lib.var_safe+'_'+region.var_safe+' = document.querySelector("#'+lib+' .'+region+'.scatter")
|
520
|
+
if(isVisible(element_chart_scatter_'+lib.var_safe+'_'+region.var_safe+')){
|
521
|
+
var chart_scatter_'+lib.var_safe+'_'+region.var_safe+' = new google.visualization.ComboChart(element_chart_scatter_'+lib.var_safe+'_'+region.var_safe+');
|
522
|
+
|
523
|
+
var view_'+lib.var_safe+'_'+region.var_safe+' = new google.visualization.DataView(
|
524
|
+
google.visualization.arrayToDataTable(' + data + ')
|
525
|
+
);
|
526
|
+
|
527
|
+
chart_scatter_'+lib.var_safe+'_'+region.var_safe+'.draw(
|
528
|
+
view_'+lib.var_safe+'_'+region.var_safe+',
|
529
|
+
{
|
530
|
+
pointSize: 5,
|
531
|
+
title: "' + region + '",
|
532
|
+
hAxis: {title: "Raw sequencing reads per unique PID"},
|
533
|
+
vAxis: {
|
534
|
+
title: "# of PIDs ",
|
535
|
+
logScale: true
|
536
|
+
},
|
537
|
+
colors: ["' + bool_colors[true] + '"],
|
538
|
+
legend: "none",
|
539
|
+
seriesType: "scatter",
|
540
|
+
series: {
|
541
|
+
1: {
|
542
|
+
type: "line",
|
543
|
+
color: "' + bool_colors[false] + '",
|
544
|
+
pointsVisible: false,
|
545
|
+
}
|
546
|
+
}
|
547
|
+
}
|
548
|
+
);
|
549
|
+
}
|
550
|
+
'}
|
551
|
+
}.join
|
552
|
+
|
553
|
+
html = '
|
554
|
+
<!DOCTYPE html>
|
555
|
+
<html lang="en">
|
556
|
+
<head>
|
557
|
+
<meta charset="UTF-8">
|
558
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
559
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
560
|
+
<title>TCS Log</title>
|
561
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
562
|
+
<script src="https://www.gstatic.com/charts/loader.js"></script>
|
563
|
+
<script>
|
564
|
+
google.charts.load("current", {packages: ["corechart", "treemap", "bar"]});
|
565
|
+
google.charts.setOnLoadCallback(drawChart);
|
566
|
+
|
567
|
+
var isInit = false
|
568
|
+
|
569
|
+
function isVisible(el) {
|
570
|
+
return (el.offsetParent !== null)
|
571
|
+
}
|
572
|
+
|
573
|
+
function drawChart() {
|
574
|
+
|
575
|
+
document.querySelectorAll(".error-message").forEach(element => element.innerHTML = "")
|
576
|
+
|
577
|
+
if(! isInit){
|
578
|
+
isInit = true;
|
579
|
+
window.onresize = drawChart;
|
580
|
+
}
|
581
|
+
|
582
|
+
try{
|
583
|
+
var element_home_chart = document.getElementById("raw-sequence-chart")
|
584
|
+
if(isVisible(element_home_chart)){
|
585
|
+
var home_chart = new google.visualization.ColumnChart(element_home_chart);
|
586
|
+
home_chart.draw(
|
587
|
+
google.visualization.arrayToDataTable('+raw_sequence_data+'),
|
588
|
+
{
|
589
|
+
annotations: {alwaysOutside: true},
|
590
|
+
colors: ["' + bool_colors[true] + '"]
|
591
|
+
}
|
592
|
+
);
|
593
|
+
}'+ paired_raw_js + tree_charts_js + tcs_bar_chart_js + distinct_bar_chart_js + resampling_bar_chart_js + detection_sensitivity_js + scatter_charts_js + '
|
594
|
+
}catch(e){
|
595
|
+
console.log(e);
|
596
|
+
document.querySelectorAll(".error-message").forEach(element =>
|
597
|
+
element.innerHTML = "There was an error displaying your data.<br>To help us improve, please forward this html file to<br>shuntaiz@email.unc.edu"
|
598
|
+
)
|
599
|
+
}
|
600
|
+
}
|
601
|
+
|
602
|
+
function showPage(pageID){
|
603
|
+
document.querySelectorAll(".page").forEach(element => element.classList.add("hidden"))
|
604
|
+
document.getElementById(pageID).classList.remove("hidden")
|
605
|
+
|
606
|
+
document.querySelectorAll(".current_page").forEach(element => element.classList.remove("current_page"));
|
607
|
+
document.getElementById("link_"+pageID).classList.add("current_page");
|
608
|
+
|
609
|
+
drawChart();
|
610
|
+
}
|
611
|
+
</script>
|
612
|
+
</head>
|
613
|
+
<body>
|
614
|
+
<div style="display: flex; flex-direction: column; height: 100vh; width: 100vw; position: fixed; overflow: hidden;">
|
615
|
+
<div style="display: flex; gap: 4px;">
|
616
|
+
<div id="nav" class="card" style="overflow: auto; height: 100vh; text-align: left; margin-top: 0;">
|
617
|
+
<a href="https://primer-id.org" target="_BLANK">
|
618
|
+
<h3 style="margin: 24px; font-weight: 600; color: #333 !important">TCS Log</h3>
|
619
|
+
</a>
|
620
|
+
'+library_links+'
|
621
|
+
</div>
|
622
|
+
<div id="pages" style="flex: 1; overflow: auto; height: 100vh;">
|
623
|
+
<div style="display: flex; flex-direction: column; align-items: center; height: 100vh;" id="basic-statistics" class="page">
|
624
|
+
<div class="error-message"></div>
|
625
|
+
<div class="card" style="margin-top: 16px; width: 100%;">
|
626
|
+
<table id="home-table">
|
627
|
+
<tr>
|
628
|
+
<td>Batch Name</td>
|
629
|
+
<td>'+batch_name+'</td>
|
630
|
+
</tr>
|
631
|
+
<tr>
|
632
|
+
<td>Processed Time</td>
|
633
|
+
<td>'+Time.now.strftime("%m/%d/%Y")+'</td>
|
634
|
+
</tr>
|
635
|
+
<tr>
|
636
|
+
<td>TCS Version</td>
|
637
|
+
<td>'+ViralSeq::TCS_VERSION+'</td>
|
638
|
+
</tr>
|
639
|
+
<tr>
|
640
|
+
<td>viral_seq Version</td>
|
641
|
+
<td>'+ViralSeq::VERSION+'</td>
|
642
|
+
</tr>
|
643
|
+
<tr>
|
644
|
+
<td>Number of Libraries</td>
|
645
|
+
<td>'+lib_names.length.to_s+'</td>
|
646
|
+
</tr>
|
647
|
+
<tr>
|
648
|
+
<td>Total Reads</td>
|
649
|
+
<td>'+total_reads+'</td>
|
650
|
+
</tr>
|
651
|
+
</table>
|
652
|
+
</div>
|
653
|
+
<div class="card" style="margin-top: 16px; width: 100%;">
|
654
|
+
<h2>Raw Sequences Distribution</h2>
|
655
|
+
<div id="raw-sequence-chart"></div>
|
656
|
+
</div>
|
657
|
+
</div>
|
658
|
+
'+library_pages+'
|
659
|
+
</div>
|
660
|
+
</div>
|
661
|
+
</div>
|
662
|
+
</body>
|
663
|
+
<style>
|
664
|
+
body {
|
665
|
+
font-size: 1.2rem;
|
666
|
+
color: #333;
|
667
|
+
}
|
668
|
+
#nav ul {
|
669
|
+
list-style-type: none;
|
670
|
+
}
|
671
|
+
.page {
|
672
|
+
margin: 16px;
|
673
|
+
}
|
674
|
+
.card {
|
675
|
+
padding: 16px;
|
676
|
+
text-align: center;
|
677
|
+
}
|
678
|
+
.hidden {
|
679
|
+
display: none !important;
|
680
|
+
visibility: hidden !important;
|
681
|
+
}
|
682
|
+
.pointer {
|
683
|
+
cursor: pointer;
|
684
|
+
}
|
685
|
+
#home-table {
|
686
|
+
margin: 0 auto;
|
687
|
+
width: 80%;
|
688
|
+
border-collapse: collapse;
|
689
|
+
}
|
690
|
+
#home-table, #home-table th, #home-table td {
|
691
|
+
border: 1px solid black;
|
692
|
+
}
|
693
|
+
#home-table td {
|
694
|
+
padding: 8px;
|
695
|
+
}
|
696
|
+
.current_page {
|
697
|
+
border-bottom: 1px solid blue;
|
698
|
+
}
|
699
|
+
.error-message {
|
700
|
+
background: red !important;
|
701
|
+
color: white;
|
702
|
+
border-radius: 15px;
|
703
|
+
font-weight: bold;
|
704
|
+
font-size: 1.8rem;
|
705
|
+
text-align: center;
|
706
|
+
padding: 1%;
|
707
|
+
margin: 16px auto;
|
708
|
+
}
|
709
|
+
.error-message:empty {
|
710
|
+
display: none;
|
711
|
+
}
|
712
|
+
</style>
|
713
|
+
</html>
|
714
|
+
'.shorten_html
|
715
|
+
|
716
|
+
File.open(File.join(tcs_dir,"log.html"), 'w') { |file| file.write(html) }
|
data/bin/tcs_sdrm
CHANGED
@@ -229,8 +229,9 @@ libs.each do |lib|
|
|
229
229
|
filtered_seq_files.each do |seq_file|
|
230
230
|
filtered_sh = ViralSeq::SeqHash.fa(seq_file)
|
231
231
|
next if filtered_sh.size < 3
|
232
|
-
aligned_sh = filtered_sh.random_select(1000).align
|
232
|
+
aligned_sh = filtered_sh.random_select(1000).align(:Super5)
|
233
233
|
aligned_sh.write_nt_fa(File.join(aln_seq_dir, File.basename(seq_file)))
|
234
|
+
puts 'aligned sequence written.'
|
234
235
|
end
|
235
236
|
|
236
237
|
r_script.gsub!(/PATH_TO_FASTA/,aln_seq_dir)
|
Binary file
|