Browse Source

Consolidated original link-outs with the work done by Peanutbase/LegumeInfo; Debugged JBrowse/GBrowse link-outs and made the code more readable.

Lacey Sanderson 9 years ago
parent
commit
86753a3f57
5 changed files with 257 additions and 115 deletions
  1. 0 1
      blast_ui.module
  2. 153 35
      includes/blast_ui.linkouts.inc
  3. 66 36
      includes/blast_ui.node.inc
  4. 32 43
      theme/blast_report.tpl.php
  5. 6 0
      theme/css/form.css

+ 0 - 1
blast_ui.module

@@ -16,7 +16,6 @@ require_once 'includes/blast_ui.node.inc';
 
 // BLAST Link-out functionality.
 require_once 'includes/blast_ui.linkouts.inc';
-include_once('includes/blast_ui.custom.inc');
 
 // Functions specific to themeing (ie: preprocess)
 require_once 'theme/blast_ui.theme.inc';

+ 153 - 35
includes/blast_ui.linkouts.inc

@@ -64,21 +64,42 @@ function blast_ui_blast_linkout_info() {
     // This function will have full access to the blast hit and database
     // prefix information and is expected to return a URL.
     'process function' => 'tripal_blast_generate_linkout_link',
+    // Help text to show in the BLAST Database create/edit form so that 
+    // users will know how to use this link-out type. Specifically, info
+    // about your assumptions for the URL prefix are very helpful.
+    // HTML is aloud but do not enclose in <p>.
+    'help' => 'The External Database choosen below provides its URL prefix when 
+      determining the URL to link-out to. If the link-out type is "Generic Link" then 
+      the hit identifier (determined using fasta header format or regular expression) is 
+      concatenated to the end of the url prefix. For example, if your hit is for "Chr01" 
+      and the URL prefix is "http://myfriendstripalsite.org/name/" then the complete URL 
+      is simply &#60;a href="http://myfriendstripalsite.org/name/Chr01"&#62;Chr01&#60;/a&#62;.',
   );
 
   $types['gbrowse'] = array(
     'name' => 'GBrowse',
     'process function' => 'tripal_blast_generate_linkout_gbrowse',
+    'help' => 'The link created will add a BLAST track to the GBrowse (specified by the 
+      External Database) that shows the HSPs as well as indicating the overall hit. 
+      <strong><em>It is assumed that the Reference of the GBrowse is the same as this BLAST 
+      database (even the names must be consistent).</em></strong> Furthermore, the URL prefix 
+      supplied is expected to have an empty query (?) or be properly ended (;). For
+      example, "http://mydomain.com/gb/gbrowse/tripalus_databasica/?" OR
+      "http://mydomain.com/gb/gbrowse/tripalus_databasica/?label=genes+markers;"',
   );
 
   $types['jbrowse'] = array(
     'name' => 'JBrowse',
     'process function' => 'tripal_blast_generate_linkout_jbrowse',
-  );
-
-  $types['custom'] = array(
-    'name' => 'Custom',
-    'process function' => 'tripal_custom_generate_linkout',
+    'help' => 'The link created will add a "Blast Result" track to the JBrowse (specified by the 
+      External Database) that shows the HSPs as well as indicating the overall hit. 
+      <strong><em>It is assumed that the Reference of the JBrowse is the same as this BLAST 
+      database (even the names must be consistent).</em></strong> Furthermore, the URL prefix 
+      supplied is expected to have an empty query (?) or be properly ended (&). For
+      example, "http://mydomain.com/jbrowse/tripalus_databasica/?" OR
+      "http://mydomain.com/jbrowse/tripalus_databasica/?tracks=genes,markers,blast&". 
+      Also <strong><em>the Blast Result track is NOT Displayed by default</em></strong>. Either include "blast" 
+      using the "tracks" directive in the URL prefix or specify it in your JBrowse.conf.',
   );
 
   return $types;
