4 my $qtl_analysis_detail_page =
5 CXGN
::Phenome
::QtlAnalysisDetailPage
->new();
7 package CXGN
::Phenome
::QtlAnalysisDetailPage
;
12 use CXGN
::Page
::FormattingHelpers qw
/info_section_html
21 use CXGN
::Phenome
::Population
;
22 use CXGN
::Phenome
::Qtl
;
23 use CXGN
::Phenome
::PopulationDbxref
;
24 use CXGN
::Tools
::WebImageCache
;
25 use CXGN
::People
::PageComment
;
26 use CXGN
::People
::Person
;
27 use CXGN
::Chado
::Publication
;
28 use CXGN
::Chado
::Pubauthor
;
33 use GD
::Graph
::points
;
34 #BEGIN { local $SIG{__WARN__} = sub {}; require GD::Graph::Map }
35 use Statistics
::Descriptive
;
38 use File
::Temp qw
/ tempfile tempdir /;
41 use File
::Path qw
/ mkpath /;
43 use File
::Spec
::Functions qw
/ catfile catdir/;
45 use File
::Slurp qw
/ read_file /;
51 #use Storable qw / store retrieve /;
53 use base qw
/ CXGN::Page::Form::SimpleFormPage CXGN::Phenome::Main/;
55 use CatalystX
::GlobalContext
qw( $c );
60 my $self = $class->SUPER::new(@_);
61 $self->set_script_name("qtl_analysis.pl");
70 $self->set_dbh( CXGN::DB::Connection->new() );
71 my %args = $self->get_args();
72 my $population_id = $args{population_id};
73 my $stock_id = $args{stock_id};
75 #########################
76 # this page needs to be re-written with CXGN::Chado::Stock object
77 # and without SimpleFormPage, since edits should be done on the parent page only
78 #########################
80 unless ( ($population_id and $population_id =~ /^\d+$/) || ($stock_id and $stock_id =~ /^\d+$/) )
82 $c->throw_404("A proper <strong>population id or stock id</strong> argument is missing");
87 $object = CXGN::Phenome::Population->new_with_stock_id($self->get_dbh, $stock_id);
88 $population_id = $object->get_population_id;
91 $object = CXGN::Phenome::Population->new($self->get_dbh, $population_id) ;
94 $self->set_object_id($population_id);
96 CXGN::Phenome::Population->new(
97 $self->get_dbh(), $self->get_object_id()
101 $self->set_primary_key("population_id");
102 $self->set_owners( $self->get_object()->get_owners() );
104 my $trait_id = $args{cvterm_id};
105 $trait_id = undef if $trait_id =~ m/\D+/;
109 $self->set_trait_id($trait_id);
111 $c->throw_404("A proper <strong>cvterm id</strong> argument is missing");
123 my %args = $self->get_args();
125 my $population = $self->get_object();
126 my $population_id = $self->get_object_id();
127 my $type_id = $args{type_id};
128 my $type = $args{type};
129 my $pop_name = $population->get_name();
131 qq |<a href="/qtl/view/$population_id">$pop_name</a> |;
133 my $sp_person_id = $population->get_sp_person_id();
134 my $submitter = CXGN::People::Person->new( $self->get_dbh(),
135 $population->get_sp_person_id() );
137 $submitter->get_first_name() . " " . $submitter->get_last_name();
139 qq |<a href="/solpeople/personal-info.pl?sp_person_id=$sp_person_id">$submitter_name </a> |;
141 my $login_user = $self->get_user();
142 my $login_user_id = $login_user->get_sp_person_id();
145 $self->get_action() =~ /edit|store/
146 && ( $login_user_id = $submitter
147 || $self->get_user()->get_user_type() eq 'curator' )
150 $form = CXGN::Page::Form::Editable->new();
154 $form = CXGN::Page::Form::Static->new();
158 display_name => "Name:",
159 field_name => "name",
160 contents => $pop_link,
164 display_name => "Description: ",
165 field_name => "description",
166 object => $population,
167 getter => "get_description",
168 setter => "set_description",
174 display_name => "Uploaded by: ",
175 field_name => "submitter",
176 contents => $submitter_link,
178 $form->add_hidden( field_name => "population_id",
179 contents => $args{population_id} );
182 field_name => "sp_person_id",
183 contents => $self->get_user()->get_sp_person_id(),
184 object => $population,
185 setter => "set_sp_person_id",
188 $form->add_hidden( field_name => "action", contents => "store" );
190 $self->set_form($form);
192 if ( $self->get_action =~ /view|edit/ )
194 $self->get_form->from_database();
197 elsif ( $self->get_action =~ /store/ )
199 $self->get_form->from_request( $self->get_args() );
208 $self->get_page->jsan_use("thickbox");
211 $self->get_page->add_style( text => <<EOS);
212 a.abstract_optional_show {
217 div.abstract_optional_show {
219 border: 1px solid #9F9FC7;
220 margin: 0.2em 1em 0.2em 1em;
221 padding: 0.2em 0.5em 0.2em 1em;
225 my %args = $self->get_args();
226 my $dbh = $self->get_dbh();
227 my $population = $self->get_object();
228 my $population_id = $self->get_object_id();
229 my $population_name = $population->get_name();
230 my $term_id = $self->get_trait_id();
231 my $term_name = $self->get_trait_name();
233 #used to show certain elements to only the proper users
234 my $login_user = $self->get_user();
235 my $login_user_id = $login_user->get_sp_person_id();
236 my $login_user_type = $login_user->get_user_type();
239 ->header(" SGN: $term_name values in population $population_name");
240 print page_title_html
(
241 "SGN: $term_name values in population $population_name \n");
243 my $population_html = $self->get_edit_link_html() . qq |<a href
="qtl_form.pl">[New QTL Population
]</a><br/>|;
245 #print all editable form fields
246 $population_html .= $self->get_form()->as_table_string();
247 my $population_obj = $self->get_object();
251 "../phenome/qtl_analysis.pl?population_id=$population_id&cvterm_id=$term_id";
252 $args{calling_page
} = $page;
255 my $url_pubmed = qq | http
://www
.ncbi
.nlm
.nih
.gov
/pubmed/|;
257 my @publications = $population->get_population_publications();
259 my $abstract_count = 0;
261 foreach my $pub (@publications)
264 $title, $abstract, $authors, $journal,
265 $pyear, $volume, $issue, $pages,
266 $obsolete, $pub_id, $accession
270 my @dbxref_objs = $pub->get_dbxrefs();
271 my $dbxref_obj = shift(@dbxref_objs);
273 $population_obj->get_population_dbxref($dbxref_obj)->get_obsolete();
275 if ( $obsolete eq 'f' )
277 $pub_id = $pub->get_pub_id();
279 $title = $pub->get_title();
280 $abstract = $pub->get_abstract();
281 $pyear = $pub->get_pyear();
282 $volume = $pub->get_volume();
283 $journal = $pub->get_series_name();
284 $pages = $pub->get_pages();
285 $issue = $pub->get_issue();
287 $accession = $dbxref_obj->get_accession();
289 qq|<a href
="/publication/$pub_id/view" >PMID
:$accession</a
> |;
296 my @pubauthors_ids = $pub->get_pubauthors_ids($pub_id);
298 foreach my $pubauthor_id (@pubauthors_ids)
301 CXGN
::Chado
::Pubauthor
->new( $self->get_dbh,
303 my $last_name = $pubauthor_obj->get_surname();
304 my $first_names = $pubauthor_obj->get_givennames();
305 my @first_names = split( /,/, $first_names );
306 $first_names = shift(@first_names);
307 push @authors, ( "$first_names" . " " . "$last_name" );
308 $authors = join( ", ", @authors );
312 $abstract_view = html_optional_show
(
313 "abstracts$abstract_count",
315 qq|$abstract <b
> <i
>$authors.</i> $journal. $pyear. $volume($issue). $pages.</b
>|,
316 0, #< do not show by default
317 'abstract_optional_show'
318 , #< don't use the default button-like style
322 qq|<div
><a href
="$url_pubmed$accession" target
="blank">$pub_info</a> $title $abstract_view </div
> |;
326 print info_section_html
( title
=> 'Population details',
327 contents
=> $population_html, );
329 my $is_public = $population->get_privacy_status();
330 my ( $submitter_obj, $submitter_link ) = $self->submitter();
333 || $login_user_type eq 'curator'
334 || $login_user_id == $population->get_sp_person_id() )
340 my ( $indl_id, $indl_name, $indl_value ) =
341 $population->get_all_indls_cvterm($term_id);
343 my ( $min, $max, $avg, $std, $count ) =
344 $population->get_pop_data_summary($term_id);
346 for ( my $i = 0 ; $i < @
$indl_name ; $i++ )
352 qq | <a href
="/phenome/individual.pl?individual_id=$indl_id->[$i]">$indl_name->[$i]</a
>|,
358 my ( $phenotype_data, $data_view, $data_download );
359 my $all_indls_count = scalar(@
$indl_name);
363 $phenotype_data = columnar_table_html
(
376 $data_view = html_optional_show
(
379 qq |$phenotype_data|,
380 0, #< don't show data by default
383 qq { Download population
: <span
><a href
="/qtl/download/phenotype/$population_id"><b
>Phenotype data
</b></a> | <a href
="/qtl/download/genotype/$population_id"><b
>Genotype data
</b></a></span
> };
388 $image_pheno, $title_pheno, $image_map_pheno,
391 ( $image_pheno, $title_pheno, $image_map_pheno ) = $self->
392 population_distribution
();
394 $plot_html .= qq | <table cellpadding
= 5><tr
><td
> |;
395 $plot_html .= $image_pheno . $image_map_pheno;
396 $plot_html .= qq | </td
><td
> |;
397 $plot_html .= $title_pheno . qq | <br
/> |;
400 my @phe_summ = ( [ 'No. of obs units', $all_indls_count ],
404 [ 'Standard deviation', $std ]
408 foreach my $phe_summ ( @phe_summ )
410 push @summ, [ map { $_ } ( $phe_summ->[0], $phe_summ->[1] ) ];
413 my $summ_data = columnar_table_html
(
414 headings
=> [ '', ''],
423 $plot_html .= $summ_data;
424 $plot_html .= qq | </td></tr
></table
> |;
428 my ( $qtl_image, $legend);
430 #using standard deviation of 0.01 as an arbitrary cut off to run
431 #qtl analysis. Probably, need to think of better solution.
432 if ( $std >= 0.01 ) {
433 $qtl_image = $self->qtl_plot();
434 $legend = $self->legend();
437 $qtl_image = 'There is no statistically significant phenotypic
438 variation for this trait to run
443 my $qtl_html = qq | <table
><tr
><td width
=70%>$qtl_image</td><td width=30%>$legend</td
></tr></table
> |;
444 my $qtl_effects_ref = $self->qtl_effects();
445 my $explained_variation = $self->explained_variation();
446 my ($qtl_effects_data, $explained_variation_data);
448 if ($qtl_effects_ref)
450 $qtl_effects_data = columnar_table_html
(
451 data
=> $qtl_effects_ref,
461 $qtl_effects_data = "No QTL effects estimates were found for QTL(s) of this trait.";
464 if ($explained_variation) {
465 $explained_variation_data = columnar_table_html
(
466 data
=> $explained_variation,
474 $explained_variation_data = "No explained variation estimates were found for QTL(s) of this trait.";
477 print info_section_html
(
479 contents
=> $qtl_html,
483 print info_section_html
( title
=> 'Variation explained by QTL(s) ( Interacting QTLs model )',
484 contents
=> $explained_variation_data
487 print info_section_html
( title
=> 'QTL effects',
488 contents
=> $qtl_effects_data
491 print info_section_html
(
492 title
=> 'Phenotype frequency distribution',
493 contents
=> $plot_html,
496 print info_section_html
(
497 title
=> 'Download data',
498 contents
=> $data_view . " " . $data_download,
507 "The QTL data for this trait in this population is not public yet.
508 If you would like to know more about this data,
509 please contact the owner of the data: <b>$submitter_link</b>
511 <a href=mailto:sgn-feedback\@sgn.cornell.edu>
512 sgn-feedback\@sgn.cornell.edu</a>.\n";
514 print info_section_html
( title
=> 'QTL(s)',
515 contents
=> $message );
518 print info_section_html
(
519 title
=> 'Publication(s)',
525 if ($population_name)
527 my $page_comment_obj =
528 CXGN
::People
::PageComment
->new( $self->get_dbh(), "population",
530 $self->get_page()->{request
}->uri()."?".$self->get_page()->{request
}->args()
532 print $page_comment_obj->get_html();
536 $self->get_page()->footer();
537 $self->remove_permu_file();
542 # override store to check if a locus with the submitted symbol/name already exists in the database
547 my $population = $self->get_object();
548 my $population_id = $self->get_object_id();
549 my %args = $self->get_args();
551 $self->SUPER::store
(0);
554 sub population_distribution
557 my $pop_id = $self->get_object_id();
558 my $pop = $self->get_object();
559 my $pop_name = $pop->get_name();
560 my $term_name = $self->get_trait_name();
561 my $term_id = $self->get_trait_id();
562 my $dbh = $self->get_dbh();
565 my $basepath = $c->get_conf("basepath");
566 my $tempfile_dir = $c->get_conf("tempfiles_subdir");
568 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) =
569 $self->cache_temp_path();
571 my $cache = CXGN
::Tools
::WebImageCache
->new();
572 $cache->set_basedir($basepath);
573 $cache->set_temp_dir( $tempfile_dir . '/temp_images');
574 $cache->set_expiration_time(259200);
575 $cache->set_key( "popluation_distribution" . $pop_id . $term_id );
576 $cache->set_map_name("popmap$pop_id$term_id");
578 my ( @value, @indl_id, @indl_name );
580 $cache->set_force(0);
581 if ( !$cache->is_valid() )
584 my ( $indl_id, $indl_name, $value ) = $pop->plot_cvterm($term_id);
587 my $stat = Statistics
::Descriptive
::Full
->new();
588 $stat->add_data(@value);
589 my %f = $stat->frequency_distribution(10);
591 my ( @keys, @counts );
593 for ( sort { $a <=> $b } keys %f )
595 my $round = Math
::Round
::Var
->new(0.001);
596 my $key = $round->round($_);
598 push @counts, $f{$_};
601 my $min = $stat->min();
607 my @keys_range = $min . '-' . $keys[0];
610 my $previous_k = $keys[0];
611 my $keys_shifted = shift(@keys);
613 foreach my $k (@keys)
615 $range = $previous_k . '-' . $k;
616 push @keys_range, $range;
620 my $max = $counts[0];
621 foreach my $i ( @counts[ 1 .. $#counts ] )
623 if ( $i > $max ) { $max = $i; }
625 $max = int( $max + ( $max * 0.1 ) );
629 my ( $lower, $upper );
631 foreach my $k (@keys_range)
633 ( $lower, $upper ) = split( /-/, $k );
635 qq | /phenome/indls_range_cvterm
.pl?cvterm_id
=$term_id&
;lower
=$lower&
;upper
=$upper&
;population_id
=$pop_id |;
636 push @c_html, $c_html;
640 my @bar_clr = ("orange");
641 my @data = ( [@keys_range], [@counts] );
642 my $graph = new GD
::Graph
::bars
();
644 $graph->set_title_font('gdTinyFont');
647 x_label
=> "$term_name values",
648 y_label
=> "No. of observation units",
658 x_labels_vertical
=> 1,
664 $cache->set_image_data( $graph->plot( \
@data )->png );
666 my $map = new GD
::Graph
::Map
(
668 hrefs
=> [ \
@c_html ],
670 mapName
=> "popmap$pop_id$term_id",
671 info
=> "%x: %y lines",
673 $cache->set_image_map_data(
674 $map->imagemap( "popimage$pop_id$term_id.png", \
@data ) );
678 my $image_map = $cache->get_image_map_data();
679 my $image = $cache->get_image_tag();
681 qq | Phenotype frequency distribution of experimental lines evaluated
for $term_name. Bars represent the number of experimental lines with
$term_name values greater than the lowest value but less
or equal to the highest value of the range
. |;
683 return $image, $title, $image_map;
689 my $dbh = $self->get_dbh();
690 my $pop_id = $self->get_object_id();
691 my $population = $self->get_object();
692 my $pop_name = $population->get_name();
693 my $mapversion = $population->mapversion_id();
694 my @linkage_groups = $population->linkage_groups();
695 my $term_name = $self->get_trait_name();
696 my $term_id = $self->get_trait_id();
697 my $ac = $population->cvterm_acronym($term_name);
698 my $basepath = $c->get_conf("basepath");
699 my $tempfile_dir = $c->get_conf("tempfiles_subdir");
701 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) = $self->cache_temp_path();
702 my $cache_tempimages = Cache
::File
->new( cache_root
=> $tempimages_path );
703 $cache_tempimages->purge();
705 my ( @marker, @chr, @pos, @lod, @chr_qtl, @peak_markers );
706 my ( $qtl_image, $image, $image_t, $image_url, $image_html, $image_t_url,
707 $thickbox, $title, $l_m, $p_m, $r_m );
709 my $round = Number
::Format
->new();
710 $qtl_image = $self->qtl_images_exist();
711 my $permu_data = $self->permu_file();
712 my $stat_option = $self->qtl_stat_option();
714 unless ( $qtl_image && -s
$permu_data > 1 && $stat_option=~/default/)
716 my ( $qtl_summary, $peak_markers_file ) = $self->run_r();
718 open my $qtl_fh, "<", $qtl_summary or die "can't open $qtl_summary: $!\n";
720 my $header = <$qtl_fh>;
721 while ( my $row = <$qtl_fh> )
723 my ( $marker, $chr, $pos, $lod ) = split( /\t/, $row );
724 push @marker, $marker;
726 push @pos, $round->round($pos, 1);
727 push @lod, $round->round($lod, 2);
730 my @o_lod = sort(@lod);
731 my $max = $o_lod[-1];
735 open my $markers_fh, "<", $peak_markers_file
736 or die "can't open $peak_markers_file: !$\n";
738 $header = <$markers_fh>;
739 while ( my $row = <$markers_fh> )
743 my ($trash, $chr_qtl, $peak_marker ) = split( /\t/, $row );
744 push @chr_qtl, $chr_qtl;
745 push @peak_markers, $peak_marker;
748 my (@h_markers, @chromosomes, @lk_groups);
751 @lk_groups = @linkage_groups;
752 @lk_groups = sort ( { $a <=> $b } @lk_groups );
753 my $random = String
::Random
->new();
754 # my $user_id = $c->user->get_object->get_sp_person_id() if $c->user;
755 # my $qtl_obj = CXGN::Phenome::Qtl->new($user_id);
756 # my ($user_qtl_dir, $user_dir) = $qtl_obj->get_user_qtl_dir($c);
758 for ( my $i = 0 ; $i < @linkage_groups ; $i++ )
760 my $lg = shift(@lk_groups);
762 #my %keys_to_urls=();
764 if ($self->qtl_stat_option() eq 'user params')
766 $key_h_marker = $random->randpattern("CCccCCnnn") . '_'. $lg;
767 # %keys_to_urls = ("user_params_${lg}" => $key_h_marker);
768 # store(\%keys_to_urls, "$user_dir/image_urls");
771 elsif ( $self->qtl_stat_option() eq 'default')
774 $key_h_marker = "$ac" . "_pop_" . "$pop_id" . "_chr_" . $lg;
777 $h_marker = $cache_tempimages->get($key_h_marker)
778 ?
$self->qtl_stat_option eq 'default'
784 push @chromosomes, $lg;
785 $p_m = $peak_markers[$i];
788 my $permu_threshold_ref = $self->permu_values();
789 my %permu_threshold = %$permu_threshold_ref;
791 foreach my $key ( keys %permu_threshold )
793 if ( $key =~ m/^\d./ )
799 my $lod1 = $permu_threshold{$p_keys[0]};
802 qq |/phenome/qtl
.pl?population_id
=$pop_id&
;term_id
=$term_id&
;chr=$lg&
;peak_marker
=$p_m&
;lod
=$lod1|;
804 $cache_tempimages->set( $key_h_marker, $h_marker, '30 days' );
805 push @h_markers, $h_marker;
812 $chr_chr, $image, $image_t,
813 $thickbox, $max_chr, $chr_chr_e, $marker_chr_e,
814 $pos_chr_e, $lod_chr_e
816 my $chrs = ( scalar(@chromosomes) ) + 1;
818 for ( my $i = 1 ; $i < $chrs ; $i++ )
820 my ( @marker_chr, @chr_chr, @pos_chr, @lod_chr, @data, @m_html ) =
822 my ( $marker_chr, $pos_chr, $lod_chr, $max_chr );
824 $h_marker = shift(@h_markers);
826 if ( ( $i == $old_chr_chr ) && ( $i != 12 ) )
828 push @marker_chr, $marker_chr_e;
829 push @chr_chr, $chr_chr_e;
830 push @pos_chr, $round->round($pos_chr_e, 1);
831 push @lod_chr, $round->round($lod_chr_e, 2);
834 my $cache_qtl_plot = CXGN
::Tools
::WebImageCache
->new();
835 $cache_qtl_plot->set_basedir($basepath);
836 $cache_qtl_plot->set_temp_dir( $tempfile_dir . "/temp_images" );
837 # $cache_qtl_plot->set_temp_dir( $tempimages_path);
838 $cache_qtl_plot->set_expiration_time(259200);
841 if ($self->qtl_stat_option() eq 'user params')
843 $cache_qtl_plot->set_key($random->randpattern("CCccCCnnn"));
844 $cache_qtl_plot->set_force(1);
846 elsif ( $self->qtl_stat_option() eq 'default')
848 $cache_qtl_plot->set_key("qtlplot" . $i . "small" . $pop_id . $term_id);
849 $cache_qtl_plot->set_force(0);
852 if ( !$cache_qtl_plot->is_valid() )
855 for ( my $j = 0 ; $j < @marker ; $j++ )
860 if ( $i == $chr_chr )
862 $marker_chr = $marker[$j];
867 push @marker_chr, $marker_chr;
868 push @chr_chr, $chr_chr;
869 push @pos_chr, $round->round($pos_chr, 1);
870 push @lod_chr, $round->round($lod_chr, 2);
872 ( $chr_chr_e, $marker_chr_e, $pos_chr_e, $lod_chr_e ) =
876 elsif ( $i != $chr_chr )
879 $chr_chr_e = $chr[$j];
880 $marker_chr_e = $marker[$j];
881 $pos_chr_e = $pos[$j];
882 $lod_chr_e = $lod[$j];
887 @data = ( [ (@pos_chr) ], [@lod_chr] );
888 my $graph = new GD
::Graph
::lines
( 110, 110 );
889 $graph->set_title_font('gdTinyFont');
892 x_label
=> "Chr $i (cM)",
901 x_labels_vertical
=> 1,
905 $cache_qtl_plot->set_image_data( $graph->plot( \
@data )->png );
909 $image = $cache_qtl_plot->get_image_tag();
910 $image_url = $cache_qtl_plot->get_image_url();
914 my $cache_qtl_plot_t = CXGN
::Tools
::WebImageCache
->new();
915 $cache_qtl_plot_t->set_basedir($basepath);
916 $cache_qtl_plot_t->set_temp_dir( $tempfile_dir . "/temp_images" );
917 # $cache_qtl_plot_t->set_temp_dir( $tempimages_path);
918 $cache_qtl_plot_t->set_expiration_time(259200);
920 if ($self->qtl_stat_option() eq 'user params')
922 $cache_qtl_plot_t->set_key($random->randpattern("CCccccnnn"));
923 $cache_qtl_plot_t->set_force(1);
926 elsif ( $self->qtl_stat_option() eq 'default')
929 $cache_qtl_plot_t->set_key("qtlplot_" . $i . "_thickbox_" . $pop_id . $term_id);
930 $cache_qtl_plot_t->set_force(0);
934 if ( !$cache_qtl_plot_t->is_valid() )
936 my @o_lod_chr = sort { $a <=> $b } @lod_chr;
937 $max_chr = pop(@o_lod_chr);
938 $max_chr = $max_chr + (0.5);
940 my $graph_t = new GD
::Graph
::lines
( 420, 420 );
941 $graph_t->set_title_font('gdTinyFont');
944 x_label
=> "Chromosome $i (cM)",
946 y_max_value
=> $max_chr,
953 x_labels_vertical
=> 1,
957 $cache_qtl_plot_t->set_image_data(
958 $graph_t->plot( \
@data )->png );
962 $image_t = $cache_qtl_plot_t->get_image_tag();
963 $image_t_url = $cache_qtl_plot_t->get_image_url();
966 qq | <a href
="$image_t_url" title
="<a href=$h_marker&qtl=$image_t_url><font color=#f87431><b>>>>>Go to the QTL page>>>> </b></font></a>" class="thickbox" rel
="gallary-qtl"> <img src
="$image_url" alt
="Chromosome $i $image_t_url $image_url" /> </a
> |;
968 $qtl_image .= $thickbox;
970 $old_chr_chr = $chr_chr;
978 Usage: my $file_in = $self->infile_list();
979 Desc: returns an R input tempfile containing a tempfile
980 holding the cvterm acronym, pop id, a filepath to the phenotype dataset file,
981 a filepath to genotype dataset file, a filepath to the permuation file.
982 Ret: an R input tempfile name (with abosulte path)
993 my $dbh = $self->get_dbh();
994 my $pop_id = $self->get_object_id();
995 my $population = $self->get_object();
996 my $term_name = $self->get_trait_name();
997 my $term_id = $self->get_trait_id();
999 my $ac = $population->cvterm_acronym($term_name);
1001 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) =
1002 $self->cache_temp_path();
1004 my $prod_permu_file = $self->permu_file();
1005 my $gen_dataset_file = $population->genotype_file($c);
1006 my $phe_dataset_file = $population->phenotype_file($c);
1007 my $crosstype_file = $self->crosstype_file();
1008 my $stat_file = $self->stat_files();
1010 my $input_file_list_temp =
1012 TEMPLATE
=> "infile_list_${ac}_$pop_id-XXXXXX",
1013 DIR
=> $prod_temp_path,
1016 my $file_in = $input_file_list_temp->filename();
1018 my $file_cvin = File
::Temp
->new(
1019 TEMPLATE
=> 'cvterm_input-XXXXXX',
1020 DIR
=> $prod_temp_path,
1023 my $file_cv_in = $file_cvin->filename();
1025 open my $cv_fh, ">", $file_cv_in or die "can't open $file_cv_in: $!\n";
1028 my $popid_temp = File
::Temp
->new(
1029 TEMPLATE
=> 'popid-XXXXXX',
1030 DIR
=> $prod_temp_path,
1033 my $file_popid = $popid_temp->filename();
1035 open my $popid_fh, ">", $file_popid or die "can't open $file_popid: $!\n";
1036 $popid_fh->print($pop_id);
1038 my $file_in_list = join( "\t",
1039 $file_cv_in, $file_popid,
1040 $gen_dataset_file, $phe_dataset_file,
1041 $prod_permu_file, $crosstype_file, $stat_file);
1043 open my $fi_fh, ">", $file_in or die "can't open $file_in: $!\n";
1044 $fi_fh->print ($file_in_list);
1052 Usage: my ($file_out, $qtl_summary, $peak_markers) = $self->outfile_list();
1053 Desc: returns an R output tempfile containing a tempfile supposed to hold the qtl
1054 mapping output and another tempfile for the qtl peak markers
1055 and the qtl mapping output and qtl peak makers files separately
1056 (convenient for reading their data when plotting the qtl)
1057 Ret: R output file names (with abosulte path)
1067 my $pop_id = $self->get_object_id();
1068 my $population = $self->get_object();
1069 my $term_id = $self->get_trait_id();
1070 my $term_name = $self->get_trait_name();
1071 my $dbh = $self->get_dbh();
1073 my $ac = $population->cvterm_acronym($term_name);
1075 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) =
1076 $self->cache_temp_path();
1078 my $output_file_list_temp =
1080 TEMPLATE
=> "outfile_list_${ac}_$pop_id-XXXXXX",
1081 DIR
=> $prod_temp_path,
1084 my $file_out = $output_file_list_temp->filename();
1086 my $qtl_temp = File
::Temp
->new(
1087 TEMPLATE
=> "qtl_summary_${ac}_$pop_id-XXXXXX",
1088 DIR
=> $prod_cache_path,
1091 my $qtl_summary = $qtl_temp->filename;
1093 my $marker_temp = File
::Temp
->new(
1094 TEMPLATE
=> "peak_markers_${ac}_$pop_id-XXXXXX",
1095 DIR
=> $prod_cache_path,
1099 my $peak_markers = $marker_temp->filename;
1101 my $ci_lod = $population->ci_lod_file($c, $ac);
1102 my $qtl_effects = $population->qtl_effects_file($c, $ac);
1103 my $explained_variation = $population->explained_variation_file($c, $ac);
1104 my $file_out_list = join ( "\t"
1109 ,$explained_variation
1112 open my $fo_fh, ">", $file_out or die "can't open $file_out: $!\n";
1113 $fo_fh->print ($file_out_list);
1115 return $file_out, $qtl_summary, $peak_markers;
1118 =head2 cache_temp_path
1120 Usage: my ($solqtl_cache, $solqtl_tempfiles, $solqtl_temp_images) = $self->cache_temp_path();
1121 Desc: creates the 'solqtl/cache', 'solqtl/tempfiles', and 'solqtl/temp_images', subdirs in the /export/prod/tmp,
1122 Ret: returns the dirs above
1129 sub cache_temp_path
{
1130 my $geno_version = $c->config->{default_genotyping_protocol
};
1131 $geno_version = 'analysis-data' if ($geno_version =~ /undefined/) || !$geno_version;
1132 $geno_version =~ s/\s+//g;
1133 my $tmp_dir = $c->site_cluster_shared_dir;
1134 $tmp_dir = catdir
($tmp_dir, $geno_version);
1136 my $solqtl_dir = catdir
($tmp_dir, 'solqtl');
1137 my $solqtl_cache = catdir
($tmp_dir, 'solqtl', 'cache');
1138 my $solqtl_tempfiles = catdir
($tmp_dir, 'solqtl', 'tempfiles');
1140 my $basepath = $c->config->{basepath
};
1141 my $temp_dir = $c->config->{tempfiles_subdir
};
1143 my $tempimages = catdir
($basepath, $temp_dir, "temp_images" );
1145 mkpath
([$solqtl_cache, $solqtl_tempfiles, $tempimages], 0, 0755);
1146 return $solqtl_cache, $solqtl_tempfiles, $tempimages;
1153 =head2 crosstype_file
1155 Usage: my $cross_file = $self->crosstype_file();
1156 Desc: creates the crosstype file in the /data/prod/tmp/r_qtl/tempfiles,
1158 Ret: crosstype filename (with absolute path)
1165 sub crosstype_file
{
1167 my $pop_id = $self->get_object_id();
1168 my $population = $self->get_object();
1170 my $type_id = $population->get_cross_type_id
1171 or die "population '$pop_id' has no cross_type, does not seem to be the product of a cross!";
1172 my ($cross_type) = $self->get_dbh->selectrow_array(<<'', undef, $type_id);
1175 where cross_type_id
= ?
1177 my $rqtl_cross_type = { 'Back cross' => 'bc',
1179 'RIL (selfing)' => 'rilself',
1180 'RIL (sibling mating)' => 'rilsib'
1182 or die "unknown cross_type '$cross_type' for population '$pop_id'";
1184 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) =
1185 $self->cache_temp_path();
1187 my $cross_temp = File
::Temp
->new(
1188 TEMPLATE
=> "cross_type_${pop_id}-XXXXXX",
1189 DIR
=> $prod_temp_path,
1193 $cross_temp->print( $rqtl_cross_type );
1194 return $cross_temp->filename;
1199 Usage: my ($qtl_summary, $peak_markers) = $self->run_r();
1200 Desc: run R in the cluster; returns the R output files (with abosulate filepath) with qtl mapping data
1212 my ($solqtl_cache, $solqtl_temp, $solqtl_tempimages) = $self->cache_temp_path();
1213 my $prod_permu_file = $self->permu_file();
1214 my $input_file = $self->infile_list();
1215 my ($output_file, $qtl_summary, $peak_markers) = $self->outfile_list();
1217 my $pop_id = $self->get_object_id();
1218 my $trait_id = $self->get_trait_id();
1220 $c->stash->{analysis_tempfiles_dir
} = $solqtl_temp;
1221 $c->stash->{r_temp_file
} = "solqtl-${pop_id}-${trait_id}";
1222 $c->stash->{input_files
} = $input_file;
1223 $c->stash->{output_files
} = $output_file;
1224 $c->stash->{r_script
} = 'R/solGS/qtl_analysis.r';
1226 $c->controller('solGS::AsyncJob')->run_r_script($c);
1228 return $qtl_summary, $peak_markers;
1234 Usage: my $permu_file = $self->permu_file();
1235 Desc: creates the permutation file in the /data/prod/tmp/r_qtl/cache,
1236 if it does not exist yet and caches it for R.
1237 Ret: permutation filename (with abosolute path)
1247 my $dbh = $self->get_dbh();
1248 my $pop_id = $self->get_object_id();
1249 my $population = $self->get_object();
1250 my $pop_name = $population->get_name();
1251 my $term_name = $self->get_trait_name();
1252 my $term_id = $self->get_trait_id();
1254 my $ac = $population->cvterm_acronym($term_name);
1256 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) =
1257 $self->cache_temp_path();
1259 my $file_cache = Cache
::File
->new( cache_root
=> $prod_cache_path );
1261 my $key_permu = "$ac" . "_" . $pop_id . "_permu";
1262 my $filename = "permu_" . $ac . "_" . $pop_id;
1264 my $permu_file = $file_cache->get($key_permu);
1266 unless ($permu_file)
1271 my $permu_file = File
::Spec
->catfile( $prod_cache_path, $filename );
1273 open my $permu_fh, ">", $permu_file or die "can't open $permu_file: !$\n";
1274 $permu_fh->print($permu);
1277 $file_cache->set( $key_permu, $permu_file, '30 days' );
1278 $permu_file = $file_cache->get($key_permu);
1287 Usage: my $permu_values = $self->permu_values();
1288 Desc: reads the permutation output from R,
1289 creates a hash with the probality level as key and LOD threshold as the value,
1291 Ret: a hash ref of the permutation values
1301 my $prod_permu_file = $self->permu_file();
1303 my %permu_threshold;
1305 my $permu_file = fileparse
($prod_permu_file);
1306 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) =
1307 $self->cache_temp_path();
1309 $permu_file = File
::Spec
->catfile( $prod_cache_path, $permu_file );
1311 my $round1 = Math
::Round
::Var
->new(0.1);
1313 open my $permu_fh, "<", $permu_file
1314 or die "can't open $permu_file: !$\n";
1316 my $header = <$permu_fh>;
1318 while ( my $row = <$permu_fh> )
1320 my ( $significance, $lod_threshold ) = split( /\t/, $row );
1321 $lod_threshold = $round1->round($lod_threshold);
1322 $permu_threshold{$significance} = $lod_threshold;
1325 return \
%permu_threshold;
1331 =head2 qtl_images_exist
1333 Usage: my $qtl_images_ref = $self->qtl_images_exist();
1334 Desc: checks and returns a scalar ref if the qtl plots (with thickbox and their links to the comparative viewer) exist in the cache
1335 Ret: scalar ref to the images or undef
1342 sub qtl_images_exist
1345 my $pop_id = $self->get_object_id();
1346 my $population = $self->get_object();
1347 my $pop_name = $population->get_name();
1348 my $term_name = $self->get_trait_name();
1349 my $term_id = $self->get_trait_id();
1350 my $dbh = $self->get_dbh();
1351 my $qtl_image = undef;
1353 my @linkage_groups = $population->linkage_groups();
1354 @linkage_groups = sort ( { $a <=> $b } @linkage_groups );
1356 my $ac = $population->cvterm_acronym($term_name);
1358 my $basepath = $c->get_conf("basepath");
1359 my $tempfile_dir = $c->get_conf("tempfiles_subdir");
1361 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) =
1362 $self->cache_temp_path();
1364 my $cache_tempimages = Cache
::File
->new( cache_root
=> $tempimages_path );
1365 $cache_tempimages->purge();
1367 my $cache_qtl_plot = CXGN
::Tools
::WebImageCache
->new();
1368 $cache_qtl_plot->set_basedir($basepath);
1369 $cache_qtl_plot->set_temp_dir( $tempfile_dir . "/temp_images" );
1371 if ($self->qtl_stat_option eq 'default')
1373 my ( $image, $image_t, $image_url, $image_html, $image_t_url,
1374 $thickbox, $title );
1377 IMAGES
: foreach my $lg (@linkage_groups)
1379 my $key_h_marker = "$ac" . "_pop_" . "$pop_id" . "_chr_" . $lg;
1380 my $h_marker = $cache_tempimages->get($key_h_marker);
1382 my $key = "qtlplot" . $lg . "small" . $pop_id . $term_id;
1383 $cache_qtl_plot->set_key($key);
1385 if ( $cache_qtl_plot->is_valid )
1387 $image = $cache_qtl_plot->get_image_tag();
1388 $image_url = $cache_qtl_plot->get_image_url();
1391 my $key_t = "qtlplot_" . $lg . "_thickbox_" . $pop_id . $term_id;
1392 $cache_qtl_plot->set_key($key_t);
1394 if ( $cache_qtl_plot->is_valid )
1396 $image_t = $cache_qtl_plot->get_image_tag();
1397 $image_t_url = $cache_qtl_plot->get_image_url();
1400 qq | <a href
="$image_t_url" title
= "<a href=$h_marker&qtl=$image_t_url><font color=#f87431><b>>>>Go to the QTL page>>>> </b></font></a>" class="thickbox" rel
="gallary-qtl"> <img src
="$image_url" alt
="Chromosome $lg $image_t_url $image_url" /> </a
> |;
1402 $qtl_image .= $thickbox;
1416 foreach my $lg (@linkage_groups)
1418 my $key_h_marker = $ac . "_pop_" . $pop_id . "_chr_" . $lg;
1419 $cache_tempimages->remove($key_h_marker);
1421 my $key = "qtlplot" . $lg . "small" . $pop_id . $term_id;
1422 $cache_qtl_plot->set_key($key);
1424 if ($cache_qtl_plot->is_valid)
1426 $cache_qtl_plot->destroy();
1428 my $key_t = "qtlplot_" . $lg . "_thickbox_" . $pop_id . $term_id;
1429 $cache_qtl_plot->set_key($key_t);
1431 if ($cache_qtl_plot->is_valid)
1433 $cache_qtl_plot->destroy();
1446 Usage: my $stat_param_files = $self->stat_files();
1447 Desc: creates a master file containing individual files
1448 in /data/prod/tmp/r_qtl for each statistical parameter
1449 which are feed to R.
1450 Ret: an absolute path to the statistical parameter's
1451 master file (and individual files)
1461 my $pop_id = $self->get_object_id();
1462 my $pop = $self->get_object();
1466 $user_id = $c->user->get_object->get_sp_person_id;
1469 $user_id = $pop->get_sp_person_id();
1472 my $qtl = CXGN
::Phenome
::Qtl
->new($user_id);
1473 my $user_stat_file = $qtl->get_stat_file($c, $pop_id);
1475 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) =
1476 $self->cache_temp_path();
1478 open my $user_stat_fh, "<", $user_stat_file or die "can't open file: !$\n";
1482 while (<$user_stat_fh>)
1484 my ( $parameter, $value ) = split( /\t/, $_ );
1486 my $stat_temp = File
::Temp
->new(
1487 TEMPLATE
=> "${parameter}_$pop_id-XXXXXX",
1488 DIR
=> $prod_temp_path,
1491 my $stat_file = $stat_temp->filename;
1493 open my $sf_fh, ">", $stat_file or die "can't open file: !$\n";
1494 $sf_fh->print($value);
1497 $stat_files .= $stat_file . "\t";
1501 my $stat_param_files =
1502 $prod_temp_path . "/" . "stat_temp_files_pop_id_${pop_id}";
1504 open my $stat_param_fh, ">", $stat_param_files or die "can't open file: !$\n";
1505 $stat_param_fh->print ($stat_files);
1508 return $stat_param_files;
1512 =head2 stat_param_hash
1514 Usage: my $stat_param = $self->stat_param_hash();
1515 Desc: creates a hash table (with the statistical parameters (as key) and
1516 their corresponding values) out of a tab delimited
1517 statistical parameters file.
1518 Ret: a hashref for statistical parameter key and value pairs table
1528 my $pop_id = $self->get_object_id();
1529 my $pop = $self->get_object();
1532 $user_id = $c->user->get_object->get_sp_person_id;
1534 $user_id = $pop->get_sp_person_id();
1536 my $qtl = CXGN
::Phenome
::Qtl
->new($user_id);
1537 my $user_stat_file = $qtl->get_stat_file($c, $pop_id);
1539 open my $user_stat_fh, "<", $user_stat_file or die "can't open file: !$\n";
1543 while (<$user_stat_fh>)
1545 my ( $parameter, $value ) = split( /\t/, $_ );
1547 $stat_param{$parameter} = $value;
1551 return \
%stat_param;
1557 my $population = $self->get_object();
1558 my $sp_person_id = $population->get_sp_person_id();
1559 my $submitter = CXGN
::People
::Person
->new( $self->get_dbh(),
1560 $population->get_sp_person_id() );
1561 my $submitter_name =
1562 $submitter->get_first_name() . " " . $submitter->get_last_name();
1563 my $submitter_link =
1564 qq |<a href
="/solpeople/personal-info.pl?sp_person_id=$sp_person_id">$submitter_name</a
> |;
1566 return $submitter, $submitter_link;
1570 #move to qtl or population object
1573 my $pop = $self->get_object();
1576 $user_id = $c->user->get_object->get_sp_person_id;
1578 $user_id = $pop->get_sp_person_id();
1581 my $qtl = CXGN
::Phenome
::Qtl
->new($user_id);
1582 my $stat_file = $qtl->get_stat_file($c, $pop->get_population_id());
1586 open my $sf, "<", $stat_file or die "$! reading $stat_file\n";
1587 while (my $row = <$sf>)
1590 my ( $parameter, $value ) = split( /\t/, $row );
1591 if ($parameter =~/qtl_method/) {$parameter = 'Mapping method';}
1592 if ($parameter =~/qtl_model/) {$parameter = 'Mapping model';}
1593 if ($parameter =~/prob_method/) {$parameter = 'QTL genotype probability method';}
1594 if ($parameter =~/step_size/) {$parameter = 'Genome scan size (cM)';}
1595 if ($parameter =~/permu_level/) {$parameter = 'Permutation significance level';}
1596 if ($parameter =~/permu_test/) {$parameter = 'No. of permutations';}
1597 if ($parameter =~/prob_level/) {$parameter = 'QTL genotype signifance level';}
1598 if ($parameter =~/stat_no_draws/) {$parameter = 'No. of imputations';}
1600 if ( $value eq 'zero' || $value eq 'Marker Regression' )
1605 unless (($parameter =~/No. of imputations/ && !$value ) ||
1606 ($parameter =~/QTL genotype probability/ && !$value ) ||
1607 ($parameter =~/Permutation significance level/ && !$value)
1611 if ($parameter =~/Genome scan/ && $value eq 'zero' || !$value)
1616 push @stat, [map{$_} ($parameter, $value)];
1623 foreach my $st (@stat) {
1624 foreach my $i (@
$st) {
1626 foreach my $s (@stat) {
1627 foreach my $j (@
$s) {
1628 $j =~ s/Maximum Likelihood/Marker Regression/;
1637 my $permu_threshold_ref = $self->permu_values();
1638 my %permu_threshold = %$permu_threshold_ref;
1642 foreach my $key ( keys %permu_threshold )
1644 if ( $key =~ m/^\d./ )
1650 my $lod1 = $permu_threshold{ $keys[0] };
1654 $lod1 = qq |<i
>Not calculated
</i
>|;
1659 map {$_} ('LOD threshold', $lod1)
1666 map {$_} ('Confidence interval', 'Based on 95% Bayesian Credible Interval')
1672 map {$_} ('QTL software', "<a href=http://www.rqtl.org>R/QTL</a>")
1676 map {$_} ('Citation', "<a href=http://www.biomedcentral.com/1471-2105/11/525>solQTL</a>")
1679 my $legend_data = columnar_table_html
(
1693 return $legend_data;
1697 =head2 set_trait_id, get_trait_id
1700 Desc: the 'cvterm id' here is not necessarily a cvterm id,
1701 but may also be user submitted trait id...
1713 return $self->{cvterm_id
};
1717 return $self->{cvterm_id
} = shift;
1720 =head2 get_trait_name
1721 Usage: my $term_name = $self->get_trait_name()
1722 Desc: retrieves the name of the trait whether
1723 it is stored in the user_trait or cvterm table
1724 Return: a trait name
1730 sub get_trait_name
{
1732 my $population = $self->get_object();
1733 my $term_id = $self->get_trait_id();
1734 my $dbh = $c->dbc()->dbh();
1736 my ($term_obj, $term_name);
1737 if ( $population->get_web_uploaded() )
1739 $term_obj = CXGN
::Phenome
::UserTrait
->new( $dbh, $term_id );
1740 $term_name = $term_obj->get_name();
1744 $term_obj = CXGN
::Chado
::Cvterm
->new( $dbh, $term_id );
1745 $term_name = $term_obj->get_cvterm_name();
1755 my $trait_name = $self->get_trait_name();
1756 my $pop = $self->get_object();
1757 $trait_name = $pop->cvterm_acronym($trait_name);
1759 my $file = $pop->qtl_effects_file($c, $trait_name);
1764 my @effects = map { [ split( /\t/, $_) ]} read_file
( $file );
1765 my $trash = shift(@effects);
1767 push @effects, map { [ $_ ] } (" ", "QTL effects interpretation example: 2\@100 means
1768 QTL at linkage group 2 and position 100cM.",
1769 "2\@100:3\@100 means interaction between QTL at linkage
1770 group 2 position 100 cM and QTL at linkage group 3
1771 position 100 cM. 'a' and 'd' stand for additive and domininace
1772 effects, respectively."
1782 sub explained_variation
{
1784 my $trait_name = $self->get_trait_name();
1785 my $pop = $self->get_object();
1786 $trait_name = $pop->cvterm_acronym($trait_name);
1788 my $file = $pop->explained_variation_file($c, $trait_name);
1792 my @anova = map { [ split( /\t/, $_) ]} read_file
( $file );
1793 $anova[0][0] = "Source";
1795 if ( $anova[1][0] eq 'Model')
1797 push @anova, map { [ $_ ] } (" ", "The ANOVA model is based on a single QTL
1798 significant source of variation"
1803 push @anova, map { [ $_ ] } ( " ", "Variance source interpretation example: 2\@100 means
1804 QTL at linkage group 2 and position 100cM.",
1805 "2\@100:3\@100 means interaction between QTL at linkage
1807 100 cM and QTL at linkage group 3 position 100 cM."
1820 sub qtl_stat_option
{
1822 my $pop_id = $self->get_object_id();
1823 my $user_id = $c->user->get_object->get_sp_person_id if $c->user;
1824 my $qtl_obj = CXGN
::Phenome
::Qtl
->new($user_id);
1826 my ($user_qtl_dir, $user_dir) = $qtl_obj->get_user_qtl_dir($c);
1827 my $stat_options_file = "$user_dir/stat_options_pop_${pop_id}.txt";
1829 my $stat_option = -e
$stat_options_file && read_file
($stat_options_file) =~ /Yes/ ?
'default'
1830 : !-e
$stat_options_file ?
'default'
1834 return $stat_option;
1837 sub remove_permu_file
{
1839 my $population = $self->get_object();
1841 if ($self->qtl_stat_option eq 'user params')
1843 my $ac = $population->cvterm_acronym($self->get_trait_name());
1844 my ( $prod_cache_path, $prod_temp_path, $tempimages_path ) =
1845 $self->cache_temp_path();
1847 my $file_cache = Cache
::File
->new( cache_root
=> $prod_cache_path );
1848 my $key_permu = $ac . "_" . $population->get_population_id() . "_permu";
1850 unlink($self->permu_file());
1851 $file_cache->remove($key_permu);