package NGS;
use Modern::Perl qw(2012); # 5.14
use autodie;
# TODO: exons not sorted naturally; data supplied to tt as chr:location key, not exon
use Dancer2;
use Dancer2::Core::Error;
# use Dancer::Plugin::Database; # doesn't work in D2
use Time::HiRes qw(gettimeofday tv_interval);
use Archive::Extract;
use Data::Dump qw(dump);
use Carp qw(croak);
use Template;
use IO::All;
use autodie;
use NGS::VEP;
our $VERSION = '0.1';
# --- Globals ------------------------------------------------------------------
use constant VEP_SRC_FILENAME => 'data_src'; # form param containing filename
use constant VEP_INPUT_TT => 'vep.tt'; # tmpl contains vep data input form
use constant VEP_OUTPUT_TT => 'vep/result.tt'; # tmpl to handle vep results
#-------------------------------------------------------------------------------
hook before_template_render => sub {
my $tokens = shift;
$tokens->{nsort_labno} = sub {
my $list = shift || return; # warn dump $list;
return sort _sort_by_labno(@$list);
};
$tokens->{nsort_exon} = sub { # can't do this yet - key = chr:location not exon
my $list = shift || return; # warn dump $list;
return sort _sort_by_exon(@$list);
};
};
#-------------------------------------------------------------------------------
get '/' => sub { template 'index' }; # default index page
#-------------------------------------------------------------------------------
get '/vep' => sub {
debug 'GET upload page';
template VEP_INPUT_TT, { title => 'Upload VEP DataFile' };
};
#-------------------------------------------------------------------------------
post '/vep' => sub {
debug 'POST upload page'; # debug "Params are : " . dump( request->params );
my %allparams = params; # debug dump \%allparams;
my $input = {
data_file => upload(VEP_SRC_FILENAME) || undef,
form_opts => \%allparams,
}; # warn dump $input->{data_file};
_validate_user_input($input); # debug 'Validation results: ' . dump($input);
#-- display any errors
if ( exists $input->{upload_error} ) {
debug 'have upload error';
return template VEP_INPUT_TT, {
title => 'Upload Data Error',
warning_message => 'You must upload the file again!',
%$input,
};
}
#-- passed validation
debug 'OK passed the error checks.';
my $src = $input->{data_file}; # debug $data;
my $opts = $input->{form_opts};
# add running environment for VEP debug rules:
$opts->{environment} = dancer_app->environment; # warn $opts->{environment};
my $t0 = [gettimeofday]; # set t0
# send submitted src data to vep script:
my $data = _parse({ src => $src, opts => $opts });
$data->{runtime} = tv_interval $t0, [gettimeofday]; # vep script runtime
# delete temp file (if exists; auto-deleted for .t uploads):
unless ( $input->{data_file}->{temp_fh} ) { # only exists for .t uploads
if ( my $tmp_file = $input->{data_file}->tempname ) {
io($tmp_file)->unlink if -e $tmp_file;
}
}
return template VEP_OUTPUT_TT, { results => $data };
};
#-------------------------------------------------------------------------------
sub _parse {
my $args = shift; # warn dump $args->{opts};
my %h = (
form_opts => $args->{opts},
src_data => $args->{src},
); # warn dump \%h;
my $parser = NGS::VEP->new(%h);
# if zip file, extract (vcf) contents & process:
if ( $h{src_data}{filename} =~ /zip\Z/ ) {
my $src_data = $h{src_data};
# get list of files and extraction path of archive:
my $extract = _unzip_file($src_data); # warn dump $extract;
return $parser->miseq_vcf($extract);
}
else { # is one of permitted files types (.vep, etc):
return $parser->result;
}
}
#-------------------------------------------------------------------------------
sub _validate_user_input {
my $validation_report = shift;
if ( defined $validation_report->{data_file} ) {
debug 'At least there is a file uploaded.';
#------ Check that the file is a valid data file
$validation_report->{upload_error} = 'Not a valid data file!'
unless _validate_file( $validation_report->{data_file} );
}
else {
$validation_report->{upload_error} = 'No data file uploaded!';
}
}
#-------------------------------------------------------------------------------
sub _validate_file {
my $in_file = shift; # warn dump $in_file;
my $cfg = config->{InputFile}; # warn dump $cfg;
my $max_file_size = $cfg->{max_file_size} * 1024 * 1024; # MB
my $file_suffix = $cfg->{file_suffix};
return (
$in_file
&& ( lc $in_file->basename =~ /$file_suffix\Z/ )
&& ( $in_file->size <= $max_file_size )
);
}
#-------------------------------------------------------------------------------
sub _unzip_file {
my $src_data = shift; # warn dump $src_data;
my $tmp_file = $src_data->tempname; # warn $tmp_file;
# my $filename = $src_data->filename; # warn $filename; # don't need it
my $ae = Archive::Extract->new( archive => $tmp_file );
my $ok = $ae->extract( to => '/tmp' ); # warn dump $ok;
if ($ok) {
my $f = $ae->files; # arrayref of zip filenames, relative to unzip location
my @files = map { File::Spec->catfile('/tmp', $_) } @$f; # make absolute
my %h = (
outdir => $ae->extract_path,
files => \@files,
); # warn dump \%h;
io($tmp_file)->unlink; # delete zip; extracted dir deleted later
return \%h;
}
else { die $ae->error }
}
#-------------------------------------------------------------------------------
sub _sort_by_labno {
my ($y1, $r1) = split '_', $a; # warn Dumper [$r1, $y1];
my ($y2, $r2) = split '_', $b; # warn Dumper [$r2, $y2];
return $y1 <=> $y2 || $r1 <=> $r2;
}
#-------------------------------------------------------------------------------
sub _sort_by_exon {
return $a cmp $b;
}
true;