Browse Source

Fixed sequence extraction and display for drush command and on feature pages

Stephen Ficklin 11 years ago
parent
commit
292c77714f

+ 0 - 62
tripal_feature/api/tripal_feature.DEPRECATED.inc

@@ -373,46 +373,6 @@ function tripal_feature_reverse_complement($sequence) {
   return tripal_reverse_compliment_sequence($sequence);
 }
 
-/**
- * @deprecated Restructured API to make naming more readable and consistent.
- * Function was deprecated in Tripal 2.0 and will be removed 2 releases from now.
- * This function has been replaced by tripal_get_sequence().
- *
- * @see tripal_get_sequence().
- */
-function tripal_feature_get_formatted_sequence($feature_id, $feature_name, $num_bases_per_line, $derive_from_parent, $aggregate, $output_format, $upstream, $downstream, $sub_features = array(), $relationship = '', $rel_part = '') {
-
-  tripal_report_error(
-    'tripal_deprecated',
-    TRIPAL_NOTICE,
-    "DEPRECATED: %old_function has been replaced with %new_function. Please update your code.",
-    array(
-      '%old_function'=>'tripal_feature_get_formatted_sequence',
-      '%new_function' => 'tripal_get_sequence'
-    )
-  );
-
-  return tripal_get_sequence(
-    // Feature
-    array(
-      'feature_id' => $feature_id,
-      'name' => $feature_name
-    ),
-    // Options
-    array(
-      'width' => $num_bases_per_line,
-      'derive_from_parent' => $derive_from_parent,
-      'aggregate' => $aggregate,
-      'output_format' => $output_format,
-      'num_upstream' => $upstream,
-      'num_downstream' => $downstream,
-      'sub_feature_types' => $sub_features,
-      'relationship_type' => $relationship,
-      'relationship_part' => $rel_part
-    )
-  );
-}
-
 /**
  * @deprecated Restructured API to make naming more readable and consistent.
  * Function was deprecated in Tripal 2.0 and will be removed 2 releases from now.
@@ -470,25 +430,3 @@ function tripal_feature_add_cvterm($feature_id, $cvname, $cvterm) {
     )
   );
 }
-
-/**
- * @deprecated Restructured API to make naming more readable and consistent.
- * Function was deprecated in Tripal 2.0 and will be removed 2 releases from now.
- * This function has been replaced by tripal_format_fasta_sequence().
- *
- * @see tripal_format_fasta_sequence().
- */
-function tripal_feature_return_fasta($feature, $desc) {
-
-  tripal_report_error(
-    'tripal_deprecated',
-    TRIPAL_NOTICE,
-    "DEPRECATED: %old_function has been replaced with %new_function. Please update your code.",
-    array(
-      '%old_function'=>'tripal_feature_return_fasta',
-      '%new_function' => 'tripal_format_fasta_sequence'
-    )
-  );
-
-  return tripal_format_fasta_sequence($feature, $desc);
-}

+ 313 - 107
tripal_feature/api/tripal_feature.api.inc

@@ -45,7 +45,8 @@ function tripal_reverse_compliment_sequence($sequence) {
 }
 
 /**
- * Retrieves the sequence for a feature.
+ * Retrieves the sequences for a given feature. If a feature has multiple alignments
+ *  or multiple relationships then multiple sequences will be returned.
  *
  * @param $feature
  *   An associative array describing the feature. Valid keys include:
@@ -59,19 +60,15 @@ function tripal_reverse_compliment_sequence($sequence) {
  *   An associative array of options. Valid keys include:
  *    - width: Indicate the number of bases to use per line.  A new line will be added
  *        after the specified number of bases on each line.
+ *    - is_html: Set to '1' if  the sequence is meant to be displayed on a web page.
+ *        This will cause a <br> tag to separate lines of the FASTA sequence. 
  *    - derive_from_parent: Set to '1' if the sequence should be obtained from the parent
  *        to which this feature is aligned.
  *    - aggregate: Set to '1' if the sequence should only contain sub features, excluding
  *        intro sub feature sequence.  For example, set this option to obtain just
  *        the coding sequence of an mRNA.
- *    - output_format: The type of format.  Valid formats include 'fasta_html', 'fasta_txt' and
- *        'raw'.  The format 'fasta_txt' outputs line breaks as <br> tags and the entire
- *        return value is in a <span> tag with a fixed-width font definition.  'fasta_txt'
- *        outputs line breaks with windows format carriage returns (e.g. \r\n) with no other
- *        formatting. The raw format is simply the sequence with no FASTA formatting and no
- *        line breaks.
- *    - num_upstream: An integer specifing the number of upstream bases to include in the output
- *    - num_downstream: An integer specifying the number of downstream bases to include in the
+ *    - upstream: An integer specifing the number of upstream bases to include in the output
+ *    - downstream: An integer specifying the number of downstream bases to include in the
  *        output.
  *    - sub_feature_types: Only include sub features (or child features) of the types
  *        provided in the array
@@ -82,33 +79,39 @@ function tripal_reverse_compliment_sequence($sequence) {
  *        relationship the matched features belong
  *
  * @return
- *   an array of matching sequence formated as requested.
+ *   an array of matching sequence in the following keys for each sequence:
+ *      'types'         => an array of feature types that were used to derive the sequence (e.g. from an aggregated sequence)
+ *      'upstream'      => the number of upstream bases included in the sequence
+ *      'downstream'    => the number of downstream bases included in the sequence
+ *      'defline'       => the definintion line used to create a FASTA sequence
+ *      'residues'      => the residues
+ *      'featureloc_id' => the featureloc_id if the sequences is from an alignment
  *
  * @ingroup tripal_feature_api
  */
