Browse Source

Re-factored BLAST submission form for code-reuse

Lacey Sanderson 10 years ago
parent
commit
2e64e6f2d9

+ 53 - 1
api/blast_ui.api.inc

@@ -20,10 +20,19 @@ function get_blast_database_options($type) {
   // Get all BlastDB nodes
   $nodes  = node_load_multiple(array(), array('type'=> 'blastdb'));
 
+  // Support obsolete database type n/p
+  $obs_type = '';
+  if ($type == 'protein') {
+    $obs_type = 'p';
+  }
+  else {
+    $obs_type = 'n';
+  }
+
   $options = array();
   foreach ($nodes as $node) {
     if ( isset($node) && isset($node->db_dbtype) ) {
-	    if ( ($node->db_dbtype == $type) ) {
+	    if ( ($node->db_dbtype == $type) OR ($node->db_dbtype == $obs_type) ) {
         $options[$node->nid] = $node->db_name;
 	    }
     }
@@ -128,4 +137,47 @@ function run_BLAST_tripal_job($program, $query, $database, $output_filestub, $op
 
 
   print "\nDone!\n";
+}
+
+/**
+ * FASTA validating parser
+ *
+ * A sequence in FASTA format begins with a single-line description, followed
+ * by lines of sequence data.The description line is distinguished from the
+ * sequence data by a greater-than (">") symbol in the first column. The word
+ * following the ">" symbol is the identifier of the sequence, and the rest of
+ * the line is the description (both are optional). There should be no space
+ * between the ">" and the first letter of the identifier. The sequence ends
+ * if another line starting with a ">" appears which indicates the start of
+ * another sequence.
+ *
+ * @param $type
+ *   The type of sequence to be validated (ie: either nucleotide or protein).
+ * @param $sequence
+ *  A string of characters to be validated.
+ *
+ * @return
+ *  Return a boolean. 1 if the sequence does not pass the format valifation stage and 0 otherwise.
+ *
+ */
+function validate_fasta_sequence($type, $sequence) {
+
+  if ($type == 'nucleotide') {
+    $fastaIdRegEx = '/^>.*(\\n|\\r)/';
+    $fastaSeqRegEx = '/[^acgntuACGNTU\n\r]/';
+    if ( preg_match($fastaSeqRegEx,$sequence) && !(preg_match($fastaIdRegEx,$sequence)) ) {
+      return TRUE;
+    } else {
+      return FALSE;
+    }
+  } elseif ($type == 'protein') {
+    $fastaIdRegEx = '/^>.*(\\n|\\r)/';
+    $fastaSeqRegEx = '/[^acgturykmswbdhvnxACGTURYKMSWBDHVNX\*\-\n\r]/';
+    if ( preg_match($fastaSeqRegEx,$sequence) && !(preg_match($fastaIdRegEx,$sequence)) ) {
+      return TRUE;
+    } else {
+      return FALSE;
+    }
+  }
+  return FALSE;
 }

+ 56 - 14
blast_ui.module

@@ -5,9 +5,11 @@
  * The main file for the blast UI module.
  */
 
-// Type-specific BLAST functionality
-require_once 'includes/blast_ui.blastn.inc';
-require_once 'includes/blast_ui.blastp.inc';
+// BLAST Submission Forms
+require_once 'includes/blast_ui.form_common.inc';
+require_once 'includes/blast_ui.form_advanced_options.inc';
+// NOTE: The forms themeselves are included using hook_menu to ensure they
+// are only inlcuded when needed.
 
 // BLAST DB Node functionality
 require_once 'includes/blast_ui.node.inc';
@@ -26,31 +28,71 @@ require_once 'includes/blast_ui.admin.inc';
  */
 function blast_ui_menu() {
 
-  // Parent menu-item for BLAST submission
+  // Single All-in-One BLAST submission form
   $items['blast'] = array(
     'title' => 'BLAST',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('blast_nucleotide_form'),
+    'page arguments' => array('blast_ui_all_in_one_form'),
     'access arguments' => array('access content'),
+    'file' => 'includes/blast_ui.form_single.inc',
+    'type' => MENU_SUGGESTED_ITEM
+  );
+
+  // Per Query Type BLAST submission forms
+  $items['blast/nucleotide'] = array(
+    'title' => 'Nucleotide Query',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('blast_ui_per_query_type_form', 1, 2),
+    'access arguments' => array('access content'),
+    'file' => 'includes/blast_ui.form_per_query_type.inc',
+    'type' => MENU_NORMAL_ITEM,
+    'expanded' => TRUE,
+  );
+
+  $items['blast/protein'] = array(
+    'title' => 'Protein Query',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('blast_ui_per_query_type_form', 1),
+    'access arguments' => array('access content'),
+    'file' => 'includes/blast_ui.form_per_query_type.inc',
     'type' => MENU_NORMAL_ITEM,
     'expanded' => TRUE,
   );
 
-  // Nucleotide BLAST submission form
-  $items['blast/blastn'] = array(
-    'title' => 'Nucleotide BLAST',
+  // Per BLAST-program submission forms
+  $items['blast/nucleotide/nucleotide'] = array(
+    'title' => 'BLASTn',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('blast_ui_per_blast_program_form', 1, 2),
+    'access arguments' => array('access content'),
+    'file' => 'includes/blast_ui.form_per_program.inc',
+    'type' => MENU_NORMAL_ITEM
+  );
+
+  $items['blast/nucleotide/protein'] = array(
+    'title' => 'BLASTx',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('blast_ui_per_blast_program_form', 1, 2),
+    'access arguments' => array('access content'),
+    'file' => 'includes/blast_ui.form_per_program.inc',
+    'type' => MENU_NORMAL_ITEM
+  );
+
+  $items['blast/protein/nucleotide'] = array(
+    'title' => 'tBLASTn',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('blast_nucleotide_form'),
+    'page arguments' => array('blast_ui_per_blast_program_form', 1, 2),
     'access arguments' => array('access content'),
+    'file' => 'includes/blast_ui.form_per_program.inc',
     'type' => MENU_NORMAL_ITEM
   );
 
-  // Protein  BLAST submission form
-  $items['blast/blastp'] = array(
-    'title' => 'Protein BLAST',
+  $items['blast/protein/protein'] = array(
+    'title' => 'BLASTp',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('blast_protein_form'),
+    'page arguments' => array('blast_ui_per_blast_program_form', 1, 2),
     'access arguments' => array('access content'),
+    'file' => 'includes/blast_ui.form_per_program.inc',
     'type' => MENU_NORMAL_ITEM
   );
 
@@ -156,7 +198,7 @@ function ajax_blast_ui_example_sequence_callback($form, $form_state) {
 
   // First, set a default example sequence in case administrators have not yet
   // bothered to set their own.
-  $sequence_type = $form_state['values']['sequence_type'];
+  $sequence_type = $form_state['values']['query_type'];
   if ($sequence_type == 'nucleotide') {
     $default_example_sequence = '>partial lipoxygenase Glyma15g03040
 TTTCGTATGA GATTAAAATG TGTGAAATTT TGTTTGATAG GACATGGGAA

+ 957 - 0
includes/blast_ui.form_advanced_options.inc

@@ -0,0 +1,957 @@
+<?php
+
+/**
+ * @file
+ * Contains helper functions related to program-specific advanced options.
+ */
+
+/**
+ * @section
+ * BLASTn: Search a nucleotide database using a nucleotide query.
+ * ----------------------------------------------------------------------------
+ */
+
+/**
+ * Adds the BLASTn Advanced Options to the passed in form.
+ *
+ * This form function is meant to be called within another form definition.
+ *
+ * @param $form
+ *   The form the advanced options should be added to. This form already
+ *   contains a $form['ALG'] fieldset meant to contain the advanced options.
+ * @param $formstate
+ *   The current state fo the form passed in as $form.
+ */
+function blast_ui_blastn_advanced_options_form(&$form, $form_state) {
+
+  // General parameters
+  //.........................
+
+  $form['ALG']['GParam'] = array(
+   '#type' => 'fieldset',
+   '#title' => t('General parameters'),
+   '#collapsible' => FALSE,
+  );
+
+  $form['ALG']['GParam']['maxTarget'] = array(
+    '#type' => 'select',
+    '#title' => t('Max target sequences:'),
+    '#options' => array(
+      0 => t('10'),
+      1 => t('50'),
+      2 => t('100'),
+      3 => t('250'),
+      4 => t('500'),
+      5 => t('1000'),
+      6 => t('5000'),
+      7 => t('10000'),
+      8 => t('20000'),
+    ),
+    '#default_value' => 2,
+    '#description' => t('Select the maximum number of aligned sequences to display'),
+  );
+
+  $form['ALG']['GParam']['shortQueries'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Automatically adjust parameters for short input sequences'),
+    '#default_value' => TRUE,
+  );
+
+  $form['ALG']['GParam']['eVal'] = array(
+    '#type' => 'textfield',
+    '#title' => t('e-Value (Expected Threshold)'),
+    '#default_value' => 10,
+    '#size' => 12,
+    '#maxlength' => 20,
+    '#description' => t('Expected number of chance matches in a random model. This number should be give in a decimal format. <a href="http://www.ncbi.nlm.nih.gov/BLAST/blastcgihelp.shtml#expect" target="_blank">More Information</a> | <a href="https://www.youtube.com/watch?v=nO0wJgZRZJs" target="_blank">Expect value vedio tutorial</a>'),
+  );
+
+  $form['ALG']['GParam']['wordSize'] = array(
+    '#type' => 'select',
+    '#title' => t('Word size:'),
+    '#options' => array(
+      0 => t('16'),
+      1 => t('20'),
+      2 => t('24'),
+      3 => t('28'),
+      4 => t('32'),
+      5 => t('48'),
+      6 => t('64'),
+      7 => t('128'),
+      8 => t('256'),
+    ),
+    '#default_value' => 3,
+    '#description' => t('The length of the seed that initiates an alignment'),
+  );
+
+  $form['ALG']['GParam']['qRange'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Max matches in a query range'),
+    '#default_value' => 0,
+    '#size' => 12,
+    '#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.'),
+  );
+
+  // Scoring parameters
+  //.........................
+
+  $form['ALG']['SParam'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Scoring parameters'),
+    '#collapsible' => FALSE,
+  );
+
+  $form['ALG']['SParam']['M&MScores'] = array(
+    '#type' => 'select',
+    '#title' => t('Match/Mismatch Scores:'),
+    '#options' => array(
+       0 => t('1,-2'),
+       1 => t('1,-3'),
+       2 => t('1,-4'),
+       3 => t('2,-3'),
+       4 => t('4,-5'),
+       5 => t('1,-1'),
+     ),
+    '#default_value' => 0,
+    '#description' => t('Reward and penalty for matching and mismatching bases.'),
+   );
+
+   $form['ALG']['SParam']['gapCost'] = array(
+    '#type' => 'select',
+    '#title' => t('Gap Costs:'),
+    '#options' => array(
+      0 => t('Existence: 5 Extension: 2'),
+      1 => t('Existence: 2 Extension: 2'),
+      2 => t('Existence: 1 Extension: 2'),
+      3 => t('Existence: 0 Extension: 2'),
+      4 => t('Existence: 3 Extension: 1'),
+      5 => t('Existence: 2 Extension: 1'),
+      6 => t('Existence: 1 Extension: 1'),
+    ),
+    '#default_value' => 0,
+    '#description' => t('Cost to create and extend a gap in an alignment. Linear costs are available only with megablast and are determined by the match/mismatch scores.'),
+  );
+
+}
+
+/**
+ * Validate the advanced options provided by the BLASTn form above.
+ *
+ * @see blast_ui_blastn_advanced_options_form().
+ */
+function blast_ui_blastn_advanced_options_form_validate($form, $form_state) { }
+
+/**
+ * Processed the advanced options provided by the BLASTn form above.
+ *
+ * @see blast_ui_blastn_advanced_options_form().
+ */
+function blast_ui_blastn_advanced_options_form_submit($form, $form_state) {
+
+  $eVal = $form_state['values']['eVal'];
+
+  $trgtKey = $form_state['values']['maxTarget'];
+  $numAlign = $form['ALG']['GParam']['maxTarget']['#options'][$trgtKey];
+
+  $wsKey = $form_state['values']['wordSize'];
+  $wordSize = $form['ALG']['GParam']['wordSize']['#options'][$wsKey];
+
+  // Expand Gap Cost key into open and extend penalties
+  $gapKey = $form_state['values']['gapCost'];
+  switch ($gapKey) {
+   case 0:
+      $gapOpen = 5;
+      $gapExtend = 2;
+      break;
+   case 1:
+      $gapOpen = 2;
+      $gapExtend = 2;
+      break;
+   case 2:
+      $gapOpen = 1;
+      $gapExtend = 2;
+      break;
+   case 3:
+      $gapOpen = 0;
+      $gapExtend = 2;
+      break;
+   case 4:
+      $gapOpen = 3;
+      $gapExtend = 1;
+      break;
+   case 5:
+      $gapOpen = 2;
+      $gapExtend = 1;
+      break;
+   case 6:
+      $gapOpen = 1;
+      $gapExtend = 1;
+      break;
+  }
+
+  // Epand Match/Mismatch option into penalty/reward values
+  // @todo Amir: Is the switch supposed to be for $scoreKey?
+  $scoreKey = $form_state['values']['M&MScores'];
+  switch ($gapKey) {
+   case 0:
+      $penalty = -2;
+      $reward = 1;
+      break;
+   case 1:
+      $penalty = -3;
+      $reward = 1;
+      break;
+   case 2:
+      $penalty = -4;
+      $reward = 1;
+      break;
+   case 3:
+      $penalty = -3;
+      $reward = 2;
+      break;
+   case 4:
+      $penalty = -5;
+      $reward = 4;
+      break;
+   case 5:
+      $penalty = -1;
+      $reward = 1;
+      break;
+  }
+
+  return array(
+    'evalue' => $eVal,
+    'word_size' => $wordSize,
+    'gapopen' => $gapOpen,
+    'gapextend' => $gapExtend,
+    'penalty' =>  $penalty,
+    'reward' => $reward
+  );
+}
+
+/**
+ * @section
+ * BLASTx: Search protein database using a translated nucleotide query.
+ * ----------------------------------------------------------------------------
+ */
+
+/**
+ * Adds the BLASTx Advanced Options to the passed in form.
+ *
+ * This form function is meant to be called within another form definition.
+ *
+ * @param $form
+ *   The form the advanced options should be added to. This form already
+ *   contains a $form['ALG'] fieldset meant to contain the advanced options.
+ * @param $formstate
+ *   The current state fo the form passed in as $form.
+ */
+function blast_ui_blastx_advanced_options_form(&$form, $form_state) { }
+
+/**
+ * Validate the advanced options provided by the BLASTn form above.
+ *
+ * @see blast_ui_blastx_advanced_options_form().
+ */
+function blast_ui_blastx_advanced_options_form_validate($form, $form_state) { }
+
+/**
+ * Processed the advanced options provided by the BLASTx form above.
+ *
+ * @see blast_ui_blastx_advanced_options_form().
+ */
+function blast_ui_blastx_advanced_options_form_submit($form, $form_state) { }
+
+/**
+ * @section
+ * BLASTp: Search protein database using a protein query.
+ * ----------------------------------------------------------------------------
+ */
+
+/**
+ * Adds the BLASTp Advanced Options to the passed in form.
+ *
+ * This form function is meant to be called within another form definition.
+ *
+ * @param $form
+ *   The form the advanced options should be added to. This form already
+ *   contains a $form['ALG'] fieldset meant to contain the advanced options.
+ * @param $formstate
+ *   The current state fo the form passed in as $form.
+ */
+function blast_ui_blastp_advanced_options_form(&$form, $form_state) {
+
+  //General parameters
+
+  $form['ALG']['GParam'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('General parameters'),
+    '#collapsible' => FALSE,
+  );
+
+  $form['ALG']['GParam']['maxTarget'] = array(
+    '#type' => 'select',
+    '#title' => t('Max target sequences:'),
+    '#options' => array(
+       0 => t('10'),
+       1 => t('50'),
+       2 => t('100'),
+       3 => t('250'),
+       4 => t('500'),
+       5 => t('1000'),
+       6 => t('5000'),
+       7 => t('10000'),
+       8 => t('20000'),
+    ),
+    '#default_value' => 2,
+    '#description' => t('Select the maximum number of aligned sequences to display'),
+  );
+
+  $form['ALG']['GParam']['shortQueries'] = array(
+   '#type' => 'checkbox',
+   '#title' => t('Automatically adjust parameters for short input sequences'),
+   '#default_value' => TRUE,
+  );
+
+  $form['ALG']['GParam']['eVal'] = array(
+  	'#type' => 'textfield',
+  	'#title' => t('e-value(Expect threshold)'),
+  	'#default_value' => 10,
+  	'#size' => 12,
+  	'#maxlength' => 20,
+  	'#description' => t('Expected number of chance matches in a random model.'),
+  );
+
+  $form['ALG']['GParam']['wordSize'] = array(
+    '#type' => 'select',
+    '#title' => t('Word size:'),
+    '#options' => array(
+       0 => t('2'),
+       1 => t('3'),
+    ),
+    '#default_value' => 1,
+    '#description' => t('The length of the seed that initiates an alignment'),
+  );
+
+  $form['ALG']['GParam']['qRange'] = array(
+   '#type' => 'textfield',
+   '#title' => t('Max matches in a query range'),
+   '#default_value' => 0,
+   '#size' => 12,
+   '#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.'),
+  );
+
+  // Scoring parameters
+
+  $form['ALG']['SParam'] = array(
+   '#type' => 'fieldset',
+   '#title' => t('Scoring parameters'),
+   '#collapsible' => FALSE,
+  );
+
+  $options_first = _ajax_example_get_first_dropdown_options();
+  $selected = isset($form_state['values']['MATRIX'] ) ? $form_state['values']['MATRIX'] : key($options_first);
+
+  $form['ALG']['SParam']['MATRIX'] = array(
+    '#type' => 'select',
+    '#title' => 'Matrix',
+    '#options' => $options_first,
+    '#default_value' => $selected,
+    '#description' => t('Assigns a score for aligning pairs of residues, and determines overall alignment score..'),
+    '#ajax' => array(
+      'callback' => 'ajax_example_dependent_dropdown_callback',
+      'wrapper' => 'dropdown-second-replace',
+    ),
+  );
+
+  $form['ALG']['SParam']['gapCost'] = array(
+    '#type' => 'select',
+    '#title' => t('Gap Costs:'),
+    '#prefix' => '<div id="dropdown-second-replace">',
+    '#suffix' => '</div>',
+    '#options' => _ajax_example_get_second_dropdown_options($selected),
+    '#default_value' => 2,
+    '#description' => t('Cost to create and extend a gap in an alignment.'),
+  );
+
+  $form['ALG']['SParam']['M&MScores'] = array(
+    '#type' => 'select',
+    '#title' => t('Match/Mismatch Scores:'),
+    '#options' => array(
+      0 => t('No adjustment'),
+      1 => t('Composition-based statistics'),
+      2 => t('Conditional compositional score matrix adjustment'),
+      3 => t('Universal composition score matrix adjustment '),
+    ),
+    '#default_value' => 2,
+    '#description' => t('Matrix adjustment method to compensate for amino acid composition of sequences'),
+  );
+
+}
+
+/**
+ * Validate the advanced options provided by the BLASTn form above.
+ *
+ * @see blast_ui_blastp_advanced_options_form().
+ */
+function blast_ui_blastp_advanced_options_form_validate($form, $form_state) { }
+
+/**
+ * Processed the advanced options provided by the BLASTp form above.
+ *
+ * @see blast_ui_blastp_advanced_options_form().
+ */
+function blast_ui_blastp_advanced_options_form_submit($form, $form_state) {
+
+  $eVal = $form_state['values']['eVal'];
+
+  $trgtKey = $form_state['values']['maxTarget'];
+  $numAlign = $form['ALG']['GParam']['maxTarget']['#options'][$trgtKey];
+
+  $wsKey = $form_state['values']['wordSize'];
+  $wordSize = $form['ALG']['GParam']['wordSize']['#options'][$wsKey];
+
+  // Expand Gap Cost key into open and extend penalties
+  $gapKey = $form_state['values']['MATRIX'];
+  switch ($gapKey) {
+   case 0:
+     $matrix ="PAM30";
+     $gapKey = $form_state['values']['gapCost'];
+     switch ($gapKey) {
+      case 0:
+         $gapOpen = 7;
+         $gapExtend = 2;
+         break;
+      case 1:
+         $gapOpen = 6;
+         $gapExtend = 2;
+         break;
+      case 2:
+         $gapOpen = 5;
+         $gapExtend = 2;
+         break;
+      case 3:
+         $gapOpen = 10;
+         $gapExtend = 1;
+         break;
+      case 4:
+         $gapOpen = 9;
+         $gapExtend = 1;
+         break;
+      case 5:
+         $gapOpen = 8;
+         $gapExtend = 1;
+         break;
+     }
+     break;
+   case 1:
+     $matrix ="PAM70";
+     $gapKey = $form_state['values']['gapCost'];
+     switch ($gapKey) {
+      case 0:
+         $gapOpen = 8;
+         $gapExtend = 2;
+         break;
+      case 1:
+         $gapOpen = 7;
+         $gapExtend = 2;
+         break;
+      case 2:
+         $gapOpen = 6;
+         $gapExtend = 2;
+         break;
+      case 3:
+         $gapOpen = 11;
+         $gapExtend = 1;
+         break;
+      case 4:
+         $gapOpen = 10;
+         $gapExtend = 1;
+         break;
+      case 5:
+         $gapOpen = 9;
+         $gapExtend = 1;
+         break;
+     }
+     break;
+   case 2:
+     $matrix ="PAM250";
+     $gapKey = $form_state['values']['gapCost'];
+     switch ($gapKey) {
+      case 0:
+         $gapOpen = 15;
+         $gapExtend = 3;
+         break;
+      case 1:
+         $gapOpen = 14;
+         $gapExtend = 3;
+         break;
+      case 2:
+         $gapOpen = 13;
+         $gapExtend = 3;
+         break;
+      case 3:
+         $gapOpen = 12;
+         $gapExtend = 3;
+         break;
+      case 4:
+         $gapOpen = 11;
+         $gapExtend = 3;
+         break;
+      case 5:
+         $gapOpen = 17;
+         $gapExtend = 2;
+         break;
+      case 6:
+         $gapOpen = 16;
+         $gapExtend = 2;
+         break;
+      case 7:
+         $gapOpen = 15;
+         $gapExtend = 2;
+         break;
+      case 8:
+         $gapOpen = 14;
+         $gapExtend = 2;
+         break;
+      case 9:
+         $gapOpen = 13;
+         $gapExtend = 2;
+         break;
+      case 10:
+         $gapOpen = 21;
+         $gapExtend = 1;
+         break;
+      case 11:
+         $gapOpen = 20;
+         $gapExtend = 1;
+         break;
+      case 12:
+         $gapOpen = 19;
+         $gapExtend = 1;
+         break;
+      case 13:
+         $gapOpen = 18;
+         $gapExtend = 1;
+         break;
+      case 14:
+         $gapOpen = 17;
+         $gapExtend = 1;
+         break;
+     }
+     break;
+   case 3:
+     $matrix ="BLOSUM80";
+     $gapKey = $form_state['values']['gapCost'];
+     switch ($gapKey) {
+      case 0:
+         $gapOpen = 8;
+         $gapExtend = 2;
+         break;
+      case 1:
+         $gapOpen = 7;
+         $gapExtend = 2;
+         break;
+      case 2:
+         $gapOpen = 6;
+         $gapExtend = 2;
+         break;
+      case 3:
+         $gapOpen = 11;
+         $gapExtend = 1;
+         break;
+      case 4:
+         $gapOpen = 10;
+         $gapExtend = 1;
+         break;
+      case 5:
+         $gapOpen = 9;
+         $gapExtend = 1;
+         break;
+     }
+      break;
+   case 4:
+     $matrix ="BLOSUM62";
+     $gapKey = $form_state['values']['gapCost'];
+     switch ($gapKey) {
+      case 0:
+         $gapOpen = 11;
+         $gapExtend = 2;
+         break;
+      case 1:
+         $gapOpen = 10;
+         $gapExtend = 2;
+         break;
+      case 2:
+         $gapOpen = 9;
+         $gapExtend = 2;
+         break;
+      case 3:
+         $gapOpen = 8;
+         $gapExtend = 2;
+         break;
+      case 4:
+         $gapOpen = 7;
+         $gapExtend = 2;
+         break;
+      case 5:
+         $gapOpen = 6;
+         $gapExtend = 2;
+         break;
+      case 6:
+         $gapOpen = 13;
+         $gapExtend = 1;
+         break;
+      case 7:
+         $gapOpen = 12;
+         $gapExtend = 1;
+         break;
+      case 8:
+         $gapOpen = 11;
+         $gapExtend = 1;
+         break;
+      case 9:
+         $gapOpen = 10;
+         $gapExtend = 1;
+         break;
+      case 10:
+         $gapOpen = 9;
+         $gapExtend = 1;
+         break;
+     }
+      break;
+   case 5:
+     $matrix ="BLOSUM45";
+     $gapKey = $form_state['values']['gapCost'];
+     switch ($gapKey) {
+      case 0:
+         $gapOpen = 13;
+         $gapExtend = 3;
+         break;
+      case 1:
+         $gapOpen = 12;
+         $gapExtend = 3;
+         break;
+      case 2:
+         $gapOpen = 11;
+         $gapExtend = 3;
+         break;
+      case 3:
+         $gapOpen = 10;
+         $gapExtend = 3;
+         break;
+      case 4:
+         $gapOpen = 15;
+         $gapExtend = 2;
+         break;
+      case 5:
+         $gapOpen = 14;
+         $gapExtend = 2;
+         break;
+      case 6:
+         $gapOpen = 13;
+         $gapExtend = 2;
+         break;
+      case 7:
+         $gapOpen = 12;
+         $gapExtend = 2;
+         break;
+      case 8:
+         $gapOpen = 19;
+         $gapExtend = 1;
+         break;
+      case 9:
+         $gapOpen = 18;
+         $gapExtend = 1;
+         break;
+      case 10:
+         $gapOpen = 17;
+         $gapExtend = 1;
+         break;
+      case 11:
+         $gapOpen = 16;
+         $gapExtend = 1;
+         break;
+     }
+     break;
+   case 6:
+     $matrix ="BLOSUM50";
+     $gapKey = $form_state['values']['gapCost'];
+     switch ($gapKey) {
+      case 0:
+         $gapOpen = 13;
+         $gapExtend = 3;
+         break;
+      case 1:
+         $gapOpen = 12;
+         $gapExtend = 3;
+         break;
+      case 2:
+         $gapOpen = 11;
+         $gapExtend = 3;
+         break;
+      case 3:
+         $gapOpen = 10;
+         $gapExtend = 3;
+         break;
+      case 4:
+         $gapOpen = 9;
+         $gapExtend = 3;
+         break;
+      case 5:
+         $gapOpen = 16;
+         $gapExtend = 2;
+         break;
+      case 6:
+         $gapOpen = 15;
+         $gapExtend = 2;
+         break;
+      case 7:
+         $gapOpen = 14;
+         $gapExtend = 2;
+         break;
+      case 8:
+         $gapOpen = 13;
+         $gapExtend = 2;
+         break;
+      case 9:
+         $gapOpen = 12;
+         $gapExtend = 2;
+         break;
+      case 10:
+         $gapOpen = 19;
+         $gapExtend = 1;
+         break;
+      case 11:
+         $gapOpen = 18;
+         $gapExtend = 1;
+         break;
+      case 12:
+         $gapOpen = 17;
+         $gapExtend = 1;
+         break;
+      case 13:
+         $gapOpen = 16;
+         $gapExtend = 1;
+         break;
+      case 14:
+         $gapOpen = 15;
+         $gapExtend = 1;
+         break;
+     }
+     break;
+   case 7:
+     $matrix ="BLOSUM90";
+     $gapKey = $form_state['values']['gapCost'];
+     switch ($gapKey) {
+      case 0:
+         $gapOpen = 9;
+         $gapExtend = 2;
+         break;
+      case 1:
+         $gapOpen = 8;
+         $gapExtend = 2;
+         break;
+      case 2:
+         $gapOpen = 7;
+         $gapExtend = 2;
+         break;
+      case 3:
+         $gapOpen = 6;
+         $gapExtend = 2;
+         break;
+      case 4:
+         $gapOpen = 11;
+         $gapExtend = 1;
+         break;
+      case 5:
+         $gapOpen = 10;
+         $gapExtend = 1;
+         break;
+      case 6:
+         $gapOpen = 9;
+         $gapExtend = 1;
+         break;
+     }
+     break;
+  }
+
+  return array(
+    'evalue' => $eVal,
+    'word_size' => $wordSize,
+    'gapopen' => $gapOpen,
+    'gapextend' => $gapExtend,
+    'matrix' => $matrix
+  );
+}
+
+/**
+ * Fill the first dropdown list with appropriate options
+ *
+ * @return
+ * An array consisting of matrices name for the first dropdown list
+ */
+function _ajax_example_get_first_dropdown_options() {
+  return drupal_map_assoc(array(
+  t('PAM30'),
+  t('PAM70'),
+  t('PAM250'),
+  t('BLOSUM80'),
+  t('BLOSUM62'),
+  t('BLOSUM45'),
+  t('BLOSUM50'),
+  t('BLOSUM90'),
+  ));
+}
+
+/**
+ * Fill the second dropdown list with appropriate options
+ *
+ * @return
+ * An array containing open and extension gap values for the chosen matrix (to fill the second dropdown list)
+ */
+function _ajax_example_get_second_dropdown_options($key = '') {
+  $options = array(
+    t('PAM30') => drupal_map_assoc(array(
+      t('Existence: 7 Extension: 2'),
+      t('Existence: 6 Extension: 2'),
+      t('Existence: 5 Extension: 2'),
+      t('Existence: 10 Extension: 1'),
+      t('Existence: 9 Extension: 1'),
+      t('Existence: 8 Extension: 1'),
+     )),
+    t('PAM70') => drupal_map_assoc(array(
+      t('Existence: 8 Extension: 2'),
+      t('Existence: 7 Extension: 2'),
+      t('Existence: 6 Extension: 2'),
+      t('Existence: 11 Extension: 1'),
+      t('Existence: 10 Extension: 1'),
+      t('Existence: 9 Extension: 1'),
+    )),
+    t('PAM250') => drupal_map_assoc(array(
+      t('Existence: 15 Extension: 3'),
+      t('Existence: 14 Extension: 3'),
+      t('Existence: 13 Extension: 3'),
+      t('Existence: 12 Extension: 3'),
+      t('Existence: 11 Extension: 3'),
+      t('Existence: 17 Extension: 2'),
+      t('Existence: 16 Extension: 2'),
+      t('Existence: 15 Extension: 2'),
+      t('Existence: 14 Extension: 2'),
+      t('Existence: 13 Extension: 2'),
+      t('Existence: 21 Extension: 1'),
+      t('Existence: 20 Extension: 1'),
+      t('Existence: 19 Extension: 1'),
+      t('Existence: 18 Extension: 1'),
+      t('Existence: 17 Extension: 1'),
+    )),
+    t('BLOSUM80') => drupal_map_assoc(array(
+      t('Existence: 8 Extension: 2'),
+      t('Existence: 7 Extension: 2'),
+      t('Existence: 6 Extension: 2'),
+      t('Existence: 11 Extension: 1'),
+      t('Existence: 10 Extension: 1'),
+      t('Existence: 9 Extension: 1'),
+    )),
+    t('BLOSUM62') => drupal_map_assoc(array(
+      t('Existence: 11 Extension: 2'),
+      t('Existence: 10 Extension: 2'),
+      t('Existence: 9 Extension: 2'),
+      t('Existence: 8 Extension: 2'),
+      t('Existence: 7 Extension: 2'),
+      t('Existence: 6 Extension: 2'),
+      t('Existence: 13 Extension: 1'),
+      t('Existence: 12 Extension: 1'),
+      t('Existence: 11 Extension: 1'),
+      t('Existence: 10 Extension: 1'),
+      t('Existence: 9 Extension: 1'),
+    )),
+    t('BLOSUM45') => drupal_map_assoc(array(
+      t('Existence: 13 Extension: 3'),
+      t('Existence: 12 Extension: 3'),
+      t('Existence: 11 Extension: 3'),
+      t('Existence: 10 Extension: 3'),
+      t('Existence: 15 Extension: 2'),
+      t('Existence: 14 Extension: 2'),
+      t('Existence: 13 Extension: 2'),
+      t('Existence: 12 Extension: 2'),
+      t('Existence: 19 Extension: 1'),
+      t('Existence: 18 Extension: 1'),
+      t('Existence: 17 Extension: 1'),
+      t('Existence: 16 Extension: 1'),
+    )),
+    t('BLOSUM50') => drupal_map_assoc(array(
+      t('Existence: 13 Extension: 3'),
+      t('Existence: 12 Extension: 3'),
+      t('Existence: 11 Extension: 3'),
+      t('Existence: 10 Extension: 3'),
+      t('Existence: 9 Extension: 3'),
+      t('Existence: 16 Extension: 2'),
+      t('Existence: 15 Extension: 2'),
+      t('Existence: 14 Extension: 2'),
+      t('Existence: 13 Extension: 2'),
+      t('Existence: 12 Extension: 2'),
+      t('Existence: 19 Extension: 1'),
+      t('Existence: 18 Extension: 1'),
+      t('Existence: 17 Extension: 1'),
+      t('Existence: 16 Extension: 1'),
+      t('Existence: 15 Extension: 1'),
+    )),
+    t('BLOSUM90') => drupal_map_assoc(array(
+      t('Existence: 9 Extension: 2'),
+      t('Existence: 8 Extension: 2'),
+      t('Existence: 7 Extension: 2'),
+      t('Existence: 6 Extension: 2'),
+      t('Existence: 11 Extension: 1'),
+      t('Existence: 10 Extension: 1'),
+      t('Existence: 9 Extension: 1'),
+    )),
+    );
+    if (isset($options[$key])) {
+    	return $options[$key];
+    } else {
+    	return array();
+    }
+}
+
+/**
+ * Respond to Ajax dropdown call
+ */
+function ajax_example_dependent_dropdown_callback($form, $form_state) {
+  return $form['ALG']['SParam']['gapCost'];
+}
+
+/**
+ * @section
+ * tBLASTn: Search translated nucleotide database using a protein query.
+ * ----------------------------------------------------------------------------
+ */
+
+/**
+ * Adds the tBLASTn Advanced Options to the passed in form.
+ *
+ * This form function is meant to be called within another form definition.
+ *
+ * @param $form
+ *   The form the advanced options should be added to. This form already
+ *   contains a $form['ALG'] fieldset meant to contain the advanced options.
+ * @param $formstate
+ *   The current state fo the form passed in as $form.
+ */
+function blast_ui_tblastn_advanced_options_form(&$form, $form_state) { }
+
+/**
+ * Validate the advanced options provided by the BLASTn form above.
+ *
+ * @see blast_ui_tblastn_advanced_options_form().
+ */
+function blast_ui_tblastn_advanced_options_form_validate($form, $form_state) { }
+
+/**
+ * Processed the advanced options provided by the yBLASTn form above.
+ *
+ * @see blast_ui_tblastn_advanced_options_form().
+ */
+function blast_ui_tblastn_advanced_options_form_submit($form, $form_state) { }

+ 6 - 0
includes/blast_ui.form_common.inc

@@ -0,0 +1,6 @@
+<?php
+
+/**
+ * @file
+ * Contains functions common to all BLAST submission forms.
+ */

+ 398 - 0
includes/blast_ui.form_per_program.inc

@@ -0,0 +1,398 @@
+<?php
+
+/**
+ * @file
+ * Forms in the file provide a per BLAST program interface to submitting BLASTs.
+ *
+ * In other words, it provides a form for blastn, one for blastp, etc. It does
+ * this using a single form function for code reusability and depending upon
+ * the $type passed in, it will execute additional hooks allowing for program
+ * specific modifications & advanced options.
+ */
+
+/**
+ * This single form definition provides 4 different program-specific forms.
+ */
+function blast_ui_per_blast_program_form($form, $form_state) {
+
+	//  CSS support to the form
+	$form['#attached']['css'] = array(
+		drupal_get_path('module', 'blast_ui') . '/theme/css/form.css',
+	);
+
+  // Determine the BLAST program.
+  $query_type = $form_state['build_info']['args'][0];
+  $db_type = $form_state['build_info']['args'][1];
+  if ($query_type == 'nucleotide') {
+    if ($db_type == 'nucleotide') {
+      $blast_program = 'blastn';
+    }
+    elseif ($db_type == 'protein') {
+      $blast_program = 'blastx';
+    }
+  }
+  elseif ($query_type == 'protein') {
+    if ($db_type == 'nucleotide') {
+      $blast_program = 'tblastn';
+    }
+    elseif ($db_type == 'protein') {
+      $blast_program = 'blastp';
+    }
+  }
+
+  // Set the title to be more Researcher friendly.
+  drupal_set_title(t(
+    '@query to @db BLAST (@program)',
+    array(
+      '@query' => ucfirst($query_type),
+      '@db' => ucfirst($db_type),
+      '@program' => $blast_program
+    )
+  ));
+
+  // Add the details about the specific BLAST choosen.
+  $form['query_type'] = array(
+    '#type' => 'hidden',
+    '#value' => $query_type
+  );
+  $form['db_type'] = array(
+    '#type' => 'hidden',
+    '#value' => $db_type
+  );
+  $form['blast_program'] = array(
+    '#type' => 'hidden',
+    '#value' => $blast_program
+  );
+
+  // NUCLEOTIDE QUERY
+  //.........................
+
+  $form['query'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Enter %type Query Sequence',
+      array('%type' => ucfirst($query_type))),
+    '#description' => t('Enter one or more queries in the top text box or use '
+      . 'the browse button to upload a file from your local disk. The file may '
+      . 'contain a single sequence or a list of sequences. In both cases, the '
+      . 'data must be in <a href="@formaturl" target="_blank">FASTA format</a>.',
+        array(
+          '@formaturl' => 'http://www.ncbi.nlm.nih.gov/BLAST/blastcgihelp.shtml'
+        )
+      ),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+  );
+
+  // Checkbox to show an example.
+  $form['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',
+    	'wrapper'  => 'fasta-textarea',
+      'method'   => 'replace',
+      'effect'   => 'fade',
+    ),
+  );
+
+  // Textfield for submitting a mult-FASTA query
+  $form['query']['FASTA'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Enter FASTA sequence(s)'),
+    '#description'=>t('Enter query sequence(s) in the text area.'),
+    '#prefix' => '<div id="fasta-textarea">',
+    '#suffix' => '</div>',
+  );
+
+  // 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) 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'),
+      'file_validate_size' => array(file_upload_max_size()),
+    ),
+  );
+
+
+  // BLAST DATABASE
+  //.........................
+
+  $form['DB'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Choose Search Set'),
+    '#description' => t('Choose from one of the %type BLAST databases listed '
+      . 'below. You can also use the browse button to upload a file from your '
+      . 'local disk. The file may contain a single sequence or a list of '
+      . 'sequences. ',
+      array('%type' => $db_type)),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+  );
+
+  $options = get_blast_database_options($db_type);
+  $form['DB']['SELECT_DB'] = array(
+    '#type' => 'select',
+    '#title' => t('%type BLAST Databases:', array('%type' => ucfirst($db_type))),
+    '#options' => $options,
+    '#default_value' => 0,
+  );
+
+  // Upload a file as an alternative to selecting an existing BLAST database
+  $form['#attributes']['enctype'] = 'multipart/form-data';
+  $form['DB']['DBUPLOAD'] = array(
+    '#title' => 'Or upload your own dataset:	',
+    '#type' => 'managed_file',
+    '#description' => t('The file should be a plain-text FASTA
+(.fasta, .fna, .fa) 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'),
+      'file_validate_size' => array(file_upload_max_size()),
+    ),
+  );
+
+  // Advanced Options
+  //.........................
+  // These options will be different depending upon the program selected.
+  // Therefore, allow for program-specific callbacks to populate these options.
+
+  $form['ALG'] = array(
+   '#type' => 'fieldset',
+   '#title' => t('Advanced Options'),
+   '#collapsible' => TRUE,
+   '#collapsed' => TRUE,
+  );
+
+  $advanced_options_form = 'blast_ui_' . $blast_program . '_advanced_options_form';
+  if (function_exists($advanced_options_form)) {
+    call_user_func_array($advanced_options_form, array(&$form, $form_state));
+  }
+
+  // Submit
+  //.........................
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#default_value' => ' BLAST ',
+  );
+
+  return $form;
+}
+
+/**
+ * Validate the user options submitted via the above form.
+ *
+ * @see blast_ui_per_blast_program_form().
+ */
+function blast_ui_per_blast_program_form_validate($form, &$form_state) {
+
+
+  $blast_program = $form_state['values']['blast_program'];
+
+  $type = $form_state['values']['query_type'];
+  if ($type == 'nucleotide') {
+    $molecule_type = 'nucleotides';
+  }
+  else {
+    $molecule_type = 'amino acid residues';
+  }
+
+  // Validate Query
+  //----------------
+  // @todo: We are currently not validating uploaded files are valid FASTA.
+  // First check to see if we have an upload & if so then validate it.
+  $file = file_load($form_state['values']['UPLOAD']);
+  // If the $file is populated then this is a newly uploaded, temporary file.
+  if (is_object($file)) {
+    $form_state['qFlag'] = 'upQuery';
+    $form_state['upQuery_path'] = drupal_realpath($file->uri);
+  }
+  // Otherwise there was no file uploaded.
+  // Check if there was a query sequence entered in the texfield.
+  elseif (!empty($form_state['input']['FASTA'])) {
+    // Check to ensure that the query sequence entered is valid FASTA.
+    if (validate_fasta_sequence($type, $form_state['input']['FASTA'])){
+      form_set_error('query', t('You need to provide a valid FASTA sequence '
+      . 'for the query. It should contain a FASTA header/definition line '
+      . 'followed by %molecule-type. For more information see the '
+      . '<a href="@url" target="_blank">NCBI FASTA Specification</a>.',
+        array(
+          '%molecule-type' => $molecule_type,
+          '@url' => 'http://www.ncbi.nlm.nih.gov/BLAST/blastcgihelp.shtml'
+      )));
+    }
+    else {
+      $form_state['qFlag'] = 'seqQuery';
+    }
+  }
+  // Otherwise they didn't enter a query!!
+  else {
+    form_set_error('query', t('No query sequence given. Only raw sequence or '
+    . 'sequence of type FASTA can be read. Enter sequence in the box provided '
+    . 'or upload a plain text file.'));
+  }
+
+  // Validate Database
+  //-------------------
+  // @todo: We are currently not validating uploaded files are valid FASTA.
+  // First check to see if we have an upload & if so then validate it.
+  $file = file_load($form_state['values']['DBUPLOAD']);
+  // If the $file is populated then this is a newly uploaded, temporary file.
+  if (is_object($file)) {
+    $form_state['dbFlag'] = 'upDB';
+    $form_state['upDB_path'] = drupal_realpath($file->uri);
+  }
+  // Otherwise there was no file uploaded
+  // Check if there was a database choosen from the list instead
+  elseif (!empty($form_state['values']['SELECT_DB'])) {
+    $form_state['dbFlag'] = 'blastdb';
+  }
+  // Otherwise they didn't select a database!!
+  else {
+    form_set_error('DB', t('No database selected. Either choose a database '
+    . 'from the list or upload one of your own.'));
+  }
+
+  // Validate Advanced Options
+  //---------------------------
+  $advanced_options_form_validate = 'blast_ui_' . $blast_program . '_advanced_options_form_validate';
+  if (function_exists($advanced_options_form_validate)) {
+    call_user_func_array(
+      $advanced_options_form_validate,
+      array(&$form, $form_state)
+    );
+  }
+
+}
+
+/**
+ * Process the user options submitted via the blast program form.
+ *
+ * @see blast_ui_per_blast_program_form().
+ */
+function blast_ui_per_blast_program_form_submit($form, &$form_state) {
+
+  $error = FALSE;
+  $blast_program = $form_state['values']['blast_program'];
+
+    if ($form_state['values']['db_type'] == 'nucleotide') {
+      $mdb_type = 'nucl';
+      $file_suffix = '.nsq';
+    }
+    else {
+      $mdb_type = 'prot';
+      $file_suffix = '.psq';
+    }
+
+  // If the query was submitted via the texrfield then create a file containing it
+  if ( isset($form_state['qFlag']) ) {
+    if ( $form_state['qFlag'] == 'seqQuery' ) {
+      $seq_content = $form_state['values']['FASTA'];
+	    $query = '/tmp/' . date('YMd_His') . '_query.fasta';
+      file_put_contents ( $query , $seq_content);
+    }
+    elseif ( $form_state['qFlag'] == 'upQuery' ) {
+      $query = $form_state['upQuery_path'];
+    }
+  }
+
+  // If the BLAST database was uploaded then use it to run the BLAST
+  if ( $form_state['dbFlag'] == 'upDB') {
+
+    // Since we only support using the -db flag (not -subject) we need to create a
+    // blast database for the FASTA uploaded.
+    // NOTE: We can't support subject because we need to generate the ASN.1+ format
+    // to provide multiple download type options from the same BLAST
+    $blastdb_with_path = $form_state['upDB_path'];
+    $result = NULL;
+    exec("makeblastdb -in $blastdb_with_path -dbtype $mdb_type -parse_seqids 2>&1", $result);
+
+    // Check that the BLAST database was made correctly.
+    $result = implode('<br />', $result);
+    if (preg_match('/Error/', $result)) {
+      drupal_set_message('Unable to generate a BLAST database from your uploaded
+FASTA sequence. Please check that your file is a valid FASTA file and that if
+your sequence headers include pipes (i.e.: | ) they adhere to '
+. l('NCBI standards.', 'http://www.ncbi.nlm.nih.gov/books/NBK21097/table/A632/?report=objectonly', array('attributes' => array('target' => '_blank'))),
+        'error'
+      );
+
+      $error = TRUE;
+    }
+
+  }
+  // Otherwise, we are using one of the website provided BLAST databases so form the
+  // BLAST command accordingly
+  elseif ($form_state['dbFlag'] == 'blastdb') {
+
+    $selected_db = $form_state['values']['SELECT_DB'];
+    $blastdb_node = node_load($selected_db);
+    $blastdb_with_path = $blastdb_node->db_path;
+
+  }
+
+  // Now let each program process it's own advanced options.
+  $advanced_options = array();
+  $advanced_options_form_submit = 'blast_ui_' . $blast_program . '_advanced_options_form_submit';
+  if (function_exists($advanced_options_form_submit)) {
+    $advanced_options = call_user_func_array(
+      $advanced_options_form_submit,
+      array($form, &$form_state)
+    );
+  }
+
+  // Actually submit the BLAST Tripal Job
+  // NOTE: Tripal jobs needs to be executed from the command-line before it will be run!!
+  $blastdb_with_suffix = $blastdb_with_path . $file_suffix;
+  if (is_readable($blastdb_with_suffix)) {
+    global $user;
+    $output_filestub = date('YMd_His');
+    $job_args = array(
+      'program' => $blast_program,
+      'query' => $query,
+      'database' => $blastdb_with_path,
+      'output_filename' => $output_filestub,
+      'options' => $advanced_options
+    );
+    $job_id = tripal_add_job(
+      t('BLAST (@program): @query', array('@program' => $blast_program, '@query' => $query)),
+      'blast_job',
+      'run_BLAST_tripal_job',
+      $job_args,
+      $user->uid
+    );
+
+    // Redirect to the BLAST results page
+    drupal_goto("blast/report/$job_id");
+  }
+  // We check if $error is set to TRUE because if so then the error has already
+  // been reported.
+  elseif (!$error) {
+    $dbfile_uploaded_msg = ($form_state['dbFlag'] == 'upDB') ? 'The BLAST database was submitted via user upload.' : 'Existing BLAST Database was chosen';
+    tripal_report_error(
+      'blast_ui',
+      TRIPAL_ERROR,
+      "BLAST database %db unaccessible. $dbfile_uploaded_msg",
+      array('%db' => $blastdb_with_path)
+    );
+    drupal_set_message('BLAST database unaccessible. Please contact the site administrator.','error');
+  }
+}

+ 45 - 0
includes/blast_ui.form_per_query_type.inc

@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ *
+ */
+
+/**
+ *
+ */
+function blast_ui_per_query_type_form($form, $form_state) {
+
+  // Determine the BLAST program.
+  $query_type = $form_state['build_info']['args'][0];
+  if ($query_type == 'nucleotide') {
+    $blast_program = array(
+      'blastn',
+      'blastx'
+    );
+  }
+  elseif ($query_type == 'protein') {
+    $blast_program = array(
+      'blastp',
+      'tblastn'
+    );
+  }
+
+  // Add the details about the specific BLAST choosen.
+  $form['query_type'] = array(
+    '#type' => 'hidden',
+    '#value' => $query_type
+  );
+  $form['blast_program'] = array(
+    '#type' => 'hidden',
+    '#value' => implode(', ', $blast_program)
+  );
+
+  // Give the users a message.
+  $form['msg'] = array(
+    '#type' => 'item',
+    '#markup' => 'COMMING SOON!'
+  );
+
+  return $form;
+}

+ 21 - 0
includes/blast_ui.form_single.inc

@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ *
+ */
+
+/**
+ *
+ */
+function blast_ui_all_in_one_form($form, $form_state) {
+
+  // Give the users a message so they don't realize this was supposed to be a
+  // form that just isn't ready yet.
+  $form['msg'] = array(
+    '#type' => 'item',
+    '#markup' => 'COMMING SOON!'
+  );
+
+  return $form;
+}

+ 4 - 4
theme/blast_report.tpl.php

@@ -75,17 +75,17 @@ if ($xml) {
 	  // to makeblastdb did a really poor job. In thhis case we want to use
 	  // the def to provide the original FASTA header.
 	  $hit_name = (preg_match('/BL_ORD_ID/', $hit->{'Hit_id'})) ? $hit->{'Hit_def'} : $hit->{'Hit_id'};
-       
+
           $score = $hit->{'Hit_hsps'}->{'Hsp'}->{'Hsp_score'};
 	  $evalue = $hit->{'Hit_hsps'}->{'Hsp'}->{'Hsp_evalue'};
 	  $query_name = $iteration->{'Iteration_query-def'};
-          
+
 	  $row = array(
 	  		'data' => array(
 			'number' => array('data' => $count, 'class' => array('number')),
 			'query' => array('data' => $query_name, 'class' => array('query')),
 			'hit' => array('data' => $hit_name, 'class' => array('hit')),
-			evalue' => array('data' => $evalue, 'class' => array('evalue')),
+			'evalue' => array('data' => $evalue, 'class' => array('evalue')),
 			'arrow-col' => array('data' => '<div class="arrow"></div>', 'class' => array('arrow-col'))
 		      ),
 		     'class' => array('result-summary')
@@ -127,7 +127,7 @@ if ($xml) {
                 'class' => array('result-summary')
             );
         $rows[] = $row;
-		} // end of else		
+		} // end of else
   }
   print theme('table', array(
       'header' => $header,

+ 1 - 1
theme/blast_ui.theme.inc

@@ -18,7 +18,7 @@ function blast_ui_preprocess_show_blast_report(&$vars) {
 
   // Add CSS and Javascript files
   $path = drupal_get_path('module', 'blast_ui');
-  drupal_add_css($path . '/theme/blast_report.css');
+  drupal_add_css($path . '/theme/css/blast_report.css');
   drupal_add_js('http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js');
 
   // Get the filename of the BLAST results

+ 1 - 1
theme/blast_report.css → theme/css/blast_report.css

@@ -22,7 +22,7 @@
   cursor:pointer;
 }
 #blast_report .arrow {
-  background:transparent url(arrows.png) no-repeat scroll 0px -16px;
+  background:transparent url('../images/arrows.png') no-repeat scroll 0px -16px;
   width:16px;
   height:16px;
   display:block;

+ 0 - 0
css/form.css → theme/css/form.css


+ 0 - 0
theme/arrows.png → theme/images/arrows.png


+ 0 - 0
theme/jExpand.js → theme/js/jExpand.js