Skip to content

Commit 356898e

Browse files
lskatzedlb-sneakernet
andauthored
Update emails (#72)
* ignore more perl libs and test files * update saveFailedGenomes to work with new fastq metrics * remove featureio * html multipart email * table sorted with footer * bump version to 0.27.1 * updated base64 * bump versions --------- Co-authored-by: edlb-sneakernet <edlb-sneakernet@scicomp>
1 parent c65e62d commit 356898e

File tree

4 files changed

+165
-42
lines changed

4 files changed

+165
-42
lines changed

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,28 @@ lib/perl5/Try
3434
lib/perl5/x86_64-linux-thread-multi
3535
lib/perl5/GD
3636
lib/perl5/perl5
37+
lib/perl5/App
38+
lib/perl5/Statistics
39+
lib/perl5/TAP
40+
lib/perl5/Tree
41+
lib/perl5/XML
3742

3843
t/M00123-18-001-test/SneakerNet/
3944
t/M00123-18-001-test/readMetrics.tsv
4045
t/M00123-18-001-test/samples.tsv
46+
t/M00123-18-001-asm
4147

4248
t/M00123-18-001-test/contaminated_1.fastq.gz
4349
t/M00123-18-001-test/contaminated_2.fastq.gz
4450
t/kraken-database
51+
t/bioprojectSurveillance*
4552

4653
db/*
4754
db/fasta/*
4855

56+
/env
57+
/findModules.pl
58+
4959
bin/bdf2gdfont.pl
5060

5161
man

SneakerNet.plugins/emailWhoever.pl

Lines changed: 136 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
use POSIX qw/strftime/;
1717
use IO::Compress::Zip qw(zip $ZipError);
1818

19+
use MIME::Base64;
20+
1921
$ENV{PATH}="$ENV{PATH}:/opt/cg_pipeline/scripts";
2022

2123
use Config::Simple;
2224
use SneakerNet qw/exitOnSomeSneakernetOptions recordProperties readConfig passfail command logmsg version/;
2325
use List::MoreUtils qw/uniq/;
2426

25-
our $VERSION = "3.4";
27+
our $VERSION = "3.7";
2628
our $CITATION= "Email whoever by Lee Katz";
2729

2830
my $snVersion=version();
@@ -159,35 +161,63 @@ sub emailWhoever{
159161
logmsg "To: $to";
160162
my $from=$$settings{from} || die "ERROR: need to set 'from' in the settings.conf file!";
161163
my $subject="$runName QC";
162-
my $body ="Please see below for QC information on $runName.\n\n";
164+
165+
my $body ="<div>\n";
166+
$body.="Please see below for QC information on $runName.\n\n";
163167
$body.="For more details, please see the other attachments.\n";
164-
$body.=" - TSV files can be opened in Excel\n";
165-
$body.=" - LOG files can be opened in Wordpad, Notepad++, or VSCode\n";
166-
$body.=" - HTML files can be opened in Edge\n";
167-
$body.=" - Full path: ".realpath($dir)."/SneakerNet\n";
168+
$body.="<ul>\n";
169+
$body.=" <li>TSV files can be opened in Excel</li>\n";
170+
$body.=" <li>LOG files can be opened in Wordpad, Notepad++, or VSCode</li>\n";
171+
$body.=" <li>HTML files can be opened in Edge</li>\n";
172+
$body.=" <li>Full path: ".realpath($dir)."/SneakerNet</li>\n";
173+
$body.="</ul>\n";
168174
$body.="\nThis message was brought to you by SneakerNet v$snVersion!\n";
169-
$body.="Documentation can be found at https://github.com/lskatz/SneakerNet\n";
175+
$body.="<p>Documentation can be found at https://github.com/lskatz/SneakerNet</p>\n";
176+
$body.="</div>\n";
170177

171178
# Failure messages in the body
172-
$body.="\nAny samples that have failed QC as shown in passfail.tsv are listed below.\n";
179+
$body.="<div>\n";
180+
$body.="Any samples that have failed QC as shown in passfail.tsv are listed below.\n";
181+
$body.="<ul>\n";
173182
for my $fastq(keys(%$failure)){
174183
my $failureMessage="";
175184
for my $failureCategory(keys(%{$$failure{$fastq}})){
176185
if($$failure{$fastq}{$failureCategory} == 1){
177-
$failureMessage.=$fastq."\n";
186+
$failureMessage.=" <li>$fastq</li>\n";
178187
last; # just list a given failed fastq once
179188
}
180189
}
181190
$body.=$failureMessage;
182191
}
192+
$body.="</ul></div>\n";
193+
194+
$body = tsvToHtml("$dir/SneakerNet/forEmail/QC_summary.tsv", $settings);
195+
$body .= "<p style='font:smaller;'>\n";
196+
$body .= "This message was brought to you by SneakerNet v$snVersion!\n";
197+
$body .= "Documentation can be found at <a href='https://github.com/lskatz/SneakerNet'>github.com/lskatz/SneakerNet</a>.\n";
198+
$body .= "</p>\n";
199+
200+
201+
# https://stackoverflow.com/a/11725308
202+
my $mailpart = generate_uuid();
203+
my $mailpart_body = generate_uuid();
183204

184205
my $emailFile = "$$settings{tempdir}/email.txt";
185206
open(my $fh, ">", $emailFile) or die "ERROR: could not write to $emailFile: $!";
186207
print $fh "To: $to\n";
187208
print $fh "From: $from\n";
188209
print $fh "Subject: $subject\n";
210+
print $fh "MIME-Version: 1.0\n";
211+
print $fh "Content-Type: multipart/mixed; boundary=\"$mailpart\"\n";
212+
print $fh "\n";
213+
print $fh "--$mailpart\n";
214+
print $fh "Content-Type: multipart/alternative; boundary=\"$mailpart_body\"\n";
189215
print $fh "\n";
216+
print $fh "--$mailpart_body\n";
217+
print $fh "Content-Type: text/html; charset=\"utf-8\"\n";
218+
print $fh "Content-Disposition: inline\n";
190219
print $fh "$body\n";
220+
print $fh "--$mailpart_body--\n";
191221

192222
# Save a list of files to be attached
193223
my @attachment;
@@ -208,7 +238,7 @@ sub emailWhoever{
208238
my @finalAttachment;
209239
for my $file(@attachment){
210240
if(-s $file > 1e7){
211-
logmsg "NOTE: $file is too big. I will not attach it.";
241+
logmsg "WARNING: $file is too big. I will not attach it.";
212242
} else {
213243
push(@finalAttachment, $file);
214244
}
@@ -226,7 +256,7 @@ sub emailWhoever{
226256

227257
# Finally, attach the files
228258
for my $file(@finalAttachment){
229-
append_attachment($fh, $file);
259+
append_attachment($fh, $file, $mailpart);
230260
}
231261

232262
close $fh;
@@ -240,6 +270,92 @@ sub emailWhoever{
240270
# Utility subs #
241271
################
242272

273+
sub generate_uuid {
274+
my @chars = ('a'..'f', 0..9);
275+
my $uuid = '';
276+
277+
$uuid .= $chars[rand @chars] for 1..8;
278+
$uuid .= '-';
279+
$uuid .= $chars[rand @chars] for 1..4;
280+
$uuid .= '-';
281+
$uuid .= $chars[rand @chars] for 1..4;
282+
$uuid .= '-';
283+
$uuid .= $chars[rand @chars] for 1..4;
284+
$uuid .= '-';
285+
$uuid .= $chars[rand @chars] for 1..12;
286+
287+
return $uuid;
288+
}
289+
290+
# Transform a tsv file into an html string
291+
sub tsvToHtml{
292+
my($tsv, $settings) = @_;
293+
294+
my $html;
295+
296+
my @footer;
297+
298+
$html .= "<!-- START $tsv -->\n";
299+
300+
$html .= "<table style='border:black solid 1px;'>";
301+
302+
my @evenOddBackground = ('#EEE','#CCC');
303+
304+
# Read the table and divvy it up into header, body, footer
305+
my(@body, $footer);
306+
open(my $fh, "<", $tsv) or die "ERROR: could not read $tsv: $!";
307+
my $header = <$fh>;
308+
chomp($header);
309+
my @header = split(/\t/, lc($header));
310+
while(my $line = <$fh>){
311+
chomp($line);
312+
my @F = split(/\t/, $line);
313+
my %F;
314+
@F{@header} = @F;
315+
if($line =~ /^#/){
316+
$line =~ s/^#\s*//;
317+
push(@footer, $line);
318+
} else {
319+
push(@body, \%F);
320+
}
321+
}
322+
close $fh;
323+
324+
# Sort the body
325+
@body = sort{
326+
$$a{score} <=> $$b{score} ||
327+
$$a{sample} cmp $$b{sample}
328+
} @body;
329+
330+
$html .= "<thead><tr style='background:#333;'>\n";
331+
$html .= "<td>" . join("</td><td>", @header) . "</td>\n";
332+
$html .= "</tr></thead>\n";
333+
for my $hash(@body){
334+
# Background color is determined by running the line number mod number of colors
335+
my $background = $evenOddBackground[$. % scalar(@evenOddBackground)];
336+
337+
$html .= "<tr style='background-color:$background;'>\n";
338+
for my $h(@header){
339+
$html .= " <td>$$hash{$h}</td>\n";
340+
}
341+
$html .= "</tr>\n";
342+
}
343+
$html .= "</table>\n";
344+
345+
# Footer lines
346+
if(@footer){
347+
$html .= "<ul style='font-size:smaller;color:#333;'>\n";
348+
for my $line(@footer){
349+
$html .= " <li>$line</li>\n";
350+
}
351+
$html .= "</ul>\n";
352+
}
353+
354+
$html .= "<!-- END $tsv -->\n";
355+
356+
return $html;
357+
}
358+
243359
# http://stackoverflow.com/a/20359734
244360
sub flatten {
245361
map { ref $_ ? flatten(@{$_}) : $_ } @_;
@@ -289,22 +405,21 @@ sub zip_file {
289405

290406
# Add an attachment to an email file handle
291407
sub append_attachment {
292-
my ($fh, $file_path) = @_;
408+
my ($fh, $file_path, $separator) = @_;
293409

294410
# Encode the attachment content using base64 encoding
295411
my $attachment_name = basename($file_path);
296-
297-
open(my $attachment_fh, "<", $file_path) or die "Failed to open attachment file $file_path: $!";
298-
binmode $attachment_fh;
299-
my $attachment_content = do { local $/; <$attachment_fh> };
300-
close $attachment_fh;
301-
302-
my $encoded_content = pack("u", $attachment_content);
412+
my $attachment_ext = $attachment_name;
413+
$attachment_ext =~ s/.+\.//;
414+
my $encoded_content = encode_base64(`cat $file_path`);
303415
die "Failed to encode attachment content from $file_path: $!" if $?;
304416

305-
print $fh "begin 644 $attachment_name\n";
417+
print $fh "--$separator\n";
418+
print $fh "Content-Type: application/$attachment_ext; name=\"$attachment_name\"\n";
419+
print $fh "Content-Transfer-Encoding: base64\n";
420+
print $fh "Content-Disposition: attachment; filename=\"$attachment_name\"\n";
421+
print $fh "\n";
306422
print $fh $encoded_content . "\n";
307-
print $fh "end\n";
308423

309424
# Print a newline to separate MIME parts
310425
print $fh "\n";

SneakerNet.plugins/sn_saveFailedGenomes.pl

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99
use File::Temp qw/tempdir/;
1010
use File::Copy qw/mv cp/;
1111
use Bio::SeqIO;
12-
use Bio::FeatureIO::gff;
1312

1413
use FindBin;
1514
use lib "$FindBin::RealBin/../lib/perl5";
1615
use SneakerNet qw/exitOnSomeSneakernetOptions recordProperties readConfig samplesheetInfo_tsv command logmsg fullPathToExec/;
1716

18-
our $VERSION = "1.4.1";
17+
our $VERSION = "1.5.0";
1918
our $CITATION = "Save failed genomes by Lee Katz";
2019

2120
# For any warnings in the SN report
@@ -77,8 +76,7 @@ sub saveGenomes{
7776
chomp;
7877
my %F;
7978
@F{@header}=split(/\t/,$_);
80-
$F{File} = basename($F{File});
81-
$readMetrics{$F{File}} = \%F;
79+
$readMetrics{$F{Sample}} = \%F;
8280
}
8381
close READMETRICS;
8482

@@ -89,26 +87,23 @@ sub saveGenomes{
8987

9088
# Get the name of all files linked to this file through the sample.
9189
$$sampleInfo{$samplename}{fastq}//=[]; # Set {fastq} to an empty list if it does not exist
92-
my @file=@{$$sampleInfo{$samplename}{fastq}};
93-
for my $fastq(@file){
94-
my $fastqMetrics = $readMetrics{basename($fastq)};
95-
96-
# Coverage
97-
if($$fastqMetrics{coverage} eq '.'){ # dot means coverage is unknown
98-
$totalCoverage = -1; # -1 means 'unknown' coverage
99-
logmsg "$fastq unknown coverage" if($$settings{debug});
100-
} else {
101-
$$fastqMetrics{coverage} ||= 0; # force it to be a number if it isn't already
102-
$totalCoverage += $$fastqMetrics{coverage};
103-
logmsg "Sample $samplename += $$fastqMetrics{coverage}x => ${totalCoverage}x" if($$settings{debug});
104-
}
90+
91+
my $fastqMetrics = $readMetrics{$samplename};
92+
93+
if($$fastqMetrics{coverage} eq '.'){ # dot means coverage is unknown
94+
$totalCoverage = -1; # -1 means 'unknown' coverage
95+
logmsg "$samplename unknown coverage" if($$settings{debug});
96+
} else {
97+
$$fastqMetrics{coverage} ||= 0; # force it to be a number if it isn't already
98+
$totalCoverage = $$fastqMetrics{coverage};
99+
logmsg "Sample $samplename = ${totalCoverage}x" if($$settings{debug});
105100
}
106101

107102
if(
108103
$totalCoverage >= $$settings{coverage}
109104
&& $totalCoverage < $$sampleInfo{$samplename}{taxonRules}{coverage}
110105
){
111-
$saved{$samplename} = \@file;
106+
$saved{$samplename} = $$sampleInfo{$samplename}{fastq}
112107
}
113108
}
114109

@@ -142,11 +137,14 @@ sub rsync{
142137
my $subfolder = "$$sample{taxonRules}{dest_subfolder}/QC_fails";
143138
my $fileString = join(" ", @{$$sample{fastq}});
144139

145-
my $command = "rsync -av -q --no-motd -av --no-g --copy-links --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r $fileString $$settings{transfer_destination_string}/$subfolder/";
140+
my $command = "rsync -e 'ssh -q' -av --no-motd --no-g --copy-links --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r $fileString $$settings{transfer_destination_string}/$subfolder/";
146141
if($$settings{debug}){
147142
logmsg "COMMAND: $command";
148143
} else {
149-
command("rsync -q --no-motd -av --no-g --copy-links --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r $fileString $$settings{transfer_destination_string}/$subfolder/");
144+
system($command);
145+
if($?){
146+
die "ERROR: could not rsync $fileString ==> $$settings{transfer_destination_string}/$subfolder/: $!";
147+
}
150148
}
151149

152150
return 1;

lib/perl5/SneakerNet.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ TODO
3636
3737
=cut
3838

39-
our $VERSION = version->declare('0.27.0');
39+
our $VERSION = version->declare('0.27.2');
4040
our %rankName = (S=>'species', G=>'genus', F=>'family', O=>'order', C=>'class', P=>'phylum', K=>'kingdom', D=>'domain', U=>'unclassified');
4141
our @rankOrder= qw(S G F O C P K D U);
4242
our %rankOrder= (S=>0, G=>1, F=>2, O=>3, C=>4, P=>5, K=>6, D=>7, U=>8);

0 commit comments

Comments
 (0)