-function tripal_get_sequence($feature, $options) {
+function tripal_get_feature_sequences($feature, $options) {
 
   // default values for finding the feature
-  $feature_id    = array_key_exists('feature_id', $feature)     ? $feature['feature_id']     : 0;
-  $parent_id     = array_key_exists('parent_id', $feature)      ? $feature['parent_id']      : 0;
-  $featureloc_id = array_key_exists('featureloc_id', $feature)  ? $feature['featureloc_id']  : 0;
-  $feature_name  = array_key_exists('name', $feature)           ? $feature['name']           : '';
+  $feature_id         = array_key_exists('feature_id', $feature)     ? $feature['feature_id']     : 0;
+  $parent_id          = array_key_exists('parent_id', $feature)      ? $feature['parent_id']      : 0;
+  $featureloc_id      = array_key_exists('featureloc_id', $feature)  ? $feature['featureloc_id']  : 0;
+  $feature_name       = array_key_exists('name', $feature)           ? $feature['name']           : '';
 
   // default values for building the sequence
   $num_bases_per_line = array_key_exists('width', $options)              ? $options['width']              : 50;
   $derive_from_parent = array_key_exists('derive_from_parent', $options) ? $options['derive_from_parent'] : 0;
   $aggregate          = array_key_exists('aggregate', $options)          ? $options['aggregate']          : 0;
-  $output_format      = array_key_exists('output_format', $options)      ? $options['output_format']      : 'fasta_txt';
-  $upstream           = array_key_exists('num_upstream', $options)       ? $options['num_upstream']       : 0;
-  $downstream         = array_key_exists('num_downstream', $options)     ? $options['num_downstream']     : 0;
+  $upstream           = array_key_exists('upstream', $options)           ? $options['upstream']           : 0;
+  $downstream         = array_key_exists('downstream', $options)         ? $options['downstream']         : 0;
   $sub_features       = array_key_exists('sub_feature_types', $options)  ? $options['sub_feature_types']  : array();
   $relationship       = array_key_exists('relationship_type', $options)  ? $options['relationship_type']  : '';
   $rel_part           = array_key_exists('relationship_part', $options)  ? $options['relationship_part']  : '';
+  $is_html            = array_key_exists('is_html', $options)            ? $options['is_html']            : 0;
   
   // make sure the sub_features variable is an array
   if (!is_array($sub_features)) {
-    tripal_report_error('tripal_deprecated', TRIPAL_ERROR,
-      "'sub_features' option must be an array for function tripal_get_sequence().",
+    tripal_report_error('tripal_feature', TRIPAL_ERROR,
+      "'sub_features' option must be an array for function tripal_get_feature_sequences().",
       array()
     );
     return array();
@@ -121,12 +124,12 @@ function tripal_get_sequence($feature, $options) {
     if ($rel_part == "subject") {
       $sql = '
         SELECT FO.feature_id, FO.name, FO.uniquename, CVTO.name as feature_type, O.genus, O.species
-        FROM feature FS
-          INNER JOIN feature_relationship FR ON FR.subject_id   = FS.feature_id
-          INNER JOIN cvterm CVTFR            ON CVTFR.cvterm_id = FR.type_id
-          INNER JOIN feature FO              ON FO.feature_id   = FR.object_id
-          INNER JOIN cvterm CVTO             ON CVTO.cvterm_id  = FO.type_id
-          INNER JOIN organism O              ON O.organism_id   = FO.organism_id
+        FROM {feature} FS
+          INNER JOIN {feature_relationship} FR ON FR.subject_id   = FS.feature_id
+          INNER JOIN {cvterm} CVTFR            ON CVTFR.cvterm_id = FR.type_id
+          INNER JOIN {feature} FO              ON FO.feature_id   = FR.object_id
+          INNER JOIN {cvterm} CVTO             ON CVTO.cvterm_id  = FO.type_id
+          INNER JOIN {organism} O              ON O.organism_id   = FO.organism_id
         WHERE
           FS.feature_id = :feature_id AND
           CVTFR.name    = :relationship
@@ -136,12 +139,12 @@ function tripal_get_sequence($feature, $options) {
     if ($rel_part == "object") {
       $sql = '
         SELECT FS.feature_id, FS.name, FS.uniquename, CVTO.name as feature_type, O.genus, O.species
-        FROM feature FO
-          INNER JOIN feature_relationship FR ON FR.object_id    = FO.feature_id
-          INNER JOIN cvterm CVTFR            ON CVTFR.cvterm_id = FR.type_id
-          INNER JOIN feature FS              ON FS.feature_id   = FR.subject_id
-          INNER JOIN cvterm CVTO             ON CVTO.cvterm_id  = FS.type_id
-          INNER JOIN organism O              ON O.organism_id   = FS.organism_id
+        FROM {feature} FO
+          INNER JOIN {feature_relationship} FR ON FR.object_id    = FO.feature_id
+          INNER JOIN {cvterm} CVTFR            ON CVTFR.cvterm_id = FR.type_id
+          INNER JOIN {feature} FS              ON FS.feature_id   = FR.subject_id
+          INNER JOIN {cvterm} CVTO             ON CVTO.cvterm_id  = FS.type_id
+          INNER JOIN {organism} O              ON O.organism_id   = FS.organism_id
         WHERE
           FO.feature_id = :feature_id AND
           CVTFR.name    = :relationship
@@ -158,7 +161,7 @@ function tripal_get_sequence($feature, $options) {
       if ($rel_part == "object") {
         $defline = "$feature->uniquename $feature->feature_type ($feature->genus $feature->species), $relationship, $feature_name";
       }
-      return tripal_get_sequence(
+      return tripal_get_feature_sequences(
         array(
           'feature_id' => $feature->feature_id, 
           'name' => $defline,
@@ -166,9 +169,8 @@ function tripal_get_sequence($feature, $options) {
         ),
         array(
           'width' => $num_bases_per_line, 
-          'derive_from_pareht' => $derive_from_parent, 
+          'derive_from_parent' => $derive_from_parent, 
           'aggregate' => $aggregate, 
-          'output_format' => $output_format,
           'upstream' => $upstream, 
           'downstream' => $downstream, 
           'sub_features' => $sub_features, 
@@ -182,7 +184,7 @@ function tripal_get_sequence($feature, $options) {
   // cases cases where the alignment is in the reverse direction and when
   // the upstream and downstream extensions go beyond the lenght of the
   // parent sequence.
-  $sql ='
+  $parent_sql ='
     SELECT featureloc_id, srcname, srcfeature_id, strand, srctypename, typename,
       fmin, fmax, upstream, downstream, adjfmin, adjfmax,
       substring(residues from (adjfmin + 1) for (upstream + (fmax - fmin) + downstream))  as residues,
@@ -255,10 +257,10 @@ function tripal_get_sequence($feature, $options) {
   $sfsql = '
     SELECT SF.feature_id, CVT.name as type_name, SF.type_id
     FROM {feature_relationship} FR
-      INNER JOIN {feature} SF on SF.feature_id = FR.subject_id
-      INNER JOIN {cvterm} CVT on CVT.cvterm_id = SF.type_id
-      INNER JOIN {featureloc} FL on FL.feature_id = FR.subject_id
-      INNER JOIN {feature} PF on PF.feature_id = FL.srcfeature_id
+      INNER JOIN {feature} SF    ON SF.feature_id = FR.subject_id
+      INNER JOIN {cvterm} CVT    ON CVT.cvterm_id = SF.type_id
+      INNER JOIN {featureloc} FL ON FL.feature_id = FR.subject_id
+      INNER JOIN {feature} PF    ON PF.feature_id = FL.srcfeature_id
     WHERE FR.object_id = :feature_id and PF.feature_id = :srcfeature_id
     ORDER BY FL.fmin ASC
   ';
@@ -266,10 +268,10 @@ function tripal_get_sequence($feature, $options) {
   $fsql ='
     SELECT count(*) as num_children
     FROM {feature_relationship} FR
-      INNER JOIN {feature} SF on SF.feature_id = FR.subject_id
-      INNER JOIN {cvterm} CVT on CVT.cvterm_id = SF.type_id
-      INNER JOIN {featureloc} FL on FL.feature_id = FR.subject_id
-      INNER JOIN {feature} PF on PF.feature_id = FL.srcfeature_id
+      INNER JOIN {feature} SF    ON SF.feature_id = FR.subject_id
+      INNER JOIN {cvterm} CVT    ON CVT.cvterm_id = SF.type_id
+      INNER JOIN {featureloc} FL ON FL.feature_id = FR.subject_id
+      INNER JOIN {feature} PF    ON PF.feature_id = FL.srcfeature_id
     WHERE FR.object_id = :feature_id and PF.feature_id = :srcfeature_id
   ';
 
@@ -280,8 +282,7 @@ function tripal_get_sequence($feature, $options) {
   if ($derive_from_parent) {
 
     // execute the query to get the sequence from the parent
-    $parents = chado_query($sql, array(':upstream' => $upstream, ':downstream' => $downstream, ':feature_id' => $feature_id));
-
+    $parents = chado_query($parent_sql, array(':upstream' => $upstream, ':downstream' => $downstream, ':feature_id' => $feature_id));
     while ($parent = $parents->fetchObject()) {
       
       // if the user specified a particular parent and this one doesn't match then skip it
@@ -293,6 +294,7 @@ function tripal_get_sequence($feature, $options) {
         continue;
       }
       $seq = '';  // initialize the sequence for each parent
+      $notes = '';
 
       // if we are to aggregate then we will ignore the feature returned
       // by the query above and rebuild it using the sub features
@@ -300,7 +302,7 @@ function tripal_get_sequence($feature, $options) {
 
         // now get the sub features that are located on the parent.
         $children = chado_query($sfsql, array(':feature_id' => $feature_id, ':srcfeature_id' => $parent->srcfeature_id));
-        $num_children = chado_query($fsql, array(':feature_id' => $feature_id, ':srcfeature_id' => $parent->srcfeature_id))->fetchObject();
+        $num_children = chado_query($fsql, array(':feature_id' => $feature_id, ':srcfeature_id' => $parent->srcfeature_id))->fetchField();
 
         // iterate through the sub features and concat their sequences. They
         // should already be in order.
@@ -311,6 +313,7 @@ function tripal_get_sequence($feature, $options) {
           // included then continue if this child is not one of those allowed
           // subfeatures
           if (count($sub_features) > 0 and !in_array($child->type_name, $sub_features)) {
+            $i++;
              continue;
           }
 
@@ -325,40 +328,44 @@ function tripal_get_sequence($feature, $options) {
             // -------------------------- ref
             //    ....---->  ---->
             //     up    1       2
-            $q = chado_query($sql, array(':upstream' => $upstream, ':downstream' => 0, ':feature_id' => $child->feature_id));
+            $q = chado_query($parent_sql, array(':upstream' => $upstream, ':downstream' => 0, ':feature_id' => $child->feature_id));
           }
           elseif ($i == 0 and $parent->strand < 0) { // reverse direction
             // -------------------------- ref
             //    ....<----  <----
             //    down  1       2
-            $q = chado_query($sql, array(':upstream' => 0, ':downstream' => $downstream, ':feature_id' => $child->feature_id));
+            $q = chado_query($parent_sql, array(':upstream' => 0, ':downstream' => $downstream, ':feature_id' => $child->feature_id));
           }
 
           // Next, if the last sub feature we need to include the downstream bases. first check if
           // the feature is in teh forward direction or the reverse
-          if ($i == $num_children->num_children - 1 and $parent->strand >= 0) {  // forward direction
+          elseif ($i == $num_children - 1 and $parent->strand >= 0) {  // forward direction
             // -------------------------- ref
             //        ---->  ---->....
             //          1       2 down
-            $q = chado_query($sql, array(':upstream' => 0, ':downstream' => $downstream, ':feature_id' => $child->feature_id));
+            $q = chado_query($parent_sql, array(':upstream' => 0, ':downstream' => $downstream, ':feature_id' => $child->feature_id));
           }
-          elseif ($i == $num_children->num_children - 1 and $parent->strand < 0) { // reverse direction
+          elseif ($i == $num_children - 1 and $parent->strand < 0) { // reverse direction
             // -------------------------- ref
             //        <----  <----....
             //          1       2  up
-            $q = chado_query($sql, array(':upstream' => $upstream, ':downstream' => 0, ':feature_id' => $child->feature_id));
+            $q = chado_query($parent_sql, array(':upstream' => $upstream, ':downstream' => 0, ':feature_id' => $child->feature_id));
           }
-
           // for internal sub features we don't want upstream or downstream bases
           else {
-            $q = chado_query($sql, array(':upstream' => 0, ':downstream' => 0, ':feature_id' => $child->feature_id));
+            $q = chado_query($parent_sql, array(':upstream' => 0, ':downstream' => 0, ':feature_id' => $child->feature_id));
           }
-
           while ($subseq = $q->fetchObject()) {
             // concatenate the sequences of all the sub features
             if ($subseq->srcfeature_id == $parent->srcfeature_id) {
               $seq .= $subseq->residues;
             }
+            if ($subseq->upstream > 0 ) { 
+              $notes .= "Includes " . $subseq->upstream . " bases upstream.  ";
+            }
+            if ($subseq->downstream > 0) {
+              $notes .= "Includes " . $subseq->downstream . " bases downstream.  ";
+            }
           }
           $i++;
         }
@@ -366,50 +373,62 @@ function tripal_get_sequence($feature, $options) {
       // if this isn't an aggregate then use the parent residues
       else {
          $seq = $parent->residues;
+         if ($parent->upstream > 0) {
+           $notes .= "Includes " . $parent->upstream . " bases upstream.  ";
+         }
+         if ($parent->downstream > 0) {
+           $notes .= "Includes " . $parent->downstream . " bases downstream.  ";
+         }
       }
 
       // get the reverse compliment if feature is on the reverse strand
       $dir = 'forward';
-      $notes = '';
+      $length = strlen($seq);
       if ($parent->strand < 0) {
         $seq = tripal_feature_reverse_complement($seq);
         $dir = 'reverse';
       }
       // now format for display
-      if ($output_format == 'fasta_html') {
-         $seq = wordwrap($seq, $num_bases_per_line, "<br>", TRUE);
+      if ($is_html) {
+        $seq = wordwrap($seq, $num_bases_per_line, "<br>", TRUE);
       }
-      elseif ($output_format == 'fasta_txt') {
-         $seq = wordwrap($seq, $num_bases_per_line, "\r\n", TRUE);
+      else {
+        $seq = wordwrap($seq, $num_bases_per_line, "\r\n", TRUE);
       }
       if (!$seq) {
-        if ($output_format == 'fasta_html') {
-          $notes .= "No sequence available.</br>";
-        }
-        else {
-          $notes .= "No sequence available.\r\n";
-        }
+        $notes .= "No sequence available.";
       }
-      
-      $notes = "Sequence derived from feature of type, '$parent->srctypename', of $parent->genus $parent->species: $parent->srcname:" . ($parent->adjfmin + 1) . ".." . $parent->adjfmax . " ($dir). ";
-      /*
+
       if (count($types) > 0) {
         $notes .= "Excludes all bases but those of type(s): " . implode(', ', $types) . ". " ;
       }
-      if ($parent->upstream > 0) {
-         $notes .= "Includes " . $parent->upstream . " bases upstream.  ";
-      }
-      if ($parent->downstream > 0) {
-         $notes .= "Includes " . $parent->downstream . " bases downstream.  ";
-      }      
-      */
+
+      // construct the definition line for this feature.
+      // to construct the defline we need a featureloc record, so we'll create one using
+      // the information we have
+      $featureloc = new stdClass;
+      $featureloc->feature_id = $feature;
+      $featureloc->fmin = $parent->adjfmin;
+      $featureloc->fmax = $parent->adjfmax;
+      $featureloc->strand = $parent->strand;
+      $featureloc->srcfeature_id = new stdClass;
+      $featureloc->srcfeature_id->name = $parent->srcname;
+      $featureloc->srcfeature_id->type_id = $parent->srctypename;
+      $featureloc->srcfeature_id->organism_id = new stdClass;
+      $featureloc->srcfeature_id->organism_id->genus = $parent->genus;
+      $featureloc->srcfeature_id->organism_id->species = $parent->species;
+      // get a proper feature object
+      $f = chado_generate_var('feature', array('feature_id' => $feature_id));
+      $defline = tripal_get_fasta_defline($f, $notes, $featureloc, '', $length);
+      
       $sequences[] = array(
         'types'         => $types,
         'upstream'      => $parent->upstream,
         'downstream'    => $parent->downstream,
-        'notes'         => $notes,
+        'defline'       => $defline,
         'residues'      => $seq,
         'featureloc_id' => $parent->featureloc_id,
+        'length'        => $length,
       );
     }
   }
@@ -419,19 +438,26 @@ function tripal_get_sequence($feature, $options) {
     $sql = "SELECT * FROM {feature} F WHERE feature_id = :feature_id";
     $values = chado_query($sql, array(':feature_id' => $feature_id))->fetchObject();
     $residues = $values->residues;
-    if ($output_format == 'fasta_html') {
-       $residues = wordwrap($residues, $num_bases_per_line, "<br>", TRUE);
+    $length = strlen($residues);
+    if ($is_html) {
+      $residues = wordwrap($residues, $num_bases_per_line, "<br>", TRUE);
     }
-    elseif ($output_format == 'fasta_txt') {
-       $residues = wordwrap($residues, $num_bases_per_line, "\r\n", TRUE);
+    else {
+      $residues = wordwrap($residues, $num_bases_per_line, "\r\n", TRUE);
     }
+    
+    // get the definintion line for this feature
+    $f = chado_generate_var('feature', array('feature_id' => $feature_id));
+    $defline = tripal_get_fasta_defline($f, '', NULL, '', $length);
 
+    // add to the sequence array
     $sequences[] = array(
       'types'      => $values->type,
       'upstream'   => 0,
       'downstream' => 0,
-      'notes'      => '',
+      'defline'    => $defline,
       'residues'   => $residues,
+      'length'     => $length,
     );
   }
 
@@ -439,55 +465,234 @@ function tripal_get_sequence($feature, $options) {
 }
 
 /**
- * Returns a fasta record for the passed in feature
- *
- * @param $feature
- *   A single feature object containing all the fields from the chado.feature table
- * @param $desc
- *   A string containing any additional details you want added to the definition line of
- *   the fasta record.
+ * Retreives or prints multiple sequences from features found using the options provided.
  *
- * Note: the feature accession and name separated by a | will be added
- *   before the description but on the same line
+ * @param $options
+ *   An associative array of options for selecting a feature. Valid keys include:
+ *    - org_commonname: The common name of the organism for which sequences should be retrieved
+ *    - genus: The genus of the organism for which sequences should be retrieved
+ *    - species: The species of the organism for which sequences should be retrieved
+ *    - analysis_name: The name of an analysis to which sequences belong. Only those that are
+ *        associated with the analysis will be retrieved.
+ *    - type: The type of feature (a sequence ontology term).
+ *    - feature_name: the name of the feature. Can be an array of feature names.
+ *    - feature_uname: the uniquename of the feature.  Can be an array of feature unique names.
+ *    - upstream: An integer specifing the number of upstream bases to include in the output
+ *    - downstream: An integer specifying the number of downstream bases to include in the
+ *        output.
+ *    - derive_from_parent: Set to '1' if the sequence should be obtained from the parent
+ *        to which this feature is aligned.
+ *    - aggregate: Set to '1' if the sequence should only contain sub features, excluding
+ *        intro sub feature sequence.  For example, set this option to obtain just
+ *        the coding sequence of an mRNA.
+ *    - sub_feature_types: Only include sub features (or child features) of the types
+ *        provided in the array
+ *    - relationship_type: If a relationship name is provided (e.g. sequence_of) then any
+ *        sequences that are in relationships of this type with matched sequences are also included
+ *    - relationship_part: If a relationship is provided in the preceeding argument then
+ *        the rel_part must be either 'object' or 'subject' to indicate which side of the
+ *        relationship the matched features belong
+ *    - width: Indicate the number of bases to use per line.  A new line will be added
+ *        after the specified number of bases on each line.
+ *    - is_html: Set to '1' if  the sequence is meant to be displayed on a web page.
+ *        This will cause a <br> tag to separate lines of the FASTA sequence. 
+ *    - print: Set to TRUE to print the sequences rather otherwise an array
+ *        of sequences will be returned.  
+ * @return
+ *   Returns an array of sequences unless the option 'print' is used.  If 'print is used
+ *   then nothing is returned but sequences are printed to STDOUT.  If returned, the 
+ *   sequences will be in an array with the following keys for each sequence:
+ *      'types'         => an array of feature types that were used to derive the sequence (e.g. from an aggregated sequence)
+ *      'upstream'      => the number of upstream bases included in the sequence
+ *      'downstream'    => the number of downstream bases included in the sequence
+ *      'defline'       => the definintion line used to create a FASTA sequence
+ *      'residues'      => the residues
+ *      'featureloc_id' => the featureloc_id if the sequences is from an alignment
  *
  * @ingroup tripal_feature_api
  */
-function tripal_format_fasta_sequence($feature, $desc) {
+function tripal_get_bulk_feature_sequences($options) {
+
+  // default values for building the sequence
+  $org_commonname     = array_key_exists('org_commonname', $options)     ? $options['org_commonname']    : '';
+  $genus              = array_key_exists('genus', $options)              ? $options['genus']             : '';
+  $species            = array_key_exists('species', $options)            ? $options['species']           : '';
+  $analysis_name      = array_key_exists('analysis_name', $options)      ? $options['analysis_name']     : '';
+  $type               = array_key_exists('type', $options)               ? $options['type']              : '';
+  $feature_name       = array_key_exists('feature_name', $options)       ? $options['feature_name']      : '';
+  $feature_uname      = array_key_exists('feature_uname', $options)      ? $options['feature_uname']     : '';
+  $derive_from_parent = array_key_exists('derive_from_parent', $options) ? $options['derive_from_parent'] : 0;
+  $aggregate          = array_key_exists('aggregate', $options)          ? $options['aggregate']          : 0;
+  $sub_features       = array_key_exists('sub_feature_types', $options)  ? $options['sub_feature_types']  : array();
+  $relationship       = array_key_exists('relationship_type', $options)  ? $options['relationship_type']  : '';
+  $rel_part           = array_key_exists('relationship_part', $options)  ? $options['relationship_part']  : '';
+  $num_bases_per_line = array_key_exists('width', $options)              ? $options['width']              : 50;
+  $upstream           = array_key_exists('upstream', $options)           ? $options['upstream']       : 0;
+  $downstream         = array_key_exists('downstream', $options)         ? $options['downstream']     : 0;
+  $print              = array_key_exists('print', $options)              ? $options['print']              : FALSE;
+
+  $sub_features = explode(',', $child);
 
-  $fasta  = ">" . variable_get('chado_feature_accession_prefix', 'FID') . "$feature->feature_id|$feature->name";
-  $fasta .= " $desc\n";
-  $fasta .= wordwrap($feature->residues, 50, "\n", TRUE);
-  $fasta .= "\n\n";
+  if (!$output_format) {
+    $output_format = 'fasta_txt';
+  }
 
-  return $fasta;
+  if (!$type and !$feature_name and !$genus) {
+    print "Please provide a type, feature name or genus\n";
+    return;
+  }
 
+  // get the list of features
+  $vars = array();
+  $sql  = "SELECT DISTINCT F.feature_id, F.name, F.uniquename, O.genus, O.species, CVT.name as feature_type " .
+      "FROM {feature} F " .
+      "  INNER JOIN {organism} O on O.organism_id = F.organism_id " .
+      "  INNER JOIN {cvterm} CVT on CVT.cvterm_id = F.type_id ";
+  if ($analysis_name) {
+    $sql .= "  INNER JOIN {analysisfeature} AF on AF.feature_id = F.feature_id " .
+        "  INNER JOIN {analysis} A on AF.analysis_id = A.analysis_id ";
+  }
+  $sql .= "WHERE (1=1) ";
+  if ($org_commonname) {
+    $sql .= "AND O.common_name = :common_name ";
+    $vars[':common_name'] = $org_commonname;
+  }
+  if ($genus) {
+    $sql .= "AND O.genus = :genus ";
+    $vars[':genus'] = $genus;
+  }
+  if ($species) {
+    $sql .= "AND O.species = :species ";
+    $vars[':species'] = $species;
+  }
+  if ($type) {
+    $sql .= "AND CVT.name = :cvtname ";
+    $vars[':cvtname'] = $type;
+  }
+  if ($feature_name) {
+    if (is_array($feature_name)) {
+      $sql .= "AND F.name IN (";
+      foreach ($feature_name as $i => $fname) {
+        $sql .= ":fname$i, ";
+        $vars[":fname$i"] = $fname;
+      }
+      // remove the trailing comma and close the paren
+      $sql = substr($sql, 0, -2) . ")";
+    }
+    else {
+      $sql .= "AND F.name = :fname";
+      $vars[':fname'] = $feature_name;
+    }
+  }
+  if ($feature_uname) {
+    if (is_array($feature_uname)) {
+      $sql .= "AND F.uniquename IN (";
+      foreach ($feature_uname as $i => $funame) {
+        $sql .= ":funame$i, ";
+        $vars[":funame$i"] = $funame;
+      }
+      // remove the trailing comma and close the paren
+      $sql = substr($sql, 0, -2) . ")";
+    }
+    else {
+      $sql .= "AND F.uniquename = :funame";
+      $vars[':funame'] = $feature_uname;
+    }
+  }
+  if ($analysis_name) {
+    $sql .= "AND A.name = :aname";
+    $vars[':aname'] = $analysis_name;
+  }
+  $num_bases_per_line = 50;
+  $num_seqs = 0;
+  $q = chado_query($sql, $vars);
+
+
+  $sequences = array();
+  while ($feature = $q->fetchObject()) {
+
+    // get the sequences
+    $seqs = tripal_get_feature_sequences(array('feature_id' => $feature->feature_id), $options);
+
+    if ($print) {
+      foreach ($seqs as $seq) {
+        print ">" . $seq['defline'] . "\n";
+        print $seq['residues'] . "\n";
+      }
+    }
+    else {
+      $sequences = array_merge($sequences, $seqs);
+    }
+    $num_seqs++;
+  }
+  if (!$print) {
+    return $seqs;
+  }
+  elseif ($num_seqs == 0) {
+    print "No Sequences Found";
+  }
 }
 
 /**
  * Returns a definition line that can be used in a FASTA file
  * 
  * @param $feature
- *   A single feature object containing all the fields from the chado.feature table
+ *   A single feature object containing all the fields from the chado.feature table.
+ *   Best case is to provide an object generated by the chado_generate_var() function.
+ * @param $notes
+ *   Optional: additional notes to be added to the definition line
  * @param $featureloc
- *   Optional: A single featureloc object generated using chado_generate_var that
- *   contains a record from the chado.featureloc table.
+ *   Optional: a single featureloc object generated using chado_generate_var that
+ *   contains a record from the chado.featureloc table. Provide this if the
+ *   sequence was obtained by using the alignment rather than from the feature.residues
+ *   column
  * @param $type
  *   Optional: the type of sequence. By default the feature type is used.
+ * @param $length
+ *   Optional: the length of the sequence
  *   
  * @return
  *   A string of the format: uniquename|name|type|feature_id
  *   or if an alignment:  srcfeature_name:fmin..fmax[+-]; alignment of uniquename|name|type|feature_id 
  */
-function tripal_get_fasta_defline($feature, $featureloc = NULL, $type = '') {
+function tripal_get_fasta_defline($feature, $notes = '', $featureloc = NULL, $type = '', $length = 0) {
+    
+  // make sure the featureloc object has the srcfeature if not, then add it
+  if ($featureloc) {
+    if (!is_object($featureloc->srcfeature_id)) {
+      $featureloc->srcfeature_id = chado_generate_var('feature', array('feature_id' => $featureloc->srcfeature_id));
+    }
+    if (!is_object($featureloc->srcfeature_id->organism_id)) {
+      $featureloc->srcfeature_id->organism_id = chado_generate_var('organism', array('organism_id' => $featureloc->srcfeature_id->organism_id));
+    }
+  }
+  // make sure the feature object has the organism if not, then add it
+  if (!is_object($feature->organism_id)) {
+    $feature->organism_id = chado_generate_var('organism', array('organism_id' => $feature->organism_id));
+  }
   
+  // if a type is not provided then use the default type
   if (!$type) {
     $type = $feature->type_id->name;
   }
-  $defline = $feature->uniquename . "|" . $feature->name . "|" . $type . "|" . $feature->feature_id; 
+  
+  // construct the definition line
+  $defline = $feature->uniquename . " " . 
+             'ID=' . $feature->uniquename . "|" .
+             'Name=' . $feature->name . "|" . 
+             'organism=' . $feature->organism_id->genus . " " . $feature->organism_id->species .  "|" .
+             'type=' . $type . '|'; 
+  if ($length > 0) {
+    $defline .= "length=" . $length . "bp|";
+  }
   if ($featureloc) {
-    $defline = $defline . "; derived from alignment at " .tripal_get_location_string($featureloc);
+    $defline .= "location=Sequence derived from alignment at " . tripal_get_location_string($featureloc);
+    $defline .= " (" . $featureloc->srcfeature_id->organism_id->genus . " " . $featureloc->srcfeature_id->organism_id->species . ")|";
   }
-  
+  if ($notes) {
+    $defline .= "Notes=$notes|";
+  }
+  $defline = substr($defline, 0, -1); // remove the trailing |
   return $defline;
 }
 
@@ -504,7 +709,7 @@ function tripal_get_location_string($featureloc) {
   if ($featureloc->strand < 0) {
     $residues = tripal_feature_reverse_complement($residues);
   }
-  $strand = '.';
+  $strand = '';
   if ($featureloc->strand == 1) {
     $strand = '+';
   }
@@ -513,4 +718,5 @@ function tripal_get_location_string($featureloc) {
   }
   
   return $featureloc->srcfeature_id->name . ":" . ($featureloc->fmin + 1) . ".." . $featureloc->fmax .  $strand;  
-}
+}
+

+ 14 - 99
tripal_feature/includes/tripal_feature.seq_extract.inc

@@ -34,9 +34,20 @@ function tripal_feature_seq_extract_page() {
       drupal_add_http_header('Content-Disposition: attachment; filename="sequences.fasta.txt"');
     }
 
-    tripal_feature_seq_extract_get_features(NULL, $genus, $species, $analysis,
-      $ftype, $fnames['items_array'], $upstream, $downstream, $format, $use_parent, $aggregate,
-      $agg_types);
+    tripal_get_bulk_feature_sequences(array(
+      'genus' => $genus, 
+      'species' => $species, 
+      'analysis_name' => $analysis,
+      'type' => $ftype, 
+      'feature_name' => $fnames['items_array'], 
+      'upstream' => $upstream, 
+      'downstream' => $downstream, 
+      'print' => 1,
+      'output_format' => $format, 
+      'derive_from_parent' => $use_parent, 
+      'aggregate' => $aggregate,
+      'sub_feature_types' => $agg_types
+    ));
 
     return;
   }
@@ -462,99 +473,3 @@ function tripal_feature_seq_extract_form_submit($form, &$form_state) {
     unset($_SESSION['tripal_feature_seq_extract']);
   }
 }
-
-/**
- * Actually extract the sequences
- *
- * @ingroup tripal_feature
- */
-function tripal_feature_seq_extract_get_features($org_commonname, $genus, $species, $analysis_name,
-  $type, $feature_name, $upstream, $downstream, $output_format, $derive_from_parent, $aggregate,
-  $child, $relationship, $rel_part) {
-
-  $sub_features = explode(',', $child);
-
-  if (!$output_format) {
-    $output_format = 'fasta_txt';
-  }
-
-  if (!$type and !$feature_name and !$genus) {
-    print "Please provide a type, feature name or genus\n";
-     return;
-  }
-
-  // get the list of features
-  $vars = array();
-  $sql  = "SELECT DISTINCT F.feature_id, F.name, F.uniquename, O.genus, O.species, CVT.name as feature_type " .
-          "FROM {feature} F " .
-          "  INNER JOIN {organism} O on O.organism_id = F.organism_id " .
-          "  INNER JOIN {cvterm} CVT on CVT.cvterm_id = F.type_id ";
-  if ($analysis_name) {
-     $sql .= "  INNER JOIN {analysisfeature} AF on AF.feature_id = F.feature_id " .
-             "  INNER JOIN {analysis} A on AF.analysis_id = A.analysis_id ";
-  }
-  $sql .= "WHERE (1=1) ";
-  if ($org_commonname) {
-    $sql .= "AND O.common_name = :common_name ";
-    $vars[':common_name'] = $org_commonname;
-  }
-  if ($genus) {
-    $sql .= "AND O.genus = :genus ";
-    $vars[':genus'] = $genus;
-  }
-  if ($species) {
-    $sql .= "AND O.species = :species ";
-    $vars[':species'] = $species;
-  }
-  if ($type) {
-    $sql .= "AND CVT.name = ':cvtname ";
-    $vars[':cvtname'] = $type;
-  }
-  if ($feature_name) {
-    if (is_array($feature_name)) {
-      $sql .= "AND F.name IN (";
-      foreach ($feature_name as $i => $fname) {
-        $sql .= ":fname$i, ";
-        $vars[":fname$i"] = $fname;
-      }
-      // remove the trailing comma and close the paren
-      $sql = substr($sql, 0, -2) . ")";
-    }
-    else {
-      $sql .= "AND F.name = :fname";
-      $vars[':fname'] = $feature_name;
-    }
-  }
-  if ($analysis_name) {
-    $sql .= "AND A.name = :aname";
-    $vars[':aname'] = $analysis_name;
-  }
-  $num_bases_per_line = 50;
-  $num_seqs = 0;
-  $q = chado_query($sql, $vars);
-
-  while ($feature = $q->fetchObject()) {
-
-    $feature_id = $feature->feature_id;
-
-    // build the header for each FASTA entry
-    if ($feature->uniquename == $feature->name) {
-      $feature_name = "$feature->uniquename $feature->feature_type ($feature->genus $feature->species)";
-    }
-    else {
-      $feature_name = "$feature->uniquename $feature->name $feature->feature_type ($feature->genus $feature->species)";
-    }
-
-    // generate the sequence
-    $sequence = tripal_feature_get_formatted_sequence($feature_id, $feature_name,
-      $num_bases_per_line, $derive_from_parent, $aggregate, $output_format,
-      $upstream, $downstream, $sub_features, $relationship, $rel_part);
-
-    // print the sequence
-    print $sequence;
-    $num_seqs++;
-  }
-  if ($num_seqs == 0) {
-    print "No Sequences Found";
-  }
-}

+ 16 - 11
tripal_feature/theme/templates/tripal_feature_sequence.tpl.php

@@ -9,9 +9,11 @@
  *
  */
 
-
 $feature = $variables['node']->feature;
 
+// number of bases per line in FASTA format
+$num_bases = 50; 
+
 // we don't want to get the sequence for traditionally large types. They are
 // too big,  bog down the web browser, take longer to load and it's not
 // reasonable to print them on a page.
@@ -43,8 +45,8 @@ if ($residues or count($featureloc_sequences) > 0) {
     $sequences_html .= '<div id="residues" class="tripal_feature-sequence-item">';
     $sequences_html .= '<p><b>' . $feature->type_id->name . ' sequence</b></p>';
     $sequences_html .= '<pre class="tripal_feature-sequence">';
-    $sequences_html .= '>' . tripal_get_fasta_defline($feature) . "\n";
-    $sequences_html .= preg_replace("/(.{50})/","\\1<br>",$feature->residues);
+    $sequences_html .= '>' . tripal_get_fasta_defline($feature, '', NULL, '', strlen($feature->residues)) . "\n";
+    $sequences_html .= wordwrap($feature->residues, $num_bases, "<br>", TRUE);
     $sequences_html .= '</pre>';
     $sequences_html .= '<a href="#sequences-top">back to top</a>';
     $sequences_html .= '</div>';
@@ -74,8 +76,8 @@ if ($residues or count($featureloc_sequences) > 0) {
             $sequences_html .= '<div id="protein-' . $protein->feature_id . '" class="tripal_feature-sequence-item">';
             $sequences_html .= '<p><b>protein sequence of ' . $protein->name . '</b></p>';
             $sequences_html .= '<pre class="tripal_feature-sequence">';
-            $sequences_html .= '>' . tripal_get_fasta_defline($protein) . "\n";
-            $sequences_html .= preg_replace("/(.{50})/","\\1<br>", $protein->residues);
+            $sequences_html .= '>' . tripal_get_fasta_defline($protein, '', NULL, '', strlen($protein->residues)) . "\n";
+            $sequences_html .= wordwrap($protein->residues, $num_bases, "<br>", TRUE);
             $sequences_html .= '</pre>';
             $sequences_html .= '<a href="#sequences-top">back to top</a>';
             $sequences_html .= '</div>';
@@ -107,7 +109,7 @@ if ($residues or count($featureloc_sequences) > 0) {
     $sequences_html .= '<div id="coding_sequence" class="tripal_feature-sequence-item">';
     $sequences_html .= '<p><b>coding sequence</b></p>';
     $sequences_html .= '<pre class="tripal_feature-sequence">';
-    $sequences_html .= $coding_seq;
+    $sequences_html .= wordwrap($coding_seq, $num_bases, "<br>", TRUE);
     $sequences_html .= '</pre>';
     $sequences_html .= '<a href="#sequences-top">back to top</a>';
     $sequences_html .= '</div>';
@@ -147,7 +149,7 @@ if ($residues or count($featureloc_sequences) > 0) {
     }
     
     // check to see if this alignment has any CDS. If so, generate a CDS sequence
-    $cds_sequence = tripal_get_sequence(
+    $cds_sequence = tripal_get_feature_sequences(
         array(
           'feature_id' => $feature->feature_id,
           'parent_id' => $attrs['featureloc']->srcfeature_id->feature_id,
@@ -155,22 +157,25 @@ if ($residues or count($featureloc_sequences) > 0) {
           'featureloc_id' => $attrs['featureloc']->featureloc_id,
         ),
         array(
-          'width' => 50,  // FASTA sequence should have 50 chars per line
+          'width' => $num_bases,  // FASTA sequence should have $num_bases chars per line
           'derive_from_parent' => 1, // CDS are in parent-child relationships so we want to use the sequence from the parent
           'aggregate' => 1, // we want to combine all CDS for this feature into a single sequence
-          'output_format' => 'fasta_txt', // we just want plain text, we'll format it here.
           'sub_feature_types' => array('CDS'), // we're looking for CDS features
-          ''
+          'is_html' => 1
         )
     );
     if (count($cds_sequence) > 0) {
+      // the tripal_get_feature_sequences() function can return multiple sequences
+      // if a feature is aligned to multiple places. In the case of CDSs we expect
+      // that one mRNA is only aligned to a single location on the assembly so we
+      // can access the CDS sequence with index 0.
       if ($cds_sequence[0]['residues']) {
         $list_items[] = '<a href="#coding_' . $attrs['id'] . '">coding sequnece from alignment at  ' . $attrs['location'] . "</a>";
         $sequences_html .= '<a name="ccoding_' . $attrs['id'] . '"></a>';
         $sequences_html .= '<div id="coding_' . $attrs['id'] . '" class="tripal_feature-sequence-item">';
         $sequences_html .= '<p><b>Coding sequence (CDS) from alignment at  ' . $attrs['location'] . '</b></p>';
         $sequences_html .= '<pre class="tripal_feature-sequence">';
-        $sequences_html .= '>' . tripal_get_fasta_defline($feature, $attrs['featureloc'], 'CDS') . "\n";
+        $sequences_html .= '>' . tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', $cds_sequence[0]['length']) . "<br>";
         $sequences_html .= $cds_sequence[0]['residues'];
         $sequences_html .= '</pre>';
         $sequences_html .= '<a href="#sequences-top">back to top</a>';

+ 1 - 1
tripal_feature/theme/tripal_feature.theme.inc

@@ -168,7 +168,7 @@ function tripal_feature_load_featureloc_sequences($feature_id, $featurelocs) {
         $strand = '-';
       }
       $floc_sequences[$src]['location'] = tripal_get_location_string($featureloc);
-      $floc_sequences[$src]['defline'] = tripal_get_fasta_defline($featureloc->feature_id, $featureloc);
+      $floc_sequences[$src]['defline'] = tripal_get_fasta_defline($featureloc->feature_id, '', $featureloc, '', strlen($residues));
       $floc_sequences[$src]['featureloc'] = $featureloc;
       $floc_sequences[$src]['formatted_seq'] =  tripal_feature_color_sequence($residues, $parts, $floc_sequences[$src]['defline']);
     }

+ 34 - 18
tripal_feature/tripal_feature.drush.inc

@@ -40,12 +40,12 @@ function tripal_feature_drush_command() {
       'name'     => dt('The name of the feature to retrieve.'),
       'up'       => dt('An integer value specifying the number of upstream bases to include.'),
       'down'     => dt('An integer value specifying the number of downstream bases to incldue.'),
-      'out'      => dt('The output format. Valid options are "fasta_html", "fasta_txt" and raw.'),
       'parent'   => dt('Set this argument to 1 to retrieve the sequence from the parent in an alignment rather than the residues column of the feature itself.'),
       'agg'      => dt('Set this argument to 1 to aggregate sub features into a single sequence.  This is useful, for example, for obtaining CDS sequence from an mRNA'),
-      'child'    => dt('Set this argument to the sequence ontology term for the children to aggregate.  This is useful in the case where a gene has exons as well as CDSs and UTRs.  You may sepcify as many feature types as desired by separating each with a single comma (no spaces).'),
+      'child'    => dt('Set this argument to the exact sequence ontology term for the children to aggregate.  This is useful in the case where a gene has exons as well as CDSs and UTRs.  You may sepcify as many feature types as desired by separating each with a single comma (no spaces). Term matching is case-sensitive.'),
       'relationship'  => dt('Retreives the sequence of any feature in the specified relationship with the matched features.'),
       'rel_part' => dt('If a relationship is provided, then this will be "subject" or "object" indicating the side of the relationship for the matched features. If the matched features are the "object" then the "subject" features will have their sequences included in the output (and vice versa).'),
+      'width'    => dt('The number of nucleotides per row (defaults to 50).')
     ),
     'examples' => array(
       'Standard example' => 'drush tripal-current-job',
@@ -72,29 +72,45 @@ function tripal_feature_drush_command() {
  */
 function drush_tripal_feature_tripal_get_sequence() {
 
-  $org_commonname = drush_get_option('org');
-  $genus = drush_get_option('genus');
-  $species = drush_get_option('species');
-  $analysis_name = drush_get_option('analysis');
-  $type = drush_get_option('type');
-  $feature_name = drush_get_option('name');
-  $upstream = drush_get_option('up');
-  $downstream = drush_get_option('down');
-  $output_format = drush_get_option('out');
+  $org_commonname     = drush_get_option('org');
+  $genus              = drush_get_option('genus');
+  $species            = drush_get_option('species');
+  $analysis_name      = drush_get_option('analysis');
+  $type               = drush_get_option('type');
+  $feature_name       = drush_get_option('name');
+  $upstream           = drush_get_option('up')   ? drush_get_option('up') : 0;
+  $downstream         = drush_get_option('down') ? drush_get_option('down') : 0;
   $derive_from_parent = drush_get_option('parent');
-  $aggregate = drush_get_option('agg');
-  $child = drush_get_option('child');
-  $relationship = drush_get_option('relationship');
-  $rel_part = drush_get_option('rel_part');
+  $aggregate          = drush_get_option('agg');
+  $child              = drush_get_option('child');
+  $relationship       = drush_get_option('relationship');
+  $rel_part           = drush_get_option('rel_part');
+  $width              = drush_get_option('width') ? drush_get_option('width') : 50;
 
   if ($relationship and !$rel_part) {
     print "Please specify both 'relationship' and a 'rel_part' arguments. Both must be used together\n";
     return;
   }
 
-  tripal_feature_seq_extract_get_features($org_commonname, $genus, $species, $analysis_name,
-    $type, $feature_name, $upstream, $downstream, $output_format, $derive_from_parent,
-    $aggregate, $child, $relationship, $rel_part);
+  $options = array(
+    'org_commonname' => $org_commonname,
+    'genus' => $genus,
+    'species' => $species,
+    'analysis_name' => $analysis_name,
+    'type' => $type,
+    'feature_name' => $feature_name,
+    'upstream' => $upstream,
+    'downstream' => $downstream,
+    'derive_from_parent' => $derive_from_parent,
+    'aggregate' => $aggregate,
+    'sub_feature_types' => explode(',', $child),
+    'relationship_type' => $relationship,
+    'relationship_part' => $rel_part,
+    'print' => TRUE,
+    'width' => $width
+  );
+  
+  tripal_get_bulk_feature_sequences($options);
 }
 
 /**