3 SGN::Controller::Qtl- controller for solQTL
7 package SGN
::Controller
::Qtl
;
10 use namespace
::autoclean
;
11 use File
::Spec
::Functions
;
12 use List
::MoreUtils qw
/uniq/;
13 use File
::Temp qw
/ tempfile /;
14 use File
::Path qw
/ mkpath /;
19 use URI
::FromHash
'uri';
22 use Bio
::Chado
::Schema
;
23 use CXGN
::Phenome
::Qtl
;
24 use CXGN
::Phenome
::Population
;
26 BEGIN { extends
'Catalyst::Controller'}
28 sub view
: Path
('/qtl/view') Args
(1) {
29 my ($self, $c, $id) = @_;
30 $c->res->redirect("/qtl/population/$id");
35 sub population
: Path
('/qtl/population') Args
(1) {
36 my ( $self, $c, $id) = @_;
40 $self->is_qtl_pop($c, $id);
41 if ( $c->stash->{is_qtl_pop
} )
44 my $pop = CXGN
::Phenome
::Population
->new($c->dbc->dbh, $id);
45 my $phenotype_file = $pop->phenotype_file($c);
46 my $genotype_file = $pop->genotype_file($c);
48 my $userid = $c->user->get_object->get_sp_person_id if $c->user;
49 $c->stash(template
=> '/qtl/population/index.mas',
51 referer
=> $c->req->path,
54 my $size = -s
$phenotype_file;
57 $self->_show_data($c);
58 $self->_list_traits($c);
59 $self->genetic_map($c);
61 $self->_get_trait_acronyms($c);
66 $c->throw_404("$id is not a QTL population.");
71 $c->throw_404("There is no QTL population for $id");
76 sub download_phenotype
: Path
('/qtl/download/phenotype') Args
(1) {
77 my ($self, $c, $id) = @_;
79 $c->throw_404("<strong>$id</strong> is not a valid population id") if $id =~ m
/\D
/;
81 $self->is_qtl_pop($c, $id);
82 if ($c->stash->{is_qtl_pop
})
84 my $pop = CXGN
::Phenome
::Population
->new($c->dbc->dbh, $id);
85 my $phenotype_file = $pop->phenotype_file($c);
87 unless (!-e
$phenotype_file || -s
$phenotype_file <= 1)
89 my @pheno_data = map { s/,/\t/g; [ $_ ]; } read_file
($phenotype_file);
91 $c->res->content_type("text/plain");
92 $c->res->body(join "", map{ $_->[0]} @pheno_data);
97 $c->throw_404("<strong>$id</strong> is not a QTL population id");
101 sub download_genotype
: Path
('/qtl/download/genotype') Args
(1) {
102 my ($self, $c, $id) = @_;
104 $c->throw_404("<strong>$id</strong> is not a valid population id") if $id =~ m
/\D
/;
106 $self->is_qtl_pop($c, $id);
107 if ($c->stash->{is_qtl_pop
})
110 my $pop = CXGN
::Phenome
::Population
->new($c->dbc->dbh, $id);
111 my $genotype_file = $pop->genotype_file($c);
113 unless (!-e
$genotype_file || -s
$genotype_file <= 1)
115 my @geno_data = map { s/,/\t/g; [ $_ ]; } read_file
($genotype_file);
117 $c->res->content_type("text/plain");
118 $c->res->body(join "", map{ $_->[0]} @geno_data);
123 $c->throw_404("<strong>$id</strong> is not a QTL population id");
127 sub download_correlation
: Path
('/qtl/download/correlation') Args
(1) {
128 my ($self, $c, $id) = @_;
130 $c->throw_404("<strong>$id</strong> is not a valid population id") if $id =~ m
/\D
/;
132 $self->is_qtl_pop($c, $id);
133 if ($c->stash->{is_qtl_pop
})
136 my $corr_file = catfile
($c->path_to($c->config->{cluster_shared_tempdir
}), 'correlation', 'cache', "corre_coefficients_table_${id}");
138 unless (!-e
$corr_file || -s
$corr_file <= 1)
143 foreach ( read_file
($corr_file) )
145 if ($count==1) { $_ = "Traits\t" . $_;}
147 push @corr_data, [ $_ ] ;
150 $c->res->content_type("text/plain");
151 $c->res->body(join "", map{ $_->[0] } @corr_data);
157 $c->throw_404("<strong>$id</strong> is not a QTL population id");
161 sub download_acronym
: Path
('/qtl/download/acronym') Args
(1) {
162 my ($self, $c, $id) = @_;
164 $c->throw_404("<strong>$id</strong> is not a valid population id") if $id =~ m
/\D
/;
166 $self->is_qtl_pop($c, $id);
167 if ($c->stash->{is_qtl_pop
})
169 my $pop = CXGN
::Phenome
::Population
->new($c->dbc->dbh, $id);
170 my $acronym = $pop->get_cvterm_acronyms;
172 $c->res->content_type("text/plain");
173 $c->res->body(join "\n", map{ $_->[1] . "\t" . $_->[0] } @
$acronym);
178 $c->throw_404("<strong>$id</strong> is not a QTL population id");
183 sub _analyze_correlation
{
185 my $pop_id = $c->stash->{pop}->get_population_id();
186 my $pheno_file = $c->stash->{pop}->phenotype_file($c);
187 my $base_path = $c->config->{basepath
};
188 my $temp_image_dir = $c->config->{tempfiles_subdir
};
189 my $r_qtl_dir = $c->config->{solqtl
};
190 my $corre_image_dir = catfile
($base_path, $temp_image_dir, "correlation");
191 my $corre_temp_dir = catfile
($r_qtl_dir, "cache");
195 mkpath
([$corre_temp_dir, $corre_image_dir], 0, 0755);
197 my ($fh_hm, $heatmap_file) = tempfile
( "heatmap_${pop_id}-XXXXXX",
198 DIR
=> $corre_temp_dir,
204 print STDERR
"\nheatmap tempfile: $heatmap_file\n";
206 my ($fh_ct, $corre_table_file) = tempfile
( "corre_table_${pop_id}-XXXXXX",
207 DIR
=> $corre_temp_dir,
213 print STDERR
"\ncorrelation coefficients tempfile: $corre_table_file\n";
215 CXGN
::Tools
::Run
->temp_base($corre_temp_dir);
216 my ($fh_out, $filename);
217 my ( $corre_commands_temp, $corre_output_temp ) =
220 ($fh_out, $filename ) =
223 CXGN
::Tools
::Run
->temp_base(),
224 "corre_pop_${pop_id}-$_-XXXXXX",
232 print STDERR
"\ncorrelation r output tempfile: $corre_output_temp\n";
235 my $corre_commands_file = $c->path_to('/cgi-bin/phenome/correlation.r');
236 copy
( $corre_commands_file, $corre_commands_temp )
237 or die "could not copy '$corre_commands_file' to '$corre_commands_temp'";
241 print STDERR
"\nsubmitting correlation job to the cluster..\n";
242 my $r_process = CXGN
::Tools
::Run
->run_cluster(
245 "--args $heatmap_file $corre_table_file $pheno_file",
246 $corre_commands_temp,
249 working_dir
=> $corre_temp_dir,
250 max_cluster_jobs
=> 1_000_000_000
,
256 print STDERR
"\ndone with correlation analysis..\n";
260 print STDERR
"\nsubmitting correlation job to the cluster gone wrong....\n";
262 $err =~ s/\n at .+//s; #< remove any additional backtrace
263 # # try to append the R output
264 try
{ $err .= "\n=== R output ===\n".file
($corre_output_temp)->slurp."\n=== end R output ===\n" };
265 # die with a backtrace
269 copy
($heatmap_file, $corre_image_dir)
270 or die "could not copy $heatmap_file to $corre_image_dir";
271 copy
($corre_table_file, $corre_image_dir)
272 or die "could not copy $corre_table_file to $corre_image_dir";
274 $heatmap_file = fileparse
($heatmap_file);
275 $heatmap_file = $c->generated_file_uri("correlation", $heatmap_file);
276 $corre_table_file = fileparse
($corre_table_file);
277 $corre_table_file = $c->generated_file_uri("correlation", $corre_table_file);
279 print STDERR
"\nheatmap tempfile after copying to the apps static dir : $heatmap_file\n";
280 print STDERR
"\ncorrelation coefficients after copying to the apps static dir: $corre_table_file\n";
282 $c->stash( heatmap_file
=> $heatmap_file,
283 corre_table_file
=> $corre_table_file
288 sub _correlation_output
{
290 my $pop = $c->{stash
}->{pop};
291 my $base_path = $c->config->{basepath
};
292 my $temp_image_dir = $c->config->{tempfiles_subdir
};
293 my $corre_image_dir = catfile
($base_path, $temp_image_dir, "correlation");
294 my $cache = Cache
::File
->new( cache_root
=> $corre_image_dir);
297 my $key_h = "heat_" . $pop->get_population_id();
298 my $key_t = "corr_table_" . $pop->get_population_id();
299 my $heatmap = $cache->get($key_h);
300 my $corre_table = $cache->get($key_t);
302 print STDERR
"\ncached heatmap file: $heatmap\n";
303 print STDERR
"\ncached correlation coefficients files: $corre_table\n";
307 $self->_analyze_correlation($c);
309 $heatmap = $c->stash->{heatmap_file
};
310 $corre_table = $c->stash->{corre_table_file
};
312 $cache->set($key_h, $heatmap, "30 days");
313 $cache->set($key_t, $corre_table, "30 days");
316 $heatmap = undef if -z
$c->config->{basepath
} . $heatmap;
317 $corre_table = undef if -z
$c->config->{basepath
} . $corre_table;
319 $c->stash( heatmap_file
=> $heatmap,
320 corre_table_file
=> $corre_table,
323 $self->_get_trait_acronyms($c);
329 my $population_id = $c->stash->{pop}->get_population_id();
332 if ($c->stash->{pop}->get_web_uploaded())
334 my @traits = $c->stash->{pop}->get_cvterms();
336 foreach my $trait (@traits)
338 my $trait_id = $trait->get_user_trait_id();
339 my $trait_name = $trait->get_name();
340 my $definition = $trait->get_definition();
342 my ($min, $max, $avg, $std, $count) = $c->stash->{pop}->get_pop_data_summary($trait_id);
344 $c->stash( trait_id
=> $trait_id,
345 trait_name
=> $trait_name
349 my $trait_link = $c->stash->{trait_page
};
351 my $qtl_analysis_page = $c->stash->{qtl_analysis_page
};
352 push @phenotype, [ map { $_ } ( $trait_link, $min, $max, $avg, $count, $qtl_analysis_page ) ];
357 my @cvterms = $c->stash->{pop}->get_cvterms();
358 foreach my $cvterm( @cvterms )
360 my $cvterm_id = $cvterm->get_cvterm_id();
361 my $cvterm_name = $cvterm->get_cvterm_name();
362 my ($min, $max, $avg, $std, $count)= $c->stash->{pop}->get_pop_data_summary($cvterm_id);
364 $c->stash( trait_name
=> $cvterm_name,
365 cvterm_id
=> $cvterm_id
369 my $qtl_analysis_page = $c->stash->{qtl_analysis_page
};
370 my $cvterm_page = $c->stash->{cvterm_page
};
371 push @phenotype, [ map { $_ } ( $cvterm_page, $min, $max, $avg, $count, $qtl_analysis_page ) ];
374 $c->stash->{traits_list
} = \
@phenotype;
377 #given $c and a population id, checks if it is a qtl population and stashes true or false
379 my ($self, $c, $id) = @_;
380 my $qtltool = CXGN
::Phenome
::Qtl
::Tools
->new();
381 my @qtl_pops = $qtltool->has_qtl_data();
383 foreach my $qtl_pop ( @qtl_pops )
385 my $pop_id = $qtl_pop->get_population_id();
386 $pop_id == $id ?
$c->stash(is_qtl_pop
=> 1) && last
387 : $c->stash(is_qtl_pop
=> 0)
395 my $pop_id = $c->stash->{pop}->get_population_id();
398 no warnings
'uninitialized';
399 my $trait_id = $c->stash->{trait_id
};
400 my $cvterm_id = $c->stash->{cvterm_id
};
401 my $trait_name = $c->stash->{trait_name
};
402 my $term_id = $trait_id ?
$trait_id : $cvterm_id;
403 my $graph_icon = qq | <img src
="/documents/img/pop_graph.png" alt
="run solqtl"/> |;
405 $self->_get_owner_details($c);
406 my $owner_name = $c->stash->{owner_name
};
407 my $owner_id = $c->stash->{owner_id
};
409 $c->stash( cvterm_page
=> qq |<a href
="/cvterm/$cvterm_id/view">$trait_name</a
> |,
410 trait_page
=> qq |<a href
="/phenome/trait.pl?trait_id=$trait_id">$trait_name</a
> |,
411 owner_page
=> qq |<a href
="/solpeople/personal-info.pl?sp_person_id=$owner_id">$owner_name</a
> |,
412 guideline
=> qq |<a href
="/qtl/submission/guide">Guideline
</a
> |,
413 phenotype_download
=> qq |<a href
="/qtl/download/phenotype/$pop_id">Phenotype data
</a
> |,
414 genotype_download
=> qq |<a href
="/qtl/download/genotype/$pop_id">Genotype data
</a
> |,
415 corre_download
=> qq |<a href
="/download/phenotypic/correlation/population/$pop_id">Correlation data
</a
> |,
416 acronym_download
=> qq |<a href
="/qtl/download/acronym/$pop_id">Trait
-acronym key
</a
> |,
417 qtl_analysis_page
=> qq |<a href
="/phenome/qtl_analysis.pl?population_id=$pop_id&cvterm_id=$term_id" onclick
="Qtl.waitPage()">$graph_icon</a
> |,
423 sub _get_trait_acronyms
{
426 $c->stash(trait_acronym_pairs
=> $c->stash->{pop}->get_cvterm_acronyms());
430 sub _get_owner_details
{
432 my $owner_id = $c->stash->{pop}->get_sp_person_id();
433 my $owner = CXGN
::People
::Person
->new($c->dbc->dbh, $owner_id);
434 my $owner_name = $owner->get_first_name()." ".$owner->get_last_name();
436 $c->stash( owner_name
=> $owner_name,
437 owner_id
=> $owner_id
444 my $user_id = $c->stash->{userid
};
445 my $user_type = $c->user->get_object->get_user_type() if $c->user;
446 my $is_public = $c->stash->{pop}->get_privacy_status();
447 my $owner_id = $c->stash->{pop}->get_sp_person_id();
451 ($user_id == $owner_id || $user_type eq 'curator') ?
$c->stash(show_data
=> 1)
452 : $c->stash(show_data
=> undef)
456 $is_public ?
$c->stash(show_data
=> 1)
457 : $c->stash(show_data
=> undef)
462 sub set_stat_option
: PathPart
('qtl/stat/option') Chained Args
(0) {
464 my $pop_id = $c->req->param('pop_id');
465 my $stat_params = $c->req->param('stat_params');
466 my $file = $self->stat_options_file($c, $pop_id);
470 my $f = file
( $file )->openw
471 or die "Can't create file: $! \n";
473 if ( $stat_params eq 'default' )
475 $f->print( "default parameters\tYes" );
479 $f->print( "default parameters\tNo" );
482 $c->res->content_type('application/json');
483 $c->res->body({undef});
487 sub stat_options_file
{
488 my ($self, $c, $pop_id) = @_;
489 my $login_id = $c->user()->get_object->get_sp_person_id() if $c->user;
493 my $qtl = CXGN
::Phenome
::Qtl
->new($login_id);
494 my ($temp_qtl_dir, $temp_user_dir) = $qtl->create_user_qtl_dir($c);
495 return catfile
( $temp_user_dir, "stat_options_pop_${pop_id}.txt" );
504 sub qtl_form
: PathPart
('qtl/form') Chained Args
{
505 my ($self, $c, $type, $pop_id) = @_;
507 my $userid = $c->user()->get_object->get_sp_person_id() if $c->user;
511 $c->res->redirect( '/user/login' );
514 $type = 'intro' if !$type;
516 if (!$pop_id and $type !~ /intro|pop_form/ )
518 $c->throw_404("Population id argument is missing");
521 if ($pop_id and $pop_id !~ /^([0-9]+)$/)
523 $c->throw_404("<strong>$pop_id</strong> is not an accepted argument.
524 This form expects an all digit population id, instead of
525 <strong>$pop_id</strong>"
529 $c->stash( template
=> $self->get_template($c, $type),
531 guide
=> qq |<a href
="/qtl/submission/guide">Guideline
</a
> |,
532 referer
=> $c->req->path,
540 my %template_of = ( intro
=> '/qtl/qtl_form/intro.mas',
541 pop_form
=> '/qtl/qtl_form/pop_form.mas',
542 pheno_form
=> '/qtl/qtl_form/pheno_form.mas',
543 geno_form
=> '/qtl/qtl_form/geno_form.mas',
544 trait_form
=> '/qtl/qtl_form/trait_form.mas',
545 stat_form
=> '/qtl/qtl_form/stat_form.mas',
546 confirm
=> '/qtl/qtl_form/confirm.mas'
548 return \
%template_of;
553 my ($self, $c, $type) = @_;
554 return $self->templates->{$type};
557 sub submission_guide
: PathPart
('qtl/submission/guide') Chained Args
(0) {
559 $c->stash(template
=> '/qtl/submission/guide/index.mas');
564 my $mapv_id = $c->stash->{pop}->mapversion_id();
565 my $map = CXGN
::Map
->new( $c->dbc->dbh, { map_version_id
=> $mapv_id } );
566 my $map_name = $map->get_long_name();
567 my $map_sh_name = $map->get_short_name();
569 $c->stash( genetic_map
=> qq | <a href
=/cview/map.pl?map_version_id
=$mapv_id>$map_name ($map_sh_name)</a
> | );
573 sub search_help
: PathPart
('qtl/search/help') Chained Args
(0) {
575 $c->stash(template
=> '/qtl/search/help/index.mas');
578 sub show_search_results
: PathPart
('qtl/search/results') Chained Args
(0) {
580 my $trait = $c->req->param('trait');
581 $trait =~ s/(^\s+|\s+$)//g;
584 my $rs = $self->search_qtl_traits($c, $trait);
588 my $rows = $self->mark_qtl_traits($c, $rs);
590 $c->stash(template
=> '/qtl/search/results.mas',
592 query
=> $c->req->param('trait'),
594 page_links
=> sub {uri
( query
=> { trait
=> $c->req->param('trait'), page
=> shift } ) }
599 $c->stash(template
=> '/qtl/search/results.mas',
608 sub search_qtl_traits
{
609 my ($self, $c, $trait) = @_;
614 my $sp_person_id = $c->user() ?
$c->user->get_object()->get_sp_person_id() : undef;
615 my $schema = $c->dbic_schema("Bio::Chado::Schema", undef, $sp_person_id);
616 my $cv_id = $schema->resultset("Cv::Cv")->search(
617 {name
=> 'solanaceae_phenotype'}
620 $rs = $schema->resultset("Cv::Cvterm")->search(
621 { name
=> { 'LIKE' => '%'.$trait .'%'},
625 columns
=> [ qw
/ cvterm_id name definition / ]
628 page
=> $c->req->param('page') || 1,
637 sub mark_qtl_traits
{
638 my ($self, $c, $rs) = @_;
647 my $qtltool = CXGN
::Phenome
::Qtl
::Tools
->new();
648 my $yes_mark = qq |<font size
=4 color
="#0033FF"> ✓</font> |;
649 my $no_mark = qq |<font size
=4 color
="#FF0000"> X
</font
> |;
651 while (my $cv = $rs->next)
653 my $id = $cv->cvterm_id;
654 my $name = $cv->name;
655 my $def = $cv->definition;
657 if ( $qtltool->is_from_qtl( $id ) )
659 push @rows, [ qq | <a href
="/cvterm/$id/view">$name</a
> |, $def, $yes_mark ];
664 push @rows, [ qq | <a href
="/cvterm/$id/view">$name</a
> |, $def, $no_mark ];
672 sub qtl_traits
: PathPart
('qtl/traits') Chained Args
(1) {
673 my ($self, $c, $index) = @_;
675 if ($index =~ /^\w{1}$/)
677 my $traits_list = $self->map_qtl_traits($c, $index);
679 $c->stash( template
=> '/qtl/traits/index.mas',
681 traits_list
=> $traits_list
686 $c->res->redirect('/search/qtl');
690 sub all_qtl_traits
: PathPart
('qtl/traits') Chained Args
(0) {
692 $c->res->redirect('/search/qtl');
695 sub filter_qtl_traits
{
696 my ($self, $index) = @_;
698 my $qtl_tools = CXGN
::Phenome
::Qtl
::Tools
->new();
699 my ( $all_traits, $all_trait_d ) = $qtl_tools->all_traits_with_qtl_data();
709 my ($self, $c, $index) = @_;
711 my $traits_list = $self->filter_qtl_traits($index);
716 foreach my $trait (@
{$traits_list})
718 my $cvterm = CXGN
::Chado
::Cvterm
::get_cvterm_by_name
( $c->dbc->dbh, $trait );
719 my $cvterm_id = $cvterm->get_cvterm_id();
726 qq |<a href
=/cvterm/$cvterm_id/view>$trait</a> |
732 my $t = CXGN
::Phenome
::UserTrait
->new_with_name( $c->dbc->dbh, $trait );
733 my $trait_id = $t->get_user_trait_id();
738 qq |<a href
=/phenome/trait
.pl?trait_id
=$trait_id>$trait</a
> |
745 return \
@traits_urls;
748 __PACKAGE__
->meta->make_immutable;