@@ -146,29 +167,54 @@ function tripal_blast_generate_linkout_link($url_prefix, $hit, $info, $options =
  */
 function tripal_blast_generate_linkout_gbrowse($url_prefix, $hit, $info, $options = array()) {
 
+  // First we need to collect the HSPs to define the ranges we want to
+  // display on the JBrowse.
   $ranges = array();
+  // We also keep track of all the coordinates in order to later
+  // calculate the smallest and largest coordinate.
   $coords = array();
   foreach($info['HSPs'] as $hsp) {
-     $start = min($hsp['Hsp_hit-from'], $hsp['Hsp_hit-to']);
-     $stop = max($hsp['Hsp_hit-from'], $hsp['Hsp_hit-to']);
-     array_push($ranges, "$start..$stop");
-     array_push($coords, $start, $stop);
-   }
-   $min = min($coords);
-   $max = max($coords);
-   $joined_ranges = join ("," , $ranges);
-//   $track_name = $hit->{'hit_name'} . '_' . $info['query_name'] . '_' . $info['e-value'];
-   $track_name = $hit->{'linkout_id'} . '_' . $info['query_name'] . '_' . $info['e-value'];
-   
-//   $url_postfix = 'query=';
-   $url_postfix = 'start=' . $min . ';stop=' . $max;
-//   $url_postfix .= ';ref=' . $hit->{'hit_name'};
-//   $url_postfix .= ';add=' . $hit->{'hit_name'} . '+BLAST+Query+' . $joined_ranges;
-   $url_postfix .= ';ref=' . $hit->{'linkout_id'};
-   $url_postfix .= ';add=' . $hit->{'linkout_id'} . '+BLAST+Query+' . $joined_ranges;
-   $url_postfix .= ';h_feat=Query';
-
-   return $url_prefix . $url_postfix;
+  
+    $start = min($hsp['Hsp_hit-from'], $hsp['Hsp_hit-to']);
+    $stop = max($hsp['Hsp_hit-from'], $hsp['Hsp_hit-to']);
+    
+    // Format the hsp for inclusion in the new track later.
+    array_push($ranges, "$start..$stop");
+    
+    // Add both the start & stop to the coordinate list.
+    array_push($coords, $start, $stop);
+  }
+  // Calculate the minimum & maximum coordinates.
+  $min = min($coords);
+  $max = max($coords);
+
+  // Now we are finally ready to build the URL.
+  // First lets set the location of the hit so the GBrowse focuses in on the correct region.
+  $query = array();
+  $query['ref'] = 'ref=' . $hit->{'linkout_id'};
+  $query['start'] = 'start=' . $min;
+  $query['stop'] = 'stop=' . $max;
+  
+  // Next we want to add our BLAST hit to the GBrowse.
+  $query['add'] = format_string(
+    'add=!ref+!trackname+!featurename+!hspcoords',
+    array(
+      '!ref' => $hit->{'linkout_id'},
+      '!trackname' => 'BLAST',
+      '!featurename' => 'BlastHit',
+      '!hspcoords' => join ("," , $ranges),
+    )
+  );
+  
+  // Highlight our newly added feature.
+  $query['highlight'] = format_string(
+    'h_feat=!featurename',
+    array('!featurename' => 'BlastHit')
+  );
+  
+  $url_postfix = implode(';', $query);
+
+  return $url_prefix . $url_postfix;
 }
 
 /**
@@ -176,9 +222,20 @@ function tripal_blast_generate_linkout_gbrowse($url_prefix, $hit, $info, $option
  *
  * NOTE: Assumes the hit is a backbone feature in the JBrowse linked to.
  *  Otherwise, the basic link can be used.
+ * NOTE: This linkout creates a "blast" track but doesn't make it visible. This is to
+ *  allow your default tracks to be visible and give contect to your blast hit. You
+ *  should include "blast" in your jbrowse.conf default track list to ensure your
+ *  users can always see their hits. If you don't have access to the jbrowse.conf,
+ *  you can place the tracks you want to see including 'blast' in the url prefix 
+ *  (see example below under @param $url_prefix).
  *
  * @param $url_prefix
- *   The URL prefix for the BLAST Database queried.
+ *   The URL prefix for the BLAST Database queried. It is assumed that the url prefix
+ *   includes the ? and if there are any key=vale pairs that the last symbol is &.
+ *   For example,
+ *     http://myserver.com/jbrowse/databasica/?
+ *     http://myserver.com/jbrowse/databasica/?tracks=myfavtrack,anoktrack,blast&
+ * 
  * @param $hit
  *   The blast XML hit object. This object has the following keys based on the
  *   XML: Hit_num, Hit_id, Hit_def, Hit_accession, Hit_len and Hit_hsps.
@@ -200,20 +257,81 @@ function tripal_blast_generate_linkout_gbrowse($url_prefix, $hit, $info, $option
  */
 function tripal_blast_generate_linkout_jbrowse($url_prefix, $hit, $info, $options = array()) {
 
+  // First we need to collect the HSPs to define the ranges we want to
+  // display on the JBrowse.
   $ranges = array();
+  // We also keep track of all the coordinates in order to later
+  // calculate the smallest and largest coordinate.
   $coords = array();
-  $hsps = array();
+  $count = 0;
   foreach($info['HSPs'] as $hsp) {
-   $hsp_start = $hsp['Hsp_hit-from'];
-   $hsp_end = $hsp['Hsp_hit-to'] ;
-   array_push($coords,$hsp['Hsp_hit-from'] , $hsp['Hsp_hit-to'] );
-   array_push($ranges, '{"start":'.$hsp_start.',"end":'.$hsp_end.',"type":"match_part"}');
+    $count++;
+    
+    $strand = '1';
+    $hsp_start = $hsp['Hsp_hit-from'];
+    $hsp_end = $hsp['Hsp_hit-to'];
+    
+    // Handle alignments on the negative strand.
+    if (($hsp_end - $hsp_start) < 0) {
+      $strand = '-1';
+      $hsp_start = $hsp['Hsp_hit-to'];
+      $hsp_end = $hsp['Hsp_hit-from'];
+    }
+    
+    // Add both the start & stop to the coordinate list.
+    array_push($coords,$hsp['Hsp_hit-from'] , $hsp['Hsp_hit-to'] );
+
+    // Format the hsp for inclusion in the subfeatures section of the track later.
+    $hsp_def = format_string(
+      '{"start":!start,"end":!end,"strand":"!strand","type":"!type"}',
+      array(
+        '!start' => $hsp_start,
+        '!end' => $hsp_end,
+        '!strand' => $strand,
+        '!type' => 'match_part'
+      )
+    );
+    array_push($ranges, $hsp_def);
   }
+  // Calculate the minimum & maximum coordinates.
   $min = min($coords);
   $max = max($coords);
-  $url_postfix = '&addFeatures=[{"seq_id":"'.$hit->{'hit_name'}.'","score":"'.$info['e-value'].'","start":'.$min.',"end":'.$max.',"type":"match","name":"MyBLASTHit","subfeatures":[';
-  $joined_ranges = join ("," , $ranges);
-  $url_postfix = $url_postfix . $joined_ranges . ']}]&addTracks=[{"label":"BLAST","type":"JBrowse/View/Track/HTMLFeatures","store":"url"}]&loc='.$hit->{'hit_name'}.'&tracks=DNA%2CBLAST&highlight=';
+  
+  // We also want some white-space on either side of out hit 
+  // when we show it in the JBrowse. To make this generic,
+  // we want our blast hit to take up 2/3 of the screen thus
+  // we have 1/6 per side for white-space.
+  $buffer = round(($max - $min) / 6);
+  $screen_start = $min - $buffer;
+  $screen_end = $max + $buffer;
+
+  // Now we are finally ready to build the URL.
+  // First lets set the location of the hit so the JBrowse focuses in on the correct region.
+  $jbrowse_query = array();
+  $jbrowse_query['loc'] = format_string(
+    'loc=!ref:!start..!stop',
+    array(
+      '!ref' => $hit->{'linkout_id'},
+      '!start' => $screen_start,
+      '!stop' => $screen_end,
+    )
+  );
+
+  // Next we want to add our BLAST hit to the JBrowse.
+  $jbrowse_query['addFeatures'] = format_string(
+    'addFeatures=[{"seq_id":"!id","start":!min,"end":!max,"name":"!name","subfeatures":[!hspcoords]}]',
+    array(
+      '!id' => $hit->{'linkout_id'},
+      '!name' => $info['query_name'] . ' Blast Hit',
+      '!min' => $min,
+      '!max' => $max,
+      '!hspcoords' => join ("," , $ranges)
+    ));
+
+  // Then add a track to display our new feature.
+  $jbrowse_query['addTracks'] = 'addTracks=[{"label":"blast","key":"BLAST Result","type":"JBrowse/View/Track/HTMLFeatures","store":"url"}]';
+
+  $url_postfix = implode('&', $jbrowse_query);
 
   return $url_prefix . $url_postfix;
-}
+}

+ 66 - 36
includes/blast_ui.node.inc

@@ -67,6 +67,10 @@ function blastdb_form($node, &$form_state) {
   $form = array();
 
   $form['#validate'] = array('blastdb_form_validate');
+  
+  $form['#attached']['css'] = array(
+    drupal_get_path('module', 'blast_ui') . '/theme/css/form.css',
+  );
 
   $form['core'] = array(
     '#type' => 'fieldset',
@@ -101,48 +105,70 @@ function blastdb_form($node, &$form_state) {
 
   $form['dbxref'] = array(
     '#type' => 'fieldset',
-    '#title' => 'Database References',
-    '#description' => 'These settings can be used to tell the BLAST UI that '
-      . 'information about the records in this BLAST database can be found '
-      . 'either in the current website or an external website.'
+    '#title' => 'Link-outs',
+    '#description' => 'These settings will be used to <em>transform the hit name into a 
+      link to additional information</em>.',
+    '#prefix' => '<div id="link-outs">',
+    '#suffix' => '</div>',
   );
 
-  $description = array(
-    'default' => 'A single word followed by a free-text definition. '
+  $regex = array(
+    'default' => array(
+      'title' => 'Generic',
+      'help' => 'A single word followed by a free-text definition. '
       . 'The first word contains only alphanumeric characters and optionally '
-      . 'underscores and will be used as the ID of the sequence.',
-    'genbank' => 'Follows the same format as the first option '
+      . 'underscores and will be used as the ID of the sequence.'
+    ),
+    'genbank' => array(
+      'title' => 'NCBI GenBank',
+      'help' => 'Follows the same format as the first option '
       . 'except that the first "word" is of the following form: '
-      . 'gb|accession|locus. The accession will be used as the ID of the sequence.',
-    'embl' => 'Follows the same format as the first option '
+      . 'gb|accession|locus. The accession will be used as the ID of the sequence.'
+    ),
+    'embl' => array(
+      'title' => 'EMBL Data Library',
+      'help' => 'Follows the same format as the first option '
       . 'except that the first "word" is of the following form: '
-      . 'emb|accession|locus. The accession will be used as the ID of the sequence.',
-    'swissprot' => 'Follows the same format as the first option '
+      . 'emb|accession|locus. The accession will be used as the ID of the sequence.'
+    ),
+    'swissprot' => array(
+      'title' => 'SWISS-PROT',
+      'help' => 'Follows the same format as the first option '
       . 'except that the first "word" is of the following form: '
-      . 'sp|accession|entry name. The accession will be used as the ID of the sequence.',
-    'custom' => 'Allows you to use a regular expression (define one below) to '
+      . 'sp|accession|entry name. The accession will be used as the ID of the sequence.'
+    ),
+    'custom' => array(
+      'title' => 'Custom Format',
+      'help' => 'Allows you to use a regular expression (define one below) to '
       . 'extract a specifc portion of the FASTA header to be used as the ID.'
+    ),
   );
+  $regex_type = (isset($node->linkout->regex_type)) ? $node->linkout->regex_type : 'default';
+  $regex_type = (isset($form_state['values'])) ? $form_state['values']['dbxref_id_type'] : $regex_type;
   $form['dbxref']['dbxref_id_type'] = array(
     '#type' => 'radios',
     '#title' => 'FASTA header format',
     '#description' => 'Choose the format that matches the format of the FASTA '
       . 'headers in this BLAST database or choose custom to define your own '
-      . 'using regular expressions. This ID will be appended to the URL Prefix '
-      . ' of the database selected below.',
+      . 'using regular expressions. This ID will be used to create the URL for the link-out.',
     '#options' => array(
-      'default' => '<span title="' . $description['default'] . '">Generic</span>',
-      'genbank' => '<span title="' . $description['genbank'] . '">NCBI GenBank</span>',
-      'embl' => '<span title="' . $description['embl'] . '">EMBL Data Library</span>',
-      'swissprot' => '<span title="' . $description['swissprot'] . '">SWISS-PROT</span>',
-      'custom' => '<span title="' . $description['custom'] . '">Custom Format</span>',
+      'default' => '<span title="' . $regex['default']['help'] . '">' . $regex['default']['title'] . '</span>',
+      'genbank' => '<span title="' . $regex['genbank']['help'] . '">' . $regex['genbank']['title'] . '</span>',
+      'embl' => '<span title="' . $regex['embl']['help'] . '">' . $regex['embl']['title'] . '</span>',
+      'swissprot' => '<span title="' . $regex['swissprot']['help'] . '">' . $regex['swissprot']['title'] . '</span>',
+      'custom' => '<span title="' . $regex['custom']['help'] . '">' . $regex['custom']['title'] . '</span>',
     ),
-    '#default_value' => (isset($node->linkout->regex_type)) ? $node->linkout->regex_type : 'default',
+    '#default_value' => $regex_type,
     '#ajax' => array(
       'callback' => 'ajax_blast_ui_node_linkout_custom_callback',
-      'wrapper' => 'link-regex',
+      'wrapper' => 'link-outs',
     )
   );
+  // Add information about each format to the description.
+  if ($regex_type) {
+    $form['dbxref']['dbxref_id_type']['#description'] .= '
+      <p class="blastdb-extra-info"><strong>'.$regex[$regex_type]['title'].'</strong>: '.$regex[$regex_type]['help'].'</p>';
+  }
 
   $hide_regex = TRUE;
   if (isset($form_state['values']['dbxref_id_type'])) {
@@ -161,8 +187,6 @@ function blastdb_form($node, &$form_state) {
       . 'available if custom was choosen for the FASTA header format above.',
       array('@url' => 'http://php.net/manual/en/reference.pcre.pattern.syntax.php')),
     '#disabled' => $hide_regex,
-    '#prefix' => '<div id="link-regex">',
-    '#suffix' => '</div>',
     '#default_value' => (isset($node->linkout->regex)) ? $node->linkout->regex : ''
   );
 
@@ -186,20 +210,26 @@ function blastdb_form($node, &$form_state) {
   foreach ($types as $machine_name => $details) {
     $options[$machine_name] = (isset($details['name'])) ? $details['name'] : $machine_name;
   }
+  $linkout_type = (isset($node->linkout->type)) ? $node->linkout->type : 'link';
+  $linkout_type = (isset($form_state['values'])) ? $form_state['values']['dbxref_linkout_type'] : $linkout_type;
   $form['dbxref']['dbxref_linkout_type'] = array(
-    '#type' => 'select',
+    '#type' => 'radios',
     '#title' => 'Link-out Type',
-    '#description' => 'This determines how the URL to be linked to is formed. '
-                    . 'NOTE: this is very dependant on the External Database '
-                    . 'chosen above since it needs to be able to support the '
-                    . 'type of linking choosen. For example, only External '
-                    . 'Databases which reference a GBrowse instance can use the '
-                    . 'GBrowse link type.  If a link requires customized code, '
-                    . 'select "custom" and add the code to the file ' 
-                    . 'includes/blast_ui.custom/inc.',
+    '#description' => 'This determines how the URL to be linked to is formed. <strong>Make 
+      sure the database chosen supports this type of link</strong> (ie: the database 
+      should point to a GBrowse instance if you choose GBrowse here).',
     '#options' => $options,
-    '#default_value' => (isset($node->linkout->type)) ? $node->linkout->type : 'link'
+    '#default_value' => $linkout_type,
+    '#ajax' => array(
+      'callback' => 'ajax_blast_ui_node_linkout_custom_callback',
+      'wrapper' => 'link-outs',
+    )
   );
+  // Add information about each format to the description.
+  if ($linkout_type) {
+    $form['dbxref']['dbxref_linkout_type']['#description'] .= '
+      <p class="blastdb-extra-info"><strong>'.$types[$linkout_type]['name'].'</strong>: '.$types[$linkout_type]['help'].'</p>';
+  }
   
   return $form;
 }
@@ -387,5 +417,5 @@ function blastdb_load($nodes) {
  * when someone selects custom.
  */
 function ajax_blast_ui_node_linkout_custom_callback($form, $form_state) {
-  return $form['dbxref']['regex'];
+  return $form['dbxref'];
 }

+ 32 - 43
theme/blast_report.tpl.php

@@ -7,9 +7,6 @@
  *   $xml_filename: The full path & filename of XML file containing the BLAST results
  *    @deepaksomanadh: $job_data = meta data related to the current job
  */
- 
-// uncomment this to see the contents of the $blastdb object
-//echo "blastdb:<pre>";var_dump($blastdb);echo "</pre>";
 
 // Set ourselves up to do link-out if our blast database is configured to do so.
 $linkout = FALSE;
@@ -144,9 +141,10 @@ and click the <em>target name </em> to get more information about the target hit
 
           // SUMMARY ROW
           // -- Save additional information needed for the summary.
-          $score = $hit->{'Hit_hsps'}->{'Hsp'}->{'Hsp_score'};
-          $evalue = $hit->{'Hit_hsps'}->{'Hsp'}->{'Hsp_evalue'};
-          $query_name = $iteration->{'Iteration_query-def'};
+          $score = (float) $hit->{'Hit_hsps'}->{'Hsp'}->{'Hsp_score'};
+          $evalue = (float) $hit->{'Hit_hsps'}->{'Hsp'}->{'Hsp_evalue'};
+          $query_name = (string) $iteration->{'Iteration_query-def'};
+          
           // If the id is of the form gnl|BL_ORD_ID|### then the parseids flag
           // to makeblastdb did a really poor job. In thhis case we want to use
           // the def to provide the original FASTA header.
@@ -247,46 +245,37 @@ and click the <em>target name </em> to get more information about the target hit
             // First extract the linkout text using the regex provided through
             // the Tripal blast database node.
             if (preg_match($linkout_regex, $hit_name, $linkout_match)) {
-              $linkout_id = $linkout_match[1];
-              $hit->{'linkout_id'} = $linkout_id;
+              $hit->{'linkout_id'} = $linkout_match[1];
               $hit->{'hit_name'} = $hit_name;
-            }
-            
-            // Allow custom functions to determine the URL to support more complicated
-            // link-outs rather than just using the tripal database prefix.
-            $hit_url = call_user_func(
-              $url_function,
-              $linkout_urlprefix,
-              $hit,
-              array(
-                'query_name' => $query_name,
-                'score'      => $score,
-                'e-value'    => $evalue,
-                'HSPs'       => $HSPs,
-                'Target'     => $blastdb->title,
-              )
-            );
             
-            // The linkout id might have been set/changed by the custom linkout code.
-            if ($linkout_type == 'custom' && $hit->{'linkout_id'}) {
-              $linkout_id = $hit->{'linkout_id'};
+              // Allow custom functions to determine the URL to support more complicated
+              // link-outs rather than just using the tripal database prefix.
+              $hit_url = call_user_func(
+                $url_function,
+                $linkout_urlprefix,
+                $hit,
+                array(
+                  'query_name' => $query_name,
+                  'score'      => $score,
+                  'e-value'    => $evalue,
+                  'HSPs'       => $HSPs,
+                  'Target'     => $blastdb->title,
+                )
+              );
+
+              // Create Link.
+              if ($hit_url) {
+                // It is important to url-encode links, especially in this case,
+                // since jbrowse links have double quotes in them which result in
+                // truncated links due to <a href="yoururl"></a> (notice the double quotes).
+                $hit_name = l(
+                  $hit->{'linkout_id'},
+                  $hit_url,
+                  array('attributes' => array('target' => '_blank'))
+                );
+              }
             }
-
-            // Create Link.
-            if ($hit_url) {
-              /* eksc- l() URL-encodes the URL path too, which is often not what we want.
-               * I think this is because they are not using the query paramater for l().
-                  $hit_name = l(
-                    $linkout_id,
-                    $hit_url,
-                    array('attributes' => array('target' => '_blank'))
-                  );*/
-               $hit_name = "
-                  <a href=\"$hit_url\" target=\"_blank\">
-                    $linkout_id
-                  </a>";
-            }
-            
+                        
             // Replace the target name with the link.
             $summary_row['data']['hit']['data'] = $hit_name;
           }

+ 6 - 0
theme/css/form.css

@@ -3,3 +3,9 @@ div.center {
   margin-right: auto;
   width:70%
 } 
+
+.blastdb-extra-info {
+  padding: 10px;
+  border: 1px solid #be7;
+  background-color: #f8fff0;
+}