Просмотр исходного кода

A couple of bug fixes and reformating of the recent jobs display into an easy to read table.

Lacey Sanderson 9 лет назад
Родитель
Сommit
d4a441473f

+ 52 - 29
api/blast_ui.api.inc

@@ -281,38 +281,61 @@ function get_blastdb_linkout_regex($node, $options = array()) {
   return $regex;
 }
 
-
-function get_recent_jobs() {
-  $html = '';
+/**
+ * Return a list of recent blast jobs to be displayed to the user.
+ *
+ * NOTE: The calling function will be expected to do the rendering.
+ *
+ * @return
+ *   An array of recent jobs.
+ */
+function get_recent_blast_jobs($programs = array()) {
   
+  $filter_jobs = !empty($programs);
+  
+  // Retrieve any recent jobs from the session variable.
   $sid = session_id();  
-  $jobs = $_SESSION['all_jobs'][$sid];
-
-  // Remove jobs older than 48 hours
-  foreach ($jobs as $job_id => $job) {
-    if ($diff = abs(time() - strtotime($job['date'])) > 172800) {
-      unset($jobs[$job_id]);
+  if (isset($_SESSION['all_jobs'][$sid])) {
+
+    $jobs = array();
+    foreach ($_SESSION['all_jobs'][$sid] as $job_id => $job) {
+      $add = TRUE;
+      
+      // @TODO: Check that the results are still available.
+      // This is meant to replace the arbitrary only show jobs executed less than 48 hrs ago.
+      
+      // Remove jobs from the list that are not of the correct program.
+      if ($filter_jobs AND !in_array($job['program'], $programs)) {
+        $add = FALSE;
+      }
+      
+      if ($add) {
+
+        // Format the query information for display.
+        // Easiest case: if there is only one query header then show it.
+        if (sizeof($job['query_defs']) == 1 AND isset($job['query_defs'][0])) {
+          $job['query_info'] = $job['query_defs'][0];
+        }
+        // If we have at least one header then show that along with the count of queries.
+        elseif (isset($job['query_defs'][0])) {
+          $job['query_info'] = sizeof($job['query_defs']) . ' queries including "' . $job['query_defs'][0] . '"';
+        }
+        // If they provided a sequence with no header.
+        elseif (empty($job['query_defs'])) {
+          $job['query_info'] = 'Unnamed Query';
+        }
+        // At the very least show the count of queries.
+        else {
+          $job['query_info'] = sizeof($job['query_defs']) . ' queries';
+        }
+      
+        $jobs[$job_id] = $job;
+      }
     }
+      
+    return $jobs;
   }
-  $_SESSION['all_jobs'][$sid] = $jobs;
-  
-  if (count($jobs) > 0) {
-    $html = "
-    <h3><strong> Recent Jobs </h3>
-    <dl>";
-
-    foreach (array_reverse($jobs) as $job) {
-      $q_def = !isset($job['query_defs'][0]) ? "Query" : $job['query_defs'][0];
-      $html .= "
-        <dd>
-          <a href='" . "../../" . $job['job_output_url'] ."'>
-            $q_def X " . $job['target'] . ' (' . $job['program'] . ') - ' . $job['date'] . "
-          </a>
-        </dd>";
-    }
-     $html .= "
-    </dl>";
+  else {
+    return array();
   }
-  
-  return $html;
 }

+ 0 - 33
blast_ui.module

@@ -249,39 +249,6 @@ function show_blast_output($job_id) {
   return '';
 }
 
-/**
- *
- */
-function ajax_blast_ui_example_sequence_callback($form, $form_state) {
-  $sequence_type = $form_state['values']['query_type'];
-  if ($sequence_type == 'nucleotide') {
-    $default_example_sequence = variable_get('blast_ui_nucleotide_example_sequence', 'sample');
-  }
-  elseif ($sequence_type == 'protein') {
-    $default_example_sequence = variable_get('blast_ui_protein_example_sequence', 'sample');
-  }
-  else {
-    $default_example_sequence = 'unknown query type';
-  }
-
-  // If the Show Example checkbox is true then put the example in the textfield
-  if ($form_state['values']['example_sequence']) {
-    // Set the value to be the example sequence (either set by the administrator
-    // or the default set above).
-    $form['query']['FASTA']['#value'] = variable_get(
-      'blast_ui_' . $sequence_type . '_example_sequence',
-      $default_example_sequence
-    );
-  }
-  // Otherwise we want to remove the already displayed example.
-  else {
-    $form['query']['FASTA']['#value'] = '';
-  }
-
-  return $form['query']['FASTA'];
-}
-
-
 /**
  * Enable web services API
  *

+ 33 - 20
includes/blast_ui.admin.inc

@@ -21,29 +21,38 @@ function blast_ui_admin_form($form, $form_state) {
     '#description' => t('You can ignore if your $PATH variable is set. Otherwise, enter the absoulte path to bin folder. For example, /opt/blast/2.2.29+/bin/'),
     '#default_value' => variable_get('blast_path', ''),
   );
-
-  $form['general']['target_upload'] = array(
-    '#type' => 'checkbox',
-    '#title' => 'Enable Taget Sequence Upload',
-    '#default_value' => FALSE,
-    '#description' => 'When this option is checked, a file upload field will '
-      . 'show up on the various BLAST forms under the Database section. This '
-      . 'will allow users to upload a FASTA file they want to BLAST against '
-      . '(database) as well as a FASTA file containing their query, providing '
-      . 'the ultimate in flexibility for your users. However, since this does '
-      . 'allow & in fact encourage the upload of large files which are then '
-      . 'processed into BLAST databases, this option is disabled by default.',
-    '#default_value' => variable_get(
-        'blast_ui_allow_target_upload',
-        FALSE
-      )
-  );
+  
 	$form['general']['blast_threads']= array(
     '#type' => 'textfield',
     '#title' => t('Enter the number of CPU threads to use in blast search.'),
     '#description' => t('You can increase the number to reduce the search time. Before you increase, please check your hardware configurations . A value of one(1) can result in a slower search for some programs eg. tblastn.'),
     '#default_value' => variable_get('blast_threads', 1),
   );
+  
+  $form['file_upload'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Allow File Upload',
+    '#description' => 'The following options allow you to control whether your users can
+      upload files for the query or target respectively. The ability to upload files allows
+      them to more conviently BLAST large sets of sequences. However, the size of the
+      files could be problematic, storage-wise, on your server.<br />'
+  );
+  
+  $form['file_upload']['query_upload'] = array(
+    '#type' => 'checkbox',
+    '#title' => 'Enable Query Sequence Upload',
+    '#description' => 'When checked, a query file upload field will be available on BLAST request forms.',
+    '#default_value' => FALSE,
+    '#default_value' => variable_get('blast_ui_allow_query_upload', TRUE)
+  );
+
+  $form['file_upload']['target_upload'] = array(
+    '#type' => 'checkbox',
+    '#title' => 'Enable Taget Sequence Upload',
+    '#description' => 'When checked, a target file upload field will be available on BLAST request forms.',
+    '#default_value' => FALSE,
+    '#default_value' => variable_get('blast_ui_allow_target_upload', FALSE)
+  );
 
   $form['example_sequence'] = array(
     '#type' => 'fieldset',
@@ -116,7 +125,7 @@ KRSLEEGLKTTGEGLDWGVLFGFGPGLTIETVVLRSVAI';
 }
 
 /**
- * Form validator for the blastdb node
+ * Validate the Admin/Settings form.
  */
 function blast_ui_admin_form_validate($form, &$form_state) {
 	$blast_path = $form_state['values']['blast_path'];
@@ -133,12 +142,16 @@ function blast_ui_admin_form_validate($form, &$form_state) {
 }
 
 /**
- *
+ * Submit the Admin/settings form.
  */
 function blast_ui_admin_form_submit($form, $form_state) {
+
   variable_set('blast_path', $form_state['values']['blast_path']);
+  variable_set('blast_threads', $form_state['values']['blast_threads']);
+  
+  variable_set('blast_ui_allow_query_upload', $form_state['values']['query_upload']);	
   variable_set('blast_ui_allow_target_upload', $form_state['values']['target_upload']);
-	variable_set('blast_threads', $form_state['values']['blast_threads']);
+  
   variable_set('blast_ui_nucleotide_example_sequence', $form_state['values']['nucleotide_example']);
   variable_set('blast_ui_protein_example_sequence', $form_state['values']['protein_example']);
 }

+ 5 - 5
includes/blast_ui.form_advanced_options.inc

@@ -151,7 +151,7 @@ function blast_ui_blastn_advanced_options_form_submit($form, $form_state) {
     'reward'          => $m_m['reward'],
     'culling_limit'   => $qRange,
   );
-}//blast_ui_blastn_advanced_options_form_submit
+}
 
 /**
  * @section
@@ -235,7 +235,7 @@ function blast_ui_blastx_advanced_options_form(&$form, $form_state) {
     '#type' => 'select',
     '#title' => 'Matrix',
     '#options' => $matrix_options,
-    '#default_value' => $default['matrix'],
+    '#default_value' => $defaults['matrix'],
     '#description' => t('Assigns a score for aligning pairs of residues, and determines overall alignment score..'),
     '#ajax' => array(
       'callback' => 'ajax_dependent_dropdown_callback',
@@ -415,7 +415,7 @@ function blast_ui_blastp_advanced_options_form(&$form, $form_state) {
     '#description' => t('Matrix adjustment method to compensate for amino acid composition of sequences'),
   );
 */
-}//blast_ui_blastp_advanced_options_form
+}
 
 /**
  * Validate the advanced options provided by the BLASTp form above.
@@ -1045,7 +1045,7 @@ function blast_ui_tblastn_advanced_options_form(&$form, $form_state) {
     '#title' => t('Gap Costs:'),
     '#prefix' => '<div id="dropdown-second-replace">',
     '#suffix' => '</div>',
-    '#options' => _get_gap_for_matrix($$default['matrix']),
+    '#options' => _get_gap_for_matrix($defaults['matrix']),
     '#default_value' => 2,
     '#description' => t('Cost to create and extend a gap in an alignment.'),
   );
@@ -1058,7 +1058,7 @@ function blast_ui_tblastn_advanced_options_form(&$form, $form_state) {
     '#maxlength' => 20,
     '#description' => t('Limit the number of matches to a query range. This option is useful if many strong matches to one part of a query may prevent BLAST from presenting weaker matches to another part of the query.'),
   );
-}//blast_ui_tblastn_advanced_options_form
+}
 
 /**
  * Validate the advanced options provided by the tBLASTn form above.

+ 117 - 39
includes/blast_ui.form_per_program.inc

@@ -20,6 +20,10 @@ function blast_ui_per_blast_program_form($form, $form_state) {
     drupal_get_path('module', 'blast_ui') . '/theme/css/form.css',
   );
   
+  // We are going to lay out this form as two choices: either look at a recent blast
+  // or execute a new one. We want to theme accordingly so set a class to aid us in such.
+  $form['#attributes'] = array('class' => array('blast-choice-form'));
+  
   // @deepaksomanadh - Code added for edit and resubmit funcitonality
   //   Approach: persist the form data and read it back using JobID
   $job_data = variable_get('job_data', '');
@@ -30,7 +34,7 @@ function blast_ui_per_blast_program_form($form, $form_state) {
     $job_data = array();
     $jid = 0;
   }
-  
+
   // Determine the BLAST program.
   $query_type = $form_state['build_info']['args'][0];
   $db_type = $form_state['build_info']['args'][1];
@@ -75,10 +79,57 @@ function blast_ui_per_blast_program_form($form, $form_state) {
     '#value' => $blast_program
   );
 
+  // CHOOSE RECENT BLAST RESULTS
+  //-----------------------------------
+  // Gets the list of recent jobs filtered to the current blast program (ie: blastn).
+  $recent_jobs = get_recent_blast_jobs(array($blast_program));
+  
+  // If there are recent jobs then show a table of them.
+  if ($recent_jobs) {
+
+    $form['A'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'See Results from a Recent BLAST',
+      '#attributes' => array('class' => array('blast-choice')),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE
+    );
+    
+    $table = array(
+      'header' => array('Query Information', 'Search Target', 'Date Requested', ''),
+      'rows' => array(),
+      'attributes' => array('class' => array('tripal-blast', 'recent-jobs')),
+    );
+      
+    foreach ($recent_jobs as $job) {
+
+      // Define a row for the current job.
+      $table['rows'][] = array(
+        $job['query_info'],
+        $job['target'],
+        $job['date'],
+        l('See Results', $job['job_output_url'])
+      );
+    }
+
+    $form['A']['job_table'] = array(
+      '#type' => 'markup',
+      '#markup' => theme('table', $table),
+    );
+  }
+
+  // REQUEST A NEW BLAST
+  //-----------------------------------
+  $form['B'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Request a New BLAST',
+    '#attributes' => array('class' => array('blast-choice')),
+    '#collapsible' => TRUE,
+  );
+     
   // NUCLEOTIDE QUERY
   //.........................
-
-  $form['query'] = array(
+  $form['B']['query'] = array(
     '#type' => 'fieldset',
     '#title' => t('Enter %type Query Sequence',
       array('%type' => ucfirst($query_type))),
@@ -95,13 +146,13 @@ function blast_ui_per_blast_program_form($form, $form_state) {
   );
 
   // Checkbox to show an example.
-  $form['query']['example_sequence'] = array(
+  $form['B']['query']['example_sequence'] = array(
     '#type' => 'checkbox',
     '#title' => t('Show an Example Sequence'),
     '#prefix' => '<span style="float: right;">',
     '#suffix' => '</span>',
     '#ajax' => array(
-      'callback' => 'ajax_blast_ui_example_sequence_callback',
+      'callback' => 'ajax_blast_ui_perprogram_example_sequence_callback',
       'wrapper'  => 'fasta-textarea',
       'method'   => 'replace',
       'effect'   => 'fade',
@@ -109,7 +160,7 @@ function blast_ui_per_blast_program_form($form, $form_state) {
   );
 
   // Textfield for submitting a mult-FASTA query
-  $form['query']['FASTA'] = array(
+  $form['B']['query']['FASTA'] = array(
     '#type' => 'textarea',
     '#title' => t('Enter FASTA sequence(s)'),
     '#description'=>t('Enter query sequence(s) in the text area.'),
@@ -118,33 +169,33 @@ function blast_ui_per_blast_program_form($form, $form_state) {
     '#suffix' => '</div>',
   );
 
-/*TODO: FIX THIS! Shouldn't come up if not selected in configuration
-  // Upload a file as an alternative to enter a query sequence
-  $form['#attributes']['enctype'] = 'multipart/form-data';
-  $form['query']['UPLOAD'] = array(
-    '#title' => 'Or upload your own query FASTA:  ',
-    '#type' => 'managed_file',
-    '#description' => t('The file should be a plain-text FASTA
-(.fasta, .fna, .fa, .fas) file. In other words, it cannot have formatting as is the
-case with MS Word (.doc, .docx) or Rich Text Format (.rtf). It cannot be greater
-than %max_size in size. <strong>Don\'t forget to press the Upload button before
-attempting to submit your BLAST.</strong>',
-      array(
-        '%max_size' => round(file_upload_max_size() / 1024 / 1024,1) . 'MB'
-      )
-    ),
-    '#upload_validators' => array(
-      'file_validate_extensions' => array('fasta fna fa fas'),
-      'file_validate_size' => array(file_upload_max_size()),
-    ),
-  );
-*/
+  if (variable_get('blast_ui_allow_query_upload', TRUE)) {
+    // Upload a file as an alternative to enter a query sequence
+    $form['#attributes']['enctype'] = 'multipart/form-data';
+    $form['B']['query']['UPLOAD'] = array(
+      '#title' => 'Or upload your own query FASTA:  ',
+      '#type' => 'managed_file',
+      '#description' => t('The file should be a plain-text FASTA
+  (.fasta, .fna, .fa, .fas) file. In other words, it cannot have formatting as is the
+  case with MS Word (.doc, .docx) or Rich Text Format (.rtf). It cannot be greater
+  than %max_size in size. <strong>Don\'t forget to press the Upload button before
+  attempting to submit your BLAST.</strong>',
+        array(
+          '%max_size' => round(file_upload_max_size() / 1024 / 1024,1) . 'MB'
+        )
+      ),
+      '#upload_validators' => array(
+        'file_validate_extensions' => array('fasta fna fa fas'),
+        'file_validate_size' => array(file_upload_max_size()),
+      ),
+    );
+  }
 
 
   // BLAST DATABASE
   //.........................
 
-  $form['DB'] = array(
+  $form['B']['DB'] = array(
     '#type' => 'fieldset',
     '#title' => t('Choose Search Target'),
     '#description' => t('Choose from one of the %type BLAST databases listed '
@@ -157,7 +208,7 @@ attempting to submit your BLAST.</strong>',
   );
 
   $options = get_blast_database_options($db_type);
-  $form['DB']['SELECT_DB'] = array(
+  $form['B']['DB']['SELECT_DB'] = array(
     '#type' => 'select',
     '#title' => t('%type BLAST Databases:', array('%type' => ucfirst($db_type))),
     '#options' => $options,
@@ -167,7 +218,7 @@ attempting to submit your BLAST.</strong>',
   if (variable_get('blast_ui_allow_target_upload', FALSE)) {
     // Upload a file as an alternative to selecting an existing BLAST database
     $form['#attributes']['enctype'] = 'multipart/form-data';
-    $form['DB']['DBUPLOAD'] = array(
+    $form['B']['DB']['DBUPLOAD'] = array(
       '#title' => 'Or upload your own dataset:  ',
       '#type' => 'managed_file',
       '#description' => t('The file should be a plain-text FASTA
@@ -192,7 +243,7 @@ attempting to submit your BLAST.</strong>',
   // These options will be different depending upon the program selected.
   // Therefore, allow for program-specific callbacks to populate these options.
 
-  $form['ALG'] = array(
+  $form['B']['ALG'] = array(
    '#type' => 'fieldset',
    '#title' => t('Advanced Options'),
    '#collapsible' => TRUE,
@@ -203,19 +254,15 @@ attempting to submit your BLAST.</strong>',
   if (function_exists($advanced_options_form)) {
     call_user_func_array($advanced_options_form, array(&$form, $form_state));
   }
+  $form['B']['ALG'] = array_merge($form['B']['ALG'], $form['ALG']);
+  unset($form['ALG']);
 
   // Submit
   //.........................
-  $form['submit'] = array(
+  $form['B']['submit'] = array(
     '#type' => 'submit',
     '#default_value' => ' BLAST ',
   );
-
-  // Recent jobs list
-  $form['recentjobs'] = array(
-     '#type' => 'fieldset',
-     '#prefix' => get_recent_jobs(),
-   );
    
   return $form;
 }
@@ -507,4 +554,35 @@ your sequence headers include pipes (i.e.: | ) they adhere to '
     $msg .= "Please contact the site administrator.";
     drupal_set_message($msg, 'error');
   }
-}//blast_ui_per_blast_program_form_submit
+}
+
+/**
+ * AJAX: Replace the sequence textarea with one containing an example.
+ */
+function ajax_blast_ui_perprogram_example_sequence_callback($form, $form_state) {
+  $sequence_type = $form_state['values']['query_type'];
+  
+  // Choose the example sequence based on the sequence type of the query.
+  if ($sequence_type == 'nucleotide') {
+    $example_sequence = variable_get('blast_ui_nucleotide_example_sequence', 'sample');
+  }
+  elseif ($sequence_type == 'protein') {
+    $example_sequence = variable_get('blast_ui_protein_example_sequence', 'sample');
+  }
+  else {
+    $example_sequence = 'unknown query type';
+  }
+
+  // If the Show Example checkbox is true then put the example in the textfield
+  if ($form_state['values']['example_sequence']) {
+
+    // Set the value to be the example sequence (set in the admin form).
+    $form['B']['query']['FASTA']['#value'] = $example_sequence;
+  }
+  // Otherwise we want to remove the already displayed example.
+  else {
+    $form['B']['query']['FASTA']['#value'] = '';
+  }
+
+  return $form['B']['query']['FASTA'];
+}

+ 39 - 1
theme/blast_nucleotide_user_menupage.tpl.php

@@ -28,6 +28,14 @@ gene based on similarity of sequence.</p>
 <blockquote>Altschul,S.F., Gish,W., Miller,W., Myers,E.W. and Lipman,D.J. (1990) Basic
 local alignment search tool. J. Mol. Biol., 215, 403–410.</blockquote>
 
+<h2> BLAST Search </h2>
+<p>
+  Search for one or more of your sequences (using BLAST). First pick 
+  a query type (nucleotide or protein). You will be able to set search 
+  parameters on the next page. Choose the appropriate program based on the Query type and Target
+  database type. Please click on the program name to view the search form.
+<p>
+
 <table>
   <tr>
     <th>Query Type</th>
@@ -56,4 +64,34 @@ local alignment search tool. J. Mol. Biol., 215, 403–410.</blockquote>
     <td><?php print l('blastp', './blast/protein/protein');?>:
       Search protein database using a protein query.</td>
   </tr>
-</table>
+</table>
+
+<!-- Recent Jobs -->
+<?php
+
+  // Gets the list of recent jobs filtered to the current blast program (ie: blastn).
+  $recent_jobs = get_recent_blast_jobs(array('blastn','blastx'));
+  if ($recent_jobs) {
+  
+    print '<h2>Recent Jobs</h2>';
+    
+    $table = array(
+      'header' => array('Query Information', 'Search Target', 'Date Requested', ''),
+      'rows' => array(),
+      'attributes' => array('class' => array('tripal-blast', 'recent-jobs')),
+    );
+  
+    foreach ($recent_jobs as $job) {
+
+      // Define a row for the current job.
+      $table['rows'][] = array(
+        $job['query_info'],
+        $job['target'],
+        $job['date'],
+        l('See Results', $job['job_output_url'])
+      );
+    }
+    
+    print theme('table', $table);
+  }
+?>

+ 39 - 1
theme/blast_protein_user_menupage.tpl.php

@@ -27,6 +27,14 @@ gene based on similarity of sequence.</p>
 <blockquote>Altschul,S.F., Gish,W., Miller,W., Myers,E.W. and Lipman,D.J. (1990) Basic
 local alignment search tool. J. Mol. Biol., 215, 403–410.</blockquote>
 
+<h2> BLAST Search </h2>
+<p>
+  Search for one or more of your sequences (using BLAST). First pick 
+  a query type (nucleotide or protein). You will be able to set search 
+  parameters on the next page. Choose the appropriate program based on the Query type and Target
+  database type. Please click on the program name to view the search form.
+<p>
+
 <table>
   <tr>
     <th>Query Type</th>
@@ -55,4 +63,34 @@ local alignment search tool. J. Mol. Biol., 215, 403–410.</blockquote>
     <td><?php print l('blastp', './blast/protein/protein');?>:
       Search protein database using a protein query.</td>
   </tr>
-</table>
+</table>
+
+<!-- Recent Jobs -->
+<?php
+
+  // Gets the list of recent jobs filtered to the current blast program (ie: blastn).
+  $recent_jobs = get_recent_blast_jobs(array('tblastn','blastp'));
+  if ($recent_jobs) {
+  
+    print '<h2>Recent Jobs</h2>';
+    
+    $table = array(
+      'header' => array('Query Information', 'Search Target', 'Date Requested', ''),
+      'rows' => array(),
+      'attributes' => array('class' => array('tripal-blast', 'recent-jobs')),
+    );
+  
+    foreach ($recent_jobs as $job) {
+
+      // Define a row for the current job.
+      $table['rows'][] = array(
+        $job['query_info'],
+        $job['target'],
+        $job['date'],
+        l('See Results', $job['job_output_url'])
+      );
+    }
+    
+    print theme('table', $table);
+  }
+?>

+ 30 - 1
theme/blast_report.tpl.php

@@ -360,4 +360,33 @@ else {
 <p> 
   <a style ="align:center" href="<?php print '../../'. $job_id_data['job_url'] . '?jid=' . base64_encode($job_id) ?>">Edit this query and re-submit</a>  
 </p>
-<?php echo get_recent_jobs(); ?>
+
+<!-- Recent Jobs -->
+<?php
+
+  // Gets the list of recent jobs filtered to the current blast program (ie: blastn).
+  $recent_jobs = get_recent_blast_jobs();
+  if ($recent_jobs) {
+  
+    print '<h2>Recent Jobs</h2>';
+    
+    $table = array(
+      'header' => array('Query Information', 'Search Target', 'Date Requested', ''),
+      'rows' => array(),
+      'attributes' => array('class' => array('tripal-blast', 'recent-jobs')),
+    );
+  
+    foreach ($recent_jobs as $job) {
+
+      // Define a row for the current job.
+      $table['rows'][] = array(
+        $job['query_info'],
+        $job['target'],
+        $job['date'],
+        l('See Results', $job['job_output_url'])
+      );
+    }
+    
+    print theme('table', $table);
+  }
+?>

+ 49 - 8
theme/blast_user_menupage.tpl.php

@@ -7,14 +7,26 @@
 
 ?>
 
-<h1> BLAST Search </h1>
+<p>In bioinformatics, BLAST (Basic Local Alignment Search Tool) is an algorithm
+for comparing primary biological sequence information, such as the amino-acid
+sequences of different proteins or the nucleotides of DNA sequences. A BLAST
+search enables a researcher to compare a query sequence with a library or
+database of sequences, and identify library sequences that resemble the query
+sequence above a certain threshold. Different types of BLASTs are available
+according to the query sequences. For example, following the discovery of a
+previously unknown gene in the mouse, a scientist will typically perform a
+BLAST search of the human genome to see if humans carry a similar gene;
+BLAST will identify sequences in the human genome that resemble the mouse
+gene based on similarity of sequence.</p>
+
+<blockquote>Altschul,S.F., Gish,W., Miller,W., Myers,E.W. and Lipman,D.J. (1990) Basic
+local alignment search tool. J. Mol. Biol., 215, 403–410.</blockquote>
+
+<h2> BLAST Search </h2>
 <p>
   Search for one or more of your sequences (using BLAST). First pick 
   a query type (nucleotide or protein). You will be able to set search 
-  parameters on the next page.
-</p>
-<p>
-  Choose the appropriate program based on the Query type and Target
+  parameters on the next page. Choose the appropriate program based on the Query type and Target
   database type. Please click on the program name to view the search form.
 <p>
 
@@ -25,7 +37,7 @@
     <th>BLAST Program</th>
   </tr>
   <tr>
-    <td  rowspan="2">Nucleotide</td>
+    <td  rowspan="2"><?php print l('Nucleotide', './blast/nucleotide');?></td>
     <td>Nucleotide</td>
     <td><?php print l('blastn', './blast/nucleotide/nucleotide');?>:
       Search a nucleotide database using a nucleotide query.</td>
@@ -36,7 +48,7 @@
       Search protein database using a translated nucleotide query.</td>
   </tr>
   <tr>
-    <td  rowspan="2">Protein</td>
+    <td  rowspan="2"><?php print l('Protein', './blast/protein');?></td>
     <td>Nucleotide</td>
     <td><?php print l('tblastn', './blast/protein/nucleotide');?>:
       Search translated nucleotide database using a protein query.</td>
@@ -48,4 +60,33 @@
   </tr>
 </table>
 
-<?php echo get_recent_jobs(); ?>
+<!-- Recent Jobs -->
+<?php
+
+  // Gets the list of recent jobs filtered to the current blast program (ie: blastn).
+  $recent_jobs = get_recent_blast_jobs();
+  if ($recent_jobs) {
+  
+    print '<h2>Recent Jobs</h2>';
+    
+    $table = array(
+      'header' => array('Query Information', 'Search Target', 'Date Requested', ''),
+      'rows' => array(),
+      'attributes' => array('class' => array('tripal-blast', 'recent-jobs')),
+    );
+  
+    foreach ($recent_jobs as $job) {
+
+      // Define a row for the current job.
+      $table['rows'][] = array(
+        $job['query_info'],
+        $job['target'],
+        $job['date'],
+        l('See Results', $job['job_output_url'])
+      );
+    }
+    
+    print theme('table', $table);
+  }
+?>
+