App-Mimosa
view release on metacpan or search on metacpan
lib/App/Mimosa/Controller/Root.pm view on Meta::CPAN
package App::Mimosa::Controller::Root;
use Moose;
use namespace::autoclean;
use autodie qw/:all/;
use App::Mimosa::Util qw/slurp/;
use File::Slurp qw/write_file/;
use File::Temp qw/tempfile/;
use IO::String;
use File::Spec::Functions;
use Storable 'freeze';
use Digest::SHA1 'sha1_hex';
use Path::Class;
use Bio::SearchIO;
use Bio::SearchIO::Writer::HTMLResultWriter;
use File::Spec::Functions;
use Bio::GMOD::Blast::Graph;
use App::Mimosa::Job;
use App::Mimosa::Database;
use Try::Tiny;
use DateTime;
use HTML::Entities;
use Digest::SHA1 qw/sha1_hex/;
#use Carp::Always;
use Cwd;
BEGIN { extends 'Catalyst::Controller' }
with 'Catalyst::Component::ApplicationAttribute';
#
# Sets the actions in this controller to be registered with no prefix
# so they function identically to actions created in MyApp.pm
#
__PACKAGE__->config(namespace => '');
=head1 NAME
App::Mimosa::Controller::Root - Mimosa Root Controller
=head1 DESCRIPTION
This is the root controller of Mimosa. It defines all the URL's which
Mimosa responds to.
=head1 METHODS
=head2 index
The root page (/)
=cut
sub index :Path :Args(0) {
my ( $self, $c ) = @_;
$c->forward('login');
$c->forward('show_grid');
}
sub show_grid :Local {
my ($self, $c) = @_;
my @sets = $c->model('BCS')->resultset('Mimosa::SequenceSet')->all;
my @setinfo = map { [ $_->mimosa_sequence_set_id, $_->title ] } @sets;
$c->stash->{sequenceset_html} = join '',
map { "<option value='$_->[0]'> $_->[1] </option>" } @setinfo;
$c->stash->{sequence_data_dir} = $self->_app->config->{sequence_data_dir};
$c->stash->{template} = 'index.mason';
$c->stash->{schema} = $c->model('Model::BCS');
# currently, any logged-in user has admin rights
$c->stash->{admin} = 1 if $c->user_exists;
# Must encode HTML entities here to prevent XSS attack
$c->stash->{sequence_input} = encode_entities($c->req->param('sequence_input')) || '';
}
sub login :Local {
lib/App/Mimosa/Controller/Root.pm view on Meta::CPAN
}
# if there is no defline, create one
unless ($sequence =~ m/^>/) {
$sequence = ">web user sequence\n$sequence";
}
$c->stash->{sequence} = $sequence;
write_file $input_file, $sequence;
# we create a file to keep track of what kind raw report format is being generated,
# so later on we can tell Bio::SearchIO which format to parse
$c->stash->{report_format} = $alignment_view;
# prevent race conditions
stat $input_file;
$c->forward('validate');
my @ss_ids;
if ($ids =~ m/,/){
(@ss_ids) = split /,/, $ids;
} else {
@ss_ids = ($ids);
}
$c->stash->{sequence_set_ids} = [ @ss_ids ];
my $db_basename;
if( @ss_ids > 1 ) {
$c->forward('compose_sequence_sets');
$db_basename = catfile($c->stash->{seq_root}, $c->stash->{composite_db_name});
} elsif( @ss_ids == 1) {
my $rs = $c->model('BCS')->resultset('Mimosa::SequenceSet');
my ($ss) = $rs->search({ 'mimosa_sequence_set_id' => $ss_ids[0] })->single;
$db_basename = catfile($c->stash->{seq_root}, $ss->shortname);
} else {
$c->stash->{error} = "The value " . encode_entities($ids) . " does not match any sequence sets";
$c->detach('/input_error');
}
my $j = App::Mimosa::Job->new(
context => $c,
timeout => $self->_app->config->{job_runtime_max} || 5,
job_id => $c->stash->{job_id},
config => $self->_app->config,
# force stringification to avoid arcane broken magic at a distance
db_basename => "$db_basename",
# TODO: fix this properly
alphabet => $c->stash->{alphabet} || 'nucleotide',
output_file => "$output_file",
input_file => "$input_file",
alignment_view => $alignment_view,
map { $_ => $c->req->param($_) || '' }
qw/ program maxhits output_graphs evalue matrix /,
);
# Regardless of it working, the job is now complete
my $rs = $c->model('BCS')->resultset('Mimosa::Job');
$rs->search( { mimosa_job_id => $j->job_id } )->update( { end_time => DateTime->now } );
my $error = $j->run;
if ($error) {
( $c->stash->{error} = $error ) =~ s!\n!<br />!g;
$c->detach( $error =~ /Could not calculate ungapped/i ? '/input_error' : '/error' );
} else {
# stat the output file before opening it in hopes of avoiding
# some kind of bizarre race condition i've been seeing in
# which the file doesn't appear to be visible yet to the web
# process after blast exits.
stat $output_file;
# these are the only formats we can parse and generate an HTML report for
my $format_num_to_name = {
0 => 'blast',
7 => 'blastxml',
8 => 'blasttable',
9 => 'blasttable',
};
my $format = $format_num_to_name->{$c->stash->{report_format}} || '';
my $in = Bio::SearchIO->new(
-format => $format,
-file => "$output_file",
);
die "Bio::SearchIO->new could not read $output_file" unless $in;
my $hit_link = sub {
my ($self, $hit) = @_;
my $name = $hit->name;
if (@ss_ids > 1) {
my $sha1 = $c->stash->{composite_sha1};
return qq{<a href="/api/sequence/sha1/$sha1/$name.fasta">$name</a>};
} else {
my $id = $ss_ids[0] || 1;
return qq{<a href="/api/sequence/id/$id/$name.fasta">$name</a>};
}
};
my $writer = Bio::SearchIO::Writer::HTMLResultWriter->new;
$writer->start_report(sub {''});
$writer->end_report(sub {''});
$writer->hit_link_desc( $hit_link );
$writer->hit_link_align( $hit_link );
my $report = '';
my $out = Bio::SearchIO->new(
-writer => $writer,
-fh => IO::String->new( \$report ),
);
$out->write_result($in->next_result);
# TODO: Fix this stuff upstream
$report =~ s!\Q<CENTER><H1><a href="http://bioperl.org">Bioperl</a> Reformatted HTML of BLASTN Search Report<br> for </H1></CENTER>\E!!g;
$report =~ s!<p><p><hr><h5>Produced by Bioperl .*\$</h5>!!gs;
my $cached_report_file = $self->_temp_file( $c->stash->{job_id}.'.html' );
my $report_html;
lib/App/Mimosa/Controller/Root.pm view on Meta::CPAN
-dstDir => $self->_app->config->{tmp_dir} || "/tmp/mimosa",
-dstURL => "/graphics/",
-imgName => $c->stash->{job_id} . '.png',
);
$graph->showGraph;
$report_html = $graph_html . $report;
$c->stash->{report} = $report_html;
} elsif ($format eq 'blast') {
# Don't show a report if there were no hits.
# The user can always download the raw report if they want.
# This is why we don't assign to $c->stash->{report}
$report_html = $report;
} else {
# The report format is not a plain blast, so just render
# the HTML report with no images
$report_html = $report;
$c->stash->{report} = $report_html;
}
$c->stash->{template} = 'report.mason';
write_file( $cached_report_file, $report_html );
}
}
sub show_cached_report :Private {
my ( $self, $c ) = @_;
my $cached_report_file = $self->_temp_file( $c->stash->{job_id} . '.html' );
if (-e $cached_report_file) {
my $cached_report = slurp($cached_report_file);
$c->stash->{report} = $cached_report;
$c->stash->{template} = 'report.mason';
} else {
$c->stash->{error} = <<ERROR;
Could not find cached report file $cached_report_file !
ERROR
$c->detach('/error');
}
}
sub make_job_id :Private {
my ( $self, $c ) = @_;
my $sha1 = sha1_hex freeze {
params => $c->req->parameters,
uploads => $c->req->uploads,
#TODO: add the user - user => $c->user,
};
my $rs = $c->model('BCS')->resultset('Mimosa::Job');
my $jobs = $rs->search( { sha1 => $sha1 } );
if ($jobs->count == 0) { # not a duplicate job, proceed
my $job = $rs->create({
sha1 => $sha1,
user => $c->user_exists ? $c->user->get('username') : 'anonymous',
start_time => DateTime->now(),
});
$c->stash->{job_id} = $job->mimosa_job_id();
} else { # this is a duplicate, check if it is still running and notify user appropriately
my $job = $jobs->single;
my ($start,$end) = ($job->start_time, $job->end_time);
my $jid = $job->mimosa_job_id;
my $user = $job->user;
# TODO: add more info to the error message
if( $end ) { # already finished
$c->stash->{job_id} = $jid;
$c->detach('/show_cached_report');
} else {
$user ||= 'anonymous';
$c->stash->{error} = <<ERROR;
This job (# $jid) was started at $start by $user and is still running.
ERROR
}
$c->detach('/input_error');
}
}
=head2 default
Standard 404 error page
=cut
sub default :Path {
my ( $self, $c ) = @_;
$c->response->body( 'Nothing to see here' );
$c->response->status(404);
}
=head2 input_error
Standard page for user-input errors.
=cut
sub input_error :Private {
my ( $self, $c ) = @_;
$c->res->status( 400 );
$c->forward('error');
}
sub error :Private {
my ( $self, $c ) = @_;
$c->stash->{template} = 'error.mason';
$c->res->status( 500 ) if ! $c->res->status || $c->res->status == 200;
}
=head2 end
Attempt to render a view, if needed.
=cut
sub end : ActionClass('RenderView') {}
=head1 AUTHOR
( run in 1.132 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )