tags and the entire * return value is in a 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 * output. * - 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 * * @return * an array of matching sequence formated as requested. * * @ingroup tripal_feature_api */ function tripal_get_sequence($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'] : ''; // 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; $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'] : ''; // 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().", array() ); return array(); } // if a relationship was specified then retreive and the sequences that // have the given relationship and the recurse to extract the appropriate // sequence if ($rel_part == "object" or $rel_part == "subject") { 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 WHERE FS.feature_id = :feature_id AND CVTFR.name = :relationship '; $features = chado_query($sql, array(':feature_id' => $feature_id, ':relationship' => $relationship)); } 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 WHERE FO.feature_id = :feature_id AND CVTFR.name = :relationship '; $features = chado_query($sql, array(':feature_id' => $feature_id, ':relationship' => $relationship)); } $sequences = ''; while ($feature = $features->fetchObject()) { // recurse and get the sequences for these in the relationship if ($rel_part == "subject") { $defline = "$feature_name, $relationship, $feature->uniquename $feature->feature_type ($feature->genus $feature->species)"; } if ($rel_part == "object") { $defline = "$feature->uniquename $feature->feature_type ($feature->genus $feature->species), $relationship, $feature_name"; } return tripal_get_sequence( array( 'feature_id' => $feature->feature_id, 'name' => $defline, 'parent_id' => $parent_id, ), array( 'width' => $num_bases_per_line, 'derive_from_pareht' => $derive_from_parent, 'aggregate' => $aggregate, 'output_format' => $output_format, 'upstream' => $upstream, 'downstream' => $downstream, 'sub_features' => $sub_features, ) ); } } // prepare the queries we're going to use later during the render phase // This SQL statement uses conditionals in the select clause to handle // 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 =' 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, genus, species FROM ( SELECT FL.featureloc_id, OF.name srcname, FL.srcfeature_id, FL.strand, OCVT.name as srctypename, SCVT.name as typename, FL.fmin, FL.fmax, OO.genus, OO.species, CASE WHEN FL.strand >= 0 THEN CASE WHEN FL.fmin - :upstream <= 0 THEN 0 ELSE FL.fmin - :upstream END WHEN FL.strand < 0 THEN CASE WHEN FL.fmin - :downstream <= 0 THEN 0 ELSE FL.fmin - :downstream END END as adjfmin, CASE WHEN FL.strand >= 0 THEN CASE WHEN FL.fmax + :downstream > OF.seqlen THEN OF.seqlen ELSE FL.fmax + :downstream END WHEN FL.strand < 0 THEN CASE WHEN FL.fmax + :upstream > OF.seqlen THEN OF.seqlen ELSE FL.fmax + :upstream END END as adjfmax, CASE WHEN FL.strand >= 0 THEN CASE WHEN FL.fmin - :upstream <= 0 THEN FL.fmin ELSE :upstream END ELSE CASE WHEN FL.fmax + :upstream > OF.seqlen THEN OF.seqlen - FL.fmax ELSE :upstream END END as upstream, CASE WHEN FL.strand >= 0 THEN CASE WHEN FL.fmax + :downstream > OF.seqlen THEN OF.seqlen - FL.fmax ELSE :downstream END ELSE CASE WHEN FL.fmin - :downstream <= 0 THEN FL.fmin ELSE :downstream END END as downstream, OF.residues FROM {featureloc} FL INNER JOIN {feature} SF on FL.feature_id = SF.feature_id INNER JOIN {cvterm} SCVT on SF.type_id = SCVT.cvterm_id INNER JOIN {feature} OF on FL.srcfeature_id = OF.feature_id INNER JOIN {cvterm} OCVT on OF.type_id = OCVT.cvterm_id INNER JOIN {organism} OO on OF.organism_id = OO.organism_id WHERE SF.feature_id = :feature_id and NOT (OF.residues = \'\' or OF.residues IS NULL)) as tbl1 '; // this query is meant to get all of the sub features of any given // feature (arg #1) and order them as they appear on the reference // feature (arg #2). $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 WHERE FR.object_id = :feature_id and PF.feature_id = :srcfeature_id ORDER BY FL.fmin ASC '; // for counting the number of children $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 WHERE FR.object_id = :feature_id and PF.feature_id = :srcfeature_id '; // the array to be returned $sequences = array(); // if we need to get the sequence from the parent then do so now. 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)); while ($parent = $parents->fetchObject()) { // if the user specified a particular parent and this one doesn't match then skip it if ($parent_id and $parent_id != $parent->srcfeature_id) { continue; } // if the user specified a particular featureloc_id and this one doesn't match then skip it if ($featureloc_id and $featureloc_id != $parent->featureloc_id) { continue; } $seq = ''; // initialize the sequence for each parent // if we are to aggregate then we will ignore the feature returned // by the query above and rebuild it using the sub features if ($aggregate) { // 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(); // iterate through the sub features and concat their sequences. They // should already be in order. $types = array(); $i = 0; while ($child = $children->fetchObject()) { // if the callee has specified that only certain sub features should be // 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)) { continue; } // keep up with the types if (!in_array($child->type_name, $types)) { $types[] = $child->type_name; } // if the first sub feature we need to include the upstream bases. first check if // the feature is in the foward direction or the reverse. if ($i == 0 and $parent->strand >= 0) { // forward direction // -------------------------- ref // ....----> ----> // up 1 2 $q = chado_query($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)); } // 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 // -------------------------- ref // ----> ---->.... // 1 2 down $q = chado_query($sql, array(':upstream' => 0, ':downstream' => $downstream, ':feature_id' => $child->feature_id)); } elseif ($i == $num_children->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)); } // 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)); } while ($subseq = $q->fetchObject()) { // concatenate the sequences of all the sub features if ($subseq->srcfeature_id == $parent->srcfeature_id) { $seq .= $subseq->residues; } } $i++; } } // if this isn't an aggregate then use the parent residues else { $seq = $parent->residues; } // get the reverse compliment if feature is on the reverse strand $dir = 'forward'; 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, "
", TRUE); } elseif ($output_format == 'fasta_txt') { $seq = wordwrap($seq, $num_bases_per_line, "\r\n", TRUE); } if (!$seq) { if ($output_format == 'fasta_html') { $notes .= "No sequence available.
"; } else { $notes .= "No sequence available.\r\n"; } } $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. "; } */ $sequences[] = array( 'types' => $types, 'upstream' => $parent->upstream, 'downstream' => $parent->downstream, 'notes' => $notes, 'residues' => $seq, 'featureloc_id' => $parent->featureloc_id, ); } } // if we are not getting the sequence from the parent sequence then // use what comes through from the feature record else { $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, "
", TRUE); } elseif ($output_format == 'fasta_txt') { $residues = wordwrap($residues, $num_bases_per_line, "\r\n", TRUE); } $sequences[] = array( 'types' => $values->type, 'upstream' => 0, 'downstream' => 0, 'notes' => '', 'residues' => $residues, ); } return $sequences; } /** * 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. * * Note: the feature accession and name separated by a | will be added * before the description but on the same line * * @ingroup tripal_feature_api */ function tripal_format_fasta_sequence($feature, $desc) { $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"; return $fasta; } /** * 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 * @param $featureloc * Optional: A single featureloc object generated using chado_generate_var that * contains a record from the chado.featureloc table. * @param $type * Optional: the type of sequence. By default the feature type is used. * * @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 = '') { if (!$type) { $type = $feature->type_id->name; } $defline = $feature->uniquename . "|" . $feature->name . "|" . $type . "|" . $feature->feature_id; if ($featureloc) { $defline = $defline . "; derived from alignment at " .tripal_get_location_string($featureloc); } return $defline; } /** * Returns a string representing a feature location in an alignment * * @param unknown $featureloc * A single featureloc object generated using chado_generate_var that * contains a record from the chado.featureloc table. */ function tripal_get_location_string($featureloc) { $feature = $featureloc->feature_id; if ($featureloc->strand < 0) { $residues = tripal_feature_reverse_complement($residues); } $strand = '.'; if ($featureloc->strand == 1) { $strand = '+'; } elseif ($featureloc->strand == -1) { $strand = '-'; } return $featureloc->srcfeature_id->name . ":" . ($featureloc->fmin + 1) . ".." . $featureloc->fmax . $strand; }