package SGX::TFSDisplay; use strict; use warnings; use base qw/SGX::Strategy::Base/; require Math::BigInt; require Math::BigFloat; use JSON qw/decode_json/; use SGX::Abstract::Exception (); use SGX::Util qw/car count_bits before_dot/; use List::Util qw[min max]; require SGX::DBHelper; #=== FUNCTION ================================================================ # NAME: get_tfs # PURPOSE: Get total flagsum (TFS), which is a sum of absolute flagsum and # directional flagsum. # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: ???? # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub get_tfs { my ( $abs_fs, $dir_fs, $num ) = @_; return sprintf( "$abs_fs.%0${num}s", Math::BigInt->badd( substr( unpack( 'b32', pack( 'V', $abs_fs ) ), 0, $num ), substr( unpack( 'b32', pack( 'V', $dir_fs ) ), 0, $num ) ) ); } #=== FUNCTION ================================================================ # NAME: breakdown_tfs # PURPOSE: Get an array with a breakdown of the TFS for each experiment. # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: ???? # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub breakdown_tfs { my ( $abs_fs, $dir_fs, $num ) = @_; my $TFS_sum= sprintf "%0${num}s", Math::BigInt->badd( substr( unpack( 'b32', pack( 'V', $abs_fs) ), 0, $num), substr( unpack( 'b32', pack( 'V', $dir_fs ) ), 0, $num ) ); my @brokendown_tfs; #push @brokendown_tfs,$TFS_sum; my @TFS_sum_array = split(//, $TFS_sum); for( my $ec=0; $ec<$num;$ec++){ #push @brokendown_tfs," "; #push @brokendown_tfs,$ec; my $powerof2_ec= ($TFS_sum_array[$ec] == 0) ? 0 :2**$ec; my $left_tfs_length=$ec; my $left_tfs = '0' x ${left_tfs_length}; my $right_tfs_len=$num - $ec-1; my $right_tfs = '0' x ${right_tfs_len}; #it turns out I had things backwards my $my_new_tfs= sprintf "%s.%s%s%s",$powerof2_ec,$left_tfs,$TFS_sum_array[$ec],$right_tfs; #push @brokendown_tfs,substr( unpack( 'b32', pack( 'V', $ec) ),0,$eid_count); #push @brokendown_tfs,$TFS_sum_array[$ec]; push @brokendown_tfs,$my_new_tfs; } #debugging #my $joint_string=join " - ", @brokendown_tfs; #printf $joint_string; return @brokendown_tfs; } #=== CLASS METHOD ============================================================ # CLASS: TFSDisplay # METHOD: init # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub init { my $self = shift; $self->SUPER::init(); $self->loadDataFromSubmission(); # sets _format attribute $self->set_attributes( _title => $self->get_subset_name(), _dbHelper => SGX::DBHelper->new( delegate => $self ) ); # find out what the current project is set to $self->{_dbHelper}->getSessionOverrideCGI(); # warning: the line below affects which hook gets called $self->set_action( $self->{_format} || '' ); $self->register_actions( CSV => { head => 'CSV_head', body => 'CSV_body' }, HTML => { head => 'default_head', body => 'default_body' } ); return $self; } #=== CLASS METHOD ============================================================ # CLASS: TFSDisplay # METHOD: CSV_head # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub CSV_head { my $self = shift; my ( $s, $js_src_yui, $js_src_code ) = @$self{qw{_UserSession _js_src_yui _js_src_code}}; #Clear our headers so all we get back is the CSV file. print $self->{_cgi}->header( -type => 'text/csv', -attachment => 'results.csv', -cookie => $s->cookie_array() ); $s->commit(); $self->getPlatformData(); $self->loadDataCSV(); $self->displayDataCSV(); exit; } #=== CLASS METHOD ============================================================ # CLASS: TFSDisplay # METHOD: default_head # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: Override CRUD default_head # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub default_head { my $self = shift; my ( $s, $js_src_yui, $js_src_code ) = @$self{qw{_UserSession _js_src_yui _js_src_code}}; push @{ $self->{_css_src_yui} }, ( 'paginator/assets/skins/sam/paginator.css', 'datatable/assets/skins/sam/datatable.css' ); push @$js_src_yui, ( 'yahoo-dom-event/yahoo-dom-event.js', 'element/element-min.js', 'paginator/paginator-min.js', 'datasource/datasource-min.js', 'datatable/datatable-min.js', 'yahoo/yahoo.js' ); $self->loadDataHTML(); my $jsresults = $self->displayDataHTML(); push @$js_src_code, ( { -code => $jsresults }, { -src => 'TFSDisplay.js' } ); return 1; } #=== CLASS METHOD ============================================================ # CLASS: TFSDisplay # METHOD: get_subset_name # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub get_subset_name { my $self = shift; my $fs = $self->{_fs}; return ( defined($fs) ? "Subset $fs" : ( $self->{_allProbes} ? 'Subset: all probes' : 'Subset: all significant probes' ) ); } #=== CLASS METHOD ============================================================ # CLASS: TFSDisplay # METHOD: default_body # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub default_body { my $self = shift; my $q = $self->{_cgi}; return $q->h2( { -id => 'summary_caption' }, $self->get_subset_name() ), $q->div( { -id => 'summary_table', -class => 'table_cont' }, '' ), $q->h2( { -id => 'tfs_caption' }, '' ), $q->div( $q->a( { -id => 'tfs_astext' }, 'View as plain text' ) ), $q->div( { -id => 'tfs_table', -class => 'table_cont' } ); } #=== CLASS METHOD ============================================================ # CLASS: SGX::TFSDisplay # METHOD: loadDataFromSubmission # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: LOAD DATA FROM FORM # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub loadDataFromSubmission { my $self = shift; my $q = $self->{_cgi}; $self->{_xExpList} = decode_json( car( $q->param('user_selection') ) || '[]' ); $self->{_allProbes} = $q->param('includeAllProbes') || ''; $self->{_searchFilter} = $q->param('searchFilter') || ''; $self->{_opts} = $q->param('opts') || '0'; # The $self->{_fs} parameter is the flagsum for which we filter data my $fs = car $q->param('selectedFS'); $self->{_fs} = ( defined($fs) && $fs ne '' ) ? $fs : undef; $self->{_expected} = decode_json( car( $q->param('selectedExp') ) || '{}' ); $self->{_format} = car $q->param('get'); return 1; } #=== CLASS METHOD ============================================================ # CLASS: SGX::TFSDisplay # METHOD: loadDataHTML # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: LOAD TFS DATA # THROWS: no exceptions # COMMENTS: This code is very old... Refactoring it would be desirable. # SEE ALSO: n/a #=============================================================================== sub loadDataHTML { my $self = shift; # index of the column that is the beginning of the "numeric" half of the # table (required for table sorting) $self->{_numStart} = 5; my $experiment_rows = $self->{_xExpList}; return if !@$experiment_rows; # Build the SQL query that does the TFS calculation my $having = ( defined( $self->{_fs} ) ) ? "HAVING abs_fs=$self->{_fs}" : ''; #If we got a list to filter on, build the string. # # :TODO:07/07/2011 12:38:40:es: SQL injection risk: no validation done # one _searchFilter before being inserted into WHERE statement. # Fix this by using prepare/execute with placeholders. my $probeListQuery = ( defined( $self->{_searchFilter} ) && $self->{_searchFilter} ne '' ) ? " WHERE rid IN (" . $self->{_searchFilter} . ") " : ''; my @query_proj; my @query_join; my @query_body; my $allProbes = $self->{_allProbes}; for ( my $i = 0 ; $i < @$experiment_rows ; $i++ ) { my $row = $experiment_rows->[$i]; my $eid = $row->{eid}; my $fc = $row->{fchange}; my $pval = $row->{pval}; my $pValClass = $row->{pValClass}; my $pval_sql = "pvalue$pValClass"; my $reverse = $row->{reverse}; my $abs_flag = 1 << $i; my $dir_flag = $reverse ? "$abs_flag,0" : "0,$abs_flag"; my $table = "tmp_$i"; #"m${eid}_${pValClass}"; push @query_proj, ( $reverse ? "1/$table.ratio AS '$eid: Ratio', $table.$pval_sql AS '$eid: P-$pValClass'" : "$table.ratio AS '$eid: Ratio', $table.$pval_sql AS '$eid: P-$pValClass'" ); if ( $self->{_opts} ne 'basic' ) { push @query_proj, ( $reverse ? "-$table.foldchange AS '$eid: FC'" : "$table.foldchange AS '$eid: FC'" ); push @query_proj, ( $reverse ? "IFNULL($table.intensity2,0) AS '$eid: Intensity-1', IFNULL($table.intensity1,0) AS '$eid: Intensity-2'" : "IFNULL($table.intensity1,0) AS '$eid: Intensity-1', IFNULL($table.intensity2,0) AS '$eid: Intensity-2'" ); push @query_proj, "$table.$pval_sql AS '$eid: P-$pValClass'"; } push @query_join, "LEFT JOIN response $table ON $table.rid=d2.rid AND $table.eid=$eid"; #This is part of the query when we are including all probes. push @query_body, ($allProbes) ? <<"END_yes_allProbes" SELECT rid, IF($pval_sql < $pval AND ABS(foldchange) > $fc, $abs_flag, 0) AS abs_flag, IF($pval_sql < $pval AND ABS(foldchange) > $fc, IF(foldchange > 0, $dir_flag), 0 ) AS dir_flag FROM response WHERE eid=$eid END_yes_allProbes : <<"END_no_allProbes"; SELECT rid, $abs_flag AS abs_flag, IF(foldchange > 0, $dir_flag) AS dir_flag FROM response WHERE eid = $eid AND $pval_sql < $pval AND ABS(foldchange) > $fc END_no_allProbes } # build record names (sample2 / sample 1, etc) $self->build_headerRecords(); if ( $self->{_opts} eq 'annot' ) { $self->{_numStart} += 3; unshift @query_proj, ( qq{probe.probe_sequence AS 'Probe Sequence'}, qq{GROUP_CONCAT(DISTINCT IF(gene.gname='', NULL, gene.gname) SEPARATOR '; ') AS 'Gene Description'} ); } my $d1SubQuery = join( ' UNION ALL ', @query_body ); my $selectSQL = join( ',', @query_proj ); my $predicateSQL = join( "\n", @query_join ); # pad TFS decimal portion with the correct number of zeroes my $query = <<"END_query"; SELECT abs_fs, dir_fs, probe.reporter AS 'Probe ID', GROUP_CONCAT(DISTINCT if(gene.gtype=0, gene.gsymbol, NULL) separator ' ') AS 'Accession No.', GROUP_CONCAT(DISTINCT if(gene.gtype=1, gene.gsymbol, NULL) separator ' ') AS 'Gene Symbol', $selectSQL FROM ( SELECT rid, BIT_OR(abs_flag) AS abs_fs, BIT_OR(dir_flag) AS dir_fs FROM ($d1SubQuery) AS d1 $probeListQuery GROUP BY rid $having ) AS d2 $predicateSQL LEFT JOIN probe ON d2.rid = probe.rid LEFT JOIN ProbeGene ON d2.rid = ProbeGene.rid LEFT JOIN gene ON ProbeGene.gid = gene.gid LEFT JOIN (select platform.pid, species.sname FROM platform LEFT JOIN species USING(sid)) AS platform_species USING(pid) GROUP BY probe.rid ORDER BY abs_fs DESC END_query my $dbh = $self->{_dbh}; $self->{_Records} = $dbh->prepare($query); $self->{_RowCountAll} = $self->{_Records}->execute; return 1; } #=== CLASS METHOD ============================================================ # CLASS: SGX::TFSDisplay # METHOD: getPlatformData # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: LOAD PLATFORM DATA FOR CSV OUTPUT # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub getPlatformData { my $self = shift; my $dbh = $self->{_dbh}; my $data_rows = $self->{_xExpList}; my @eidList = map { $_->{eid} } @$data_rows; my $placeholders = (@$data_rows) ? join( ',', map { '?' } @eidList ) : 'NULL'; # :TODO:07/25/2011 02:39:05:es: Since experiments from different platforms # cannot be compared using Compare Experiments, this query is a little bit too # fat for what we need. my $singleItemQuery = <<"END_LoadQuery"; SELECT platform.pname AS 'Platform', species.sname AS 'Species', probes.id_count AS 'Probe Count', probes.sequence_count AS 'Sequences Loaded' FROM platform LEFT JOIN species USING(sid) LEFT JOIN ( SELECT pid, COUNT(probe.rid) AS id_count, COUNT(probe_sequence) AS sequence_count FROM probe WHERE probe.pid IN (SELECT DISTINCT pid FROM experiment WHERE eid IN($placeholders)) GROUP BY pid ) AS probes USING(pid) WHERE pid IN (SELECT DISTINCT pid FROM experiment WHERE eid IN($placeholders)) GROUP BY platform.pid END_LoadQuery my $sth = $dbh->prepare($singleItemQuery); my $rc = $sth->execute( @eidList, @eidList ); $self->{_FieldNames} = $sth->{NAME}; $self->{_DataPlatform} = $sth->fetchall_arrayref; $sth->finish; return 1; } #=== CLASS METHOD ============================================================ # CLASS: SGX::TFSDisplay # METHOD: build_headerRecords # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub build_headerRecords { my $self = shift; $self->{_headerRecords} = +{ map { $_->{eid} => +{ study_desc => $_->{study_desc}, exp_info => $_->{exp_info}, exp_samples => ( $_->{reverse} ? ( $_->{sample1} . '/' . $_->{sample2} ) : ( $_->{sample2} . '/' . $_->{sample1} ) ) }; } @{ $self->{_xExpList} } }; return 1; } #=== CLASS METHOD ============================================================ # CLASS: SGX::TFSDisplay # METHOD: loadDataCSV # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: LOAD ALL DATA FOR CSV OUTPUT # THROWS: no exceptions # COMMENTS: This code is very old... Refactoring it would be desirable. # SEE ALSO: n/a #=============================================================================== sub loadDataCSV { my $self = shift; my $experiment_rows = $self->{_xExpList}; return if !@$experiment_rows; #This is the different parts of the experiment and titles query. my @query_body; my @query_proj; my @query_join; #If we got a list to filter on, build the string. my $probeListQuery = ( defined( $self->{_searchFilter} ) && $self->{_searchFilter} ne '' ) ? " WHERE rid IN (" . $self->{_searchFilter} . ") " : ''; for ( my $i = 0 ; $i < @$experiment_rows ; $i++ ) { my $row = $experiment_rows->[$i]; my $eid = $row->{eid}; my $fc = $row->{fchange}; my $pval = $row->{pval}; my $reverse = $row->{reverse}; my $pValClass = $row->{pValClass}; my $pval_sql = "pvalue$pValClass"; my $abs_flag = 1 << $i; my $dir_flag = $reverse ? "$abs_flag,0" : "0,$abs_flag"; my $table = "tmp_$i"; #"m${eid}_${pValClass}"; push @query_proj, ( $reverse ? "1/$table.ratio AS '$eid: Ratio'" : "$table.ratio AS '$eid: Ratio'" ); push @query_proj, ( $reverse ? "-$table.foldchange AS '$eid: FC'" : "$table.foldchange AS '$eid: FC'" ); push @query_proj, ( $reverse ? "IFNULL($table.intensity2,0) AS '$eid: Intensity-1', IFNULL($table.intensity1,0) AS '$eid: Intensity-2'" : "IFNULL($table.intensity1,0) AS '$eid: Intensity-1', IFNULL($table.intensity2,0) AS '$eid: Intensity-2'" ); push @query_proj, "$table.$pval_sql AS '$eid: P-$pValClass'"; push @query_join, "LEFT JOIN response $table ON $table.rid=d2.rid AND $table.eid=$eid"; push @query_body, ( $self->{_allProbes} ) ? <<"END_yes_allProbesCSV" SELECT rid, IF($pval_sql < $pval AND ABS(foldchange) > $fc, $abs_flag, 0) AS abs_flag, IF($pval_sql < $pval AND ABS(foldchange) > $fc, IF(foldchange > 0, $dir_flag), 0) AS dir_flag FROM response WHERE eid=$eid END_yes_allProbesCSV : <<"END_no_allProbesCSV"; SELECT rid, $abs_flag AS abs_flag, IF(foldchange > 0, $dir_flag) AS dir_flag FROM response WHERE eid = $eid AND $pval_sql < $pval AND ABS(foldchange) > $fc END_no_allProbesCSV } #This is the having part of the data query. my $having = ( defined( $self->{_fs} ) && $self->{_fs} ) ? "HAVING abs_fs=$self->{_fs}" : ''; my $d1SubQuery = join( ' UNION ALL ', @query_body ); my $selectSQL = join( ',', @query_proj ); my $predicateSQL = join( "\n", @query_join ); # pad TFS decimal portion with the correct number of zeroes my $query = <<"END_queryCSV"; SELECT abs_fs, dir_fs, probe.reporter AS 'Probe ID', probe.probe_sequence AS 'Comment', probe.probe_comment AS 'Probe Note', GROUP_CONCAT(DISTINCT format_locus(locus.chr, locus.zinterval) separator ' ') AS 'Mapping Location(s)', GROUP_CONCAT(DISTINCT IF(gene.gtype=0, gene.gsymbol, NULL) separator ' ') AS 'Accession No.', GROUP_CONCAT(DISTINCT IF(gene.gtype=1, gene.gsymbol, NULL) separator ' ') AS 'Gene Symbol', GROUP_CONCAT(DISTINCT CONCAT(gene.gname, IF(ISNULL(gene.gdesc), '', CONCAT(', ', gene.gdesc))) separator '; ') AS 'Gene Name/Desc.', GROUP_CONCAT(DISTINCT CONCAT(go_term.go_name, ' (GO:', go_term.go_acc, ')' ) ORDER BY go_term.go_acc SEPARATOR '; ') AS 'GO terms', $selectSQL FROM ( SELECT rid, BIT_OR(abs_flag) AS abs_fs, BIT_OR(dir_flag) AS dir_fs FROM ($d1SubQuery) AS d1 $probeListQuery GROUP BY rid $having ) AS d2 $predicateSQL LEFT JOIN probe ON d2.rid = probe.rid LEFT JOIN ProbeGene ON d2.rid = ProbeGene.rid LEFT JOIN locus ON d2.rid = locus.rid LEFT JOIN gene USING(gid) LEFT JOIN GeneGO USING(gid) LEFT JOIN go_term USING(go_acc) LEFT JOIN (select platform.pid, species.sname FROM platform LEFT JOIN species USING(sid)) AS platform_species USING(pid) GROUP BY probe.rid ORDER BY abs_fs DESC END_queryCSV # build record names (sample2 / sample 1, etc) $self->build_headerRecords(); #Run the query for the actual data records. my $dbh = $self->{_dbh}; $self->{_Records} = $dbh->prepare($query); $self->{_RowCountAll} = $self->{_Records}->execute; return 1; } #=== CLASS METHOD ============================================================ # CLASS: SGX::TFSDisplay # METHOD: displayDataCSV # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: DISPLAY PLATFORM,EXPERIMENT INFO, AND EXPERIMENT DATA TO A CSV # FILE # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub displayDataCSV { my $self = shift; my $data_rows = $self->{_xExpList}; require SGX::CSV; my $print = SGX::CSV::bind_handle( \*STDOUT ); my $sth_records = $self->{_Records}; # get name array from statement handle my $record_names = $sth_records->{NAME}; shift @$record_names; shift @$record_names; unshift @$record_names, 'TFS'; # Report Header #modification by GB: we want the report header to use fewer rows, so some of the bottom rows will become columns on the top rows #since the 2 parts at the top will have varying lengths, we need to pad the shortest with empty lines, to make things match my @left_fields; ## new right fields my @new_right_fields; push @new_right_fields, ['Compare Experiments Report', scalar localtime(),(undef) x5 ]; #$print->( [ 'Generated By', $self->{_UserFullName} ] ); #$print->( [ 'Working Project', $self->{_WorkingProjectName} ] ); #$print->(); # Print Platform info. push @new_right_fields, $self->{_FieldNames} ; push @new_right_fields,($_) for @{ $self->{_DataPlatform} }; ################## old right fields # Print Platform info. # Print Experiment info header. push @new_right_fields, [ #(undef) x 2, 'Exp. no.', '|FC| >', 'P-value used', 'P <', #'Signif. in', 'Study', 'Exp. info', 'Exp. samples' ]; ## test printing those 2 sections, just to see if my use of push is working. The version with the magic character worked my $eid_count = @{ $self->{_xExpList} }; ###### data by probe starts here # The line with the experiment name and eid above the data columns has 6 # fields: TFS, Probe ID, Accession No., Gene Symbol, Probe Sequence, Gene # Name my @experimentNameHeader = (undef) x ($eid_count+9); #this is leaving 9 empty cells = (undef) x 9; #this is leaving 9 empty cells my @brokendownTFSHeader; # Print Experiment info. my $headerRecords = $self->{_headerRecords}; my $currFS = $self->{_fs}; my $signExpCount = 0; for ( my $i = 0 ; $i < @$data_rows ; $i++ ) { my $row = $data_rows->[$i]; my $eid = $row->{eid}; my $eid_node = $headerRecords->{$eid}; # Form the line that displays experiment names above data columns. # Offsetting by 4 fields: FC, Intensity-1, Intensity-2, P-value push @experimentNameHeader, $eid . ':' . $eid_node->{exp_samples}, (undef) x 4; push @brokendownTFSHeader,$eid . ':' . "FS"; my $signCell = ''; if ( defined $currFS ) { # Test for bit presence, print out 'Yes' if present, 'No' if absent if ( 1 << $i & $currFS ) { # significant experiment $signCell = 'Y'; $signExpCount++; } else { # not significant experiment $signCell = 'N'; } } my $currLine = [ $eid, $row->{fchange}, $row->{pValClass}, $row->{pval}, #$signCell, $eid_node->{study_desc}, $eid_node->{exp_info}, $eid_node->{exp_samples}, ]; push @new_right_fields,$currLine; #$print->($currLine); } push @$record_names,@brokendownTFSHeader; #push @right_fields,[""]; #push @right_fields, ["end of right fields"]; my @TFS_fields; # Calculate TFS along with distinct counts my %TFSCounts; my $data_array = $sth_records->fetchall_arrayref; $sth_records->finish; #$print->(); ## yes, it does #$print->($joint_string) ; foreach (@$data_array) { # for each row my $val1= shift @$_; my $val2= shift @$_; my $currentTFS = get_tfs( $val1, $val2, $eid_count ); my @brokendown_tfs= breakdown_tfs( $val1, $val2, $eid_count ); unshift @$_, $currentTFS; push @$_, @brokendown_tfs; $TFSCounts{$currentTFS} = ( defined $TFSCounts{$currentTFS} ) ? 1 + $TFSCounts{$currentTFS} : 1; } # print comparison stats Math::BigFloat->accuracy(3); my $observed = scalar(@$data_array); my $expected = $self->{_expected}; #push @TFS_fields,[(undef) x 4]; #removed empty space at the top #push @TFS_fields,['TFS Summary',(undef) x 3]; #removed the header "TFS Summary" #push @TFS_fields,[ 'TFS', 'Probe Count' #, #,'Log Odds Over Expected' #(undef) x 2 #]; push @TFS_fields, [ undef, $observed #, # , # # log_odds # eval { # Math::BigFloat->new( log( $observed / $expected->{null} ) ); # } #(undef) x 2 ]; push @TFS_fields, [ $_, $TFSCounts{$_}#, #, # log_odds # eval { # Math::BigFloat->new( # log( # $TFSCounts{$_} / # $expected->{ ( before_dot($_) ) } * # ( 1 << count_bits( before_dot($_) ) + 0.0 ) # ) # ); # } #(undef) x 2 ] for sort { $TFSCounts{$b} <=> $TFSCounts{$a} } keys %TFSCounts; #$print->(); my $length_of_left_side = scalar @TFS_fields; my $length_of_right_side = scalar @new_right_fields; # print $length_of_left_side; # print "\n"; # print $length_of_right_side; # print "\n"; ## 13 is hardcoded, because it's the length we've chosen as the max for the spreadsheet my $TFS_max=min($length_of_left_side,8); my $row_max=max(5,$length_of_right_side,$TFS_max); # print $row_max; # if TFS is longer than 13, then split TFS into into chunks ## instead of printing to the file, print to a variable my @right_fields_TFS; my @right_fields_TFS_w_header; my $right_field_length; if($length_of_left_side > $row_max){ my $TFS_length=scalar @TFS_fields; #cap length of TFS patterns to 100 my $TFS_cap = min(100, $TFS_length); #print $TFS_cap; my $chunk_size=$row_max-1; ## number of lines that will be added my $TFS_iterations=POSIX::ceil(($TFS_cap / $chunk_size) ); # printf "TFS iterations\n"; # printf $TFS_iterations; # printf "\n"; # printf "chunk size\n"; # printf $chunk_size; # printf "\n"; my @right_TFS_header; for(my $c_TFS_iteration=0;$c_TFS_iteration<$TFS_iterations;$c_TFS_iteration++){ push @right_TFS_header, 'TFS', 'Probe Count',(undef) x1; } for(my $c_row=0;$c_row<$chunk_size;$c_row++){ my @new_row; for(my $c_TFS_iteration=0;$c_TFS_iteration<=$TFS_iterations;$c_TFS_iteration++){ my $c_index=($c_TFS_iteration*$chunk_size)+$c_row; # print $c_index; # print "\n"; if ($c_index<$TFS_cap){ push @new_row, @{$TFS_fields[$c_index]},(undef) x1; } } push @right_fields_TFS,[@new_row]; } # my $TFS_iterations=ceil(($remaining_TFS / $chunk_size) + 0.5); #$print->([@right_TFS_header]); push @right_fields_TFS_w_header,[@right_TFS_header]; # printf "concatenated space and header"; $right_field_length=scalar @right_fields_TFS; # printf "right field length"; # printf $right_field_length; for (my $tfs_i=0;$tfs_i<$right_field_length;$tfs_i++){ #$print->([@{$right_fields_TFS[$tfs_i]}]); push @right_fields_TFS_w_header,[@{$right_fields_TFS[$tfs_i]}]; } # printf "added right field TFS to right_fields_TFS_w_header"; } else{ push @right_fields_TFS_w_header, ['TFS', 'Probe Count',(undef) x1]; push @right_fields_TFS_w_header, $_ for @TFS_fields; } # printf "after right side"; $right_field_length=scalar @right_fields_TFS_w_header; # print $right_field_length; # print "\n"; #if row max > length of right side pad right fields with empty spaces until row max if($row_max > $length_of_right_side){ for (1..($row_max-$length_of_right_side)) { push @new_right_fields,[(undef) x7] ; } } if($row_max > $right_field_length){ for (1..($row_max-$right_field_length)) { push @right_fields_TFS_w_header,[(undef) x3] ; } } ## add TFS to the right of the right fields my @right_fields_w_TFS; for (my $tfs_i=0;$tfs_i<$row_max;$tfs_i++){ ########################################### HERE ######################################## ################################################################################### ################################################################################### my $new_line; if($tfs_i ==1 || $tfs_i==2){ $new_line=[@{$new_right_fields[$tfs_i]},(undef) x11,@{$right_fields_TFS_w_header[$tfs_i]}]; } else{ $new_line=[@{$new_right_fields[$tfs_i]},(undef) x8,@{$right_fields_TFS_w_header[$tfs_i]}]; } #new_right_fields , 6 empty spaces , right_fields_TFS_w_header #$print->([@{$right_fields_TFS[$tfs_i]}]); #push @right_fields_w_TFS,$new_line; $print->($new_line); ################################################################################### ################################################################################### ################################################################################### } #$print->($_) for @left_fields; #$print->($_) for @right_fields; $print->(); #$print->($_) for @TFS_fields; # Print header line. $print->( \@experimentNameHeader ); my $row_test=@$data_array->[0]; # print "\n"; # $print->($row_test); # print "length of row"; my $row_length=scalar @{$row_test}; # print scalar @{$row_test}; # print "\n"; # print $_."\n" for @{$row_test}; #get the TFS columns, which are the last columns, and there are as many as experiments # $row_length my $start_of_TFS=$row_length-$eid_count; # print "start of TFS columns"; # print $start_of_TFS; # print "\n"; # Experiment Data header. my @reordred_record_names; push @reordred_record_names,@$record_names[1,0,$start_of_TFS..($start_of_TFS+$eid_count-1),2,3,4,5,7,8,6,9..(9+5*$eid_count-1)]; $print->(\@reordred_record_names); # Print Experiment data sorting by TFS in descending order, then by gene # name in ascending order. my @sorted_data_array; push @sorted_data_array,$_ for sort { $b->[0] cmp $a->[0] || ( $a->[3] || '' ) cmp( $b->[3] || '' ) } @$data_array; # $print->($_) # for sort { $b->[0] cmp $a->[0] || ( $a->[3] || '' ) cmp( $b->[3] || '' ) } # @$data_array; # print "\n"; # $print->(@$data_array->[0]); # print "\n"; # print @sorted_data_array->[0]; # print scalar @sorted_data_array; # print "\n"; # print scalar @sorted_data_array->[0]; # print "\n"; my @reordered_columns; push @reordered_columns, [@{$_}[1,0,$start_of_TFS..($start_of_TFS+$eid_count-1),2,3,4,5,7,8,6,9..(9+5*$eid_count-1)]] for @sorted_data_array; $print->($_) for @reordered_columns; return 1; } #=== CLASS METHOD ============================================================ # CLASS: SGX::TFSDisplay # METHOD: displayDataHTML # PARAMETERS: ???? # RETURNS: ???? # DESCRIPTION: Display TFS info # THROWS: no exceptions # COMMENTS: none # SEE ALSO: n/a #=============================================================================== sub displayDataHTML { my $self = shift; my $q = $self->{_cgi}; # Fields with indexes less num_start are formatted as strings, fields with # indexes equal to or greater than num_start are formatted as numbers. my @table_header; my @table_parser; my @table_format; my $sth_records = $self->{_Records}; my $record_names = $sth_records->{NAME} || []; for ( my $j = 2 ; $j < $self->{_numStart} ; $j++ ) { push @table_header, $record_names->[$j]; push @table_parser, 'string'; push @table_format, 'formatText'; } for ( my $j = $self->{_numStart} ; $j < @$record_names ; $j++ ) { push @table_header, $record_names->[$j]; push @table_parser, 'number'; push @table_format, 'formatNumber'; } # wrapping block below in curly brackets because of "Insecure dependency # when running with -T" (taint mode) Perl message my %format_template; { my $findProbesHTML = $q->a( { -title => 'Find all %1$ss related to %1$s {0}', -target => '_blank', -href => $self->url( -absolute => 1 ) . '?a=findProbes&b=Search&scope=%1$s&q={0}', }, '{0}' ); $format_template{probe} = sprintf( $findProbesHTML, 'Probe IDs' ); $format_template{accnum} = sprintf( $findProbesHTML, 'Genes/Accession Nos.' ); $format_template{gene} = sprintf( $findProbesHTML, 'Genes/Accession Nos.' ); }; $table_format[0] = 'formatProbe'; $table_format[1] = 'formatAccNum'; $table_format[2] = 'formatGene'; if ( $self->{_opts} eq 'annot' ) { $table_format[3] = 'formatProbeSequence'; $format_template{probeseq} = $q->a( { -title => 'UCSC BLAT on DNA', -target => '_blank', -class => 'external', -href => 'http://genome.ucsc.edu/cgi-bin/hgBlat?org={1}&type=DNA&userSeq={0}' }, '{0}' ); } #--------------------------------------------------------------------------- # print table body #--------------------------------------------------------------------------- my $data_array = eval { my $cache = $sth_records->fetchall_arrayref; $sth_records->finish; $cache; } || []; my $eid_count = @{ $self->{_xExpList} }; unshift( @$_, get_tfs( shift @$_, shift @$_, $eid_count ) ) for @$data_array; my $js = $self->{_js_emitter}; return '' . $js->let( [ _xExpList => $self->{_xExpList}, _expected => $self->{_expected}, _fs => $self->{_fs}, tfs => { caption => sprintf( 'This subset includes %d probes', ( $self->{_RowCountAll} || 0 ) ), headers => [ 'TFS', @table_header ], parsers => [ 'string', @table_parser ], formats => [ 'formatText', @table_format ], frm_tpl => \%format_template, records => [ sort { $b->[0] cmp $a->[0] || ( $a->[3] || '' ) cmp( $b->[3] || '' ) } @$data_array ] } ], declare => 1 ); } 1; __END__ =head1 NAME SGX::TFSDisplay =head1 SYNOPSIS =head1 DESCRIPTION =head1 AUTHORS Eugene Scherba Michael McDuffie =head1 SEE ALSO =head1 COPYRIGHT =head1 LICENSE Artistic License 2.0 http://www.opensource.org/licenses/artistic-license-2.0.php =